resolve merge conflicts of 7a78e14ec to oc-dev-plus-aosp

Change-Id: Icdd22fcf328935b7f5647270489539dab0ec4369
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/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
index c89e3b1..867eff9 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -15,6 +15,15 @@
         "libz",
         "libbase",
     ],
+    static_libs: [
+        "libpdx_default_transport",
+    ],
 
     init_rc: ["atrace.rc"],
+
+    product_variables: {
+        debuggable: {
+            init_rc: ["atrace_userdebug.rc"],
+        },
+    },
 }
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index add5285..1ce88b9 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -39,6 +39,7 @@
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
 
+#include <pdx/default_transport/service_utility.h>
 #include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Tokenizer.h>
@@ -49,6 +50,7 @@
 #include <android-base/stringprintf.h>
 
 using namespace android;
+using pdx::default_transport::ServiceUtility;
 
 using std::string;
 
@@ -381,7 +383,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;
@@ -550,7 +552,13 @@
                 // ignore
                 continue;
             }
+
             sp<IBase> interface = interfaceRet;
+            if (interface == nullptr) {
+                // ignore
+                continue;
+            }
+
             auto notifyRet = interface->notifySyspropsChanged();
             if (!notifyRet.isOk()) {
                 // ignore
@@ -801,6 +809,7 @@
     ok &= setAppCmdlineProperty(&packageList[0]);
     ok &= pokeBinderServices();
     pokeHalServices();
+    ok &= ServiceUtility::PokeServices();
 
     // 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.
@@ -838,6 +847,7 @@
     setTagsProperty(0);
     clearAppProperties();
     pokeBinderServices();
+    ServiceUtility::PokeServices();
 
     // Set the options back to their defaults.
     setTraceOverwriteEnable(true);
@@ -1034,7 +1044,7 @@
                     "  -s N            sleep for N seconds before tracing [default 0]\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"
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/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..73d274f 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;
     }
 };
 
@@ -54,13 +128,13 @@
     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 +159,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.bp b/cmds/dumpstate/Android.bp
deleted file mode 100644
index 3db41c2..0000000
--- a/cmds/dumpstate/Android.bp
+++ /dev/null
@@ -1,4 +0,0 @@
-cc_library_static {
-    name: "libdumpstate.default",
-    srcs: ["libdumpstate_default.cpp"],
-}
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index b6aacb5..a407ea2 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,16 +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 := dumpstate.cpp utils.cpp
+LOCAL_MODULE := libdumpstateutil
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_SRC_FILES := \
+        DumpstateInternal.cpp \
+        DumpstateUtil.cpp
+LOCAL_SHARED_LIBRARIES := \
+        libbase \
+        liblog \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ====================#
+# libdumpstateheaders #
+# ====================#
+# TODO: this module is necessary so the device-specific libdumpstate implementations do not
+# need to add any other dependency (like libbase). Should go away once dumpstate HAL changes.
+include $(CLEAR_VARS)
+
+LOCAL_EXPORT_C_INCLUDE_DIRS = $(LOCAL_PATH)
+LOCAL_MODULE := libdumpstateheaders
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+        $(COMMON_SHARED_LIBRARIES)
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
+        $(COMMON_STATIC_LIBRARIES)
+# Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_
+LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS)
+LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ================ #
+# libdumpstateaidl #
+# =================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdumpstateaidl
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+        libbinder \
+        libutils
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/binder
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_SRC_FILES := \
+        binder/android/os/IDumpstate.aidl \
+        binder/android/os/IDumpstateListener.aidl \
+        binder/android/os/IDumpstateToken.aidl
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ==========#
+# dumpstate #
+# ==========#
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+        DumpstateService.cpp \
+        dumpstate.cpp
 
 LOCAL_MODULE := dumpstate
 
-LOCAL_SHARED_LIBRARIES := libcutils libdebuggerd_client liblog libselinux libbase
-# ZipArchive support, the order matters here to get all symbols.
-LOCAL_STATIC_LIBRARIES := libziparchive libz libcrypto_static
-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 bb56984..5dad511 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #define LOG_TAG "dumpstate"
 
 #include <dirent.h>
@@ -36,36 +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 "DumpstateInternal.h"
+#include "DumpstateService.h"
 #include "dumpstate.h"
-#include "ziparchive/zip_writer.h"
 
-#include <openssl/sha.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"
@@ -90,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
@@ -141,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);
@@ -150,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)
@@ -174,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.
@@ -224,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) {
@@ -242,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;
         }
@@ -295,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)) {
@@ -311,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;
@@ -332,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())) {
@@ -352,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;
     }
 
@@ -368,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());
@@ -399,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;
 
@@ -411,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) {
@@ -452,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());
@@ -471,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;
 }
 
@@ -630,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 */
@@ -653,36 +649,35 @@
     return 10 * (property_size + worst_write_perf) / worst_write_perf;
 }
 
-/* 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];
+void Dumpstate::PrintHeader() const {
+    std::string build, fingerprint, radio, bootloader, network;
+    char 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");
 }
 
@@ -694,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;
@@ -715,253 +710,310 @@
 
     // 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) {
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK
-            | O_CLOEXEC)));
+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) {
-    DurationReporter duration_reporter("DUMPSTATE");
+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"});
+}
 
-    dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
-    run_command("UPTIME", 10, "uptime", NULL);
-    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", "-b", "-n", "1", "-H", "-s", "6",
-                "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name", 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");
-
-    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");
-
-    run_command("PROCESSES AND THREADS", 10, "ps", "-A", "-T", "-Z",
-                "-O", "pri,nice,rtprio,sched,pcy", NULL);
-    run_command("LIBRANK", 10, SU_PATH, "root", "librank", NULL);
-
-    run_command("PRINTENV", 10, "printenv", NULL);
-    run_command("NETSTAT", 10, "netstat", "-nW", NULL);
-    run_command("LSMOD", 10, "lsmod", NULL);
-
-    do_dmesg();
-
-    run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
-    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);
-
-    if (!screenshot_path.empty()) {
-        MYLOGI("taking late screenshot\n");
-        take_screenshot(screenshot_path);
-        MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
-    }
-
-    do_logcat();
+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 != NULL) {
-        dump_file("VM TRACES JUST NOW", dump_traces_path);
+    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);
+        }
     }
 
-    /* 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]) {
+    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,
-                                       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);
-      }
+        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 */
-    if (anr_traces_path[0] != 0) {
-        int tail = strlen(anr_traces_path)-1;
-        while (tail > 0 && anr_traces_path[tail] != '/') {
+    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) {
-            sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
-            if (stat(anr_traces_path, &st)) {
+            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;
             }
-            dump_file("VM TRACES WHEN SLOW", anr_traces_path);
+            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");
+    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");
+    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");
+
+    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");
+
+    RunCommand("PROCESSES AND THREADS",
+               {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
+    RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT);
+
+    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();
+
+    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)");
+
+    if (true) {
+        // TODO: temporary disabled because /data/misc/bluetooth/logs/btsnoop_hci.log can be huge
+        MYLOGD("Skipping /data/misc/bluetooth/logs");
+    } else {
+        /* Dump Bluetooth HCI logs */
+        ds.AddDir("/data/misc/bluetooth/logs", true);
+    }
+
+    if (!ds.do_early_screenshot_) {
+        MYLOGI("taking late screenshot\n");
+        ds.TakeScreenshot();
+    }
+
+    DoLogcat();
+
+    AddAnrTraceFiles();
 
     int dumped = 0;
     for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -969,8 +1021,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 {
@@ -984,193 +1036,279 @@
         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());
 
-    dump_file("INTERRUPTS (1)", "/proc/interrupts");
+    RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
+               CommandOptions::WithTimeout(10).Build());
 
-    run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "-t", "10", "connectivity", "--diag", NULL);
+    RunCommand("SYSTEM PROPERTIES", {"getprop"});
 
-    dump_file("INTERRUPTS (2)", "/proc/interrupts");
+    RunCommand("VOLD DUMP", {"vdc", "dump"});
+    RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
 
-    run_command("SYSTEM PROPERTIES", 5, "getprop", NULL);
+    RunCommand("STORAGED TASKIOINFO", {"storaged", "-u"}, CommandOptions::WithTimeout(10).Build());
 
-    run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
-    run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
+    RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
 
-    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;
@@ -1211,8 +1349,34 @@
     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;
@@ -1221,33 +1385,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);
@@ -1261,60 +1407,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.
@@ -1324,98 +1556,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());
             }
         }
     }
@@ -1427,66 +1635,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
@@ -1495,40 +1698,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 */
@@ -1540,125 +1747,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 0b6aaab..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,34 +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);
+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);
@@ -151,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);
 
@@ -184,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);
 
@@ -205,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:
-    explicit 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 6ec636e..f649a5e 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -16,10 +16,12 @@
 
 #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>
@@ -36,21 +38,41 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <set>
 #include <string>
 #include <vector>
 
 #include <android-base/file.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,7 +80,6 @@
         "/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",
@@ -68,37 +89,180 @@
         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,7 +344,11 @@
 }
 
 void for_each_pid(for_each_pid_func func, const char *header) {
-    ON_DRY_RUN_RETURN();
+    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);
 }
 
@@ -233,12 +401,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 +478,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 +536,7 @@
     if (iotime) {
         snprdec(buffer, sizeof(buffer), 79, permille);
     }
-    puts(buffer); // adds a trailing newline
+    puts(buffer);  // adds a trailing newline
 
     return;
 }
@@ -371,7 +546,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 +577,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 +617,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 +626,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 +660,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 +675,7 @@
         (*dump_from_fd)(NULL, newpath, fd);
     }
     closedir(dirp);
-    if (title) {
+    if (!title.empty()) {
         printf("\n");
     }
     return retval;
@@ -578,7 +686,8 @@
  * stuck.
  */
 int dump_file_from_fd(const char *title, const char *path, int fd) {
-    ON_DRY_RUN_RETURN(0);
+    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));
@@ -589,297 +698,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);
+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) {
@@ -940,11 +782,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));
@@ -955,6 +797,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)) {
@@ -964,44 +823,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_APPEND | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
+        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");
@@ -1017,9 +902,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;
     }
 
@@ -1052,7 +937,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;
@@ -1060,7 +945,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) {
@@ -1073,16 +958,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) {
@@ -1093,8 +979,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);
             }
         }
     }
@@ -1103,17 +989,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);
@@ -1124,9 +1010,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));
@@ -1137,72 +1023,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) {
@@ -1246,19 +1127,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;
@@ -1272,8 +1150,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;
     }
 
@@ -1283,11 +1160,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;
@@ -1316,48 +1192,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/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 5c04f6c..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);
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.mk b/cmds/installd/Android.mk
index d3429ed..1d21b3c 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -6,7 +6,6 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := otapreopt
-LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS := -Wall -Werror
 
 # Base & ASLR boundaries for boot image creation.
@@ -25,6 +24,7 @@
 LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
 
 LOCAL_SRC_FILES := otapreopt.cpp globals.cpp utils.cpp dexopt.cpp
+LOCAL_HEADER_LIBRARIES := dex2oat_headers
 LOCAL_SHARED_LIBRARIES := \
     libbase \
     libcutils \
@@ -34,7 +34,6 @@
     libutils \
 
 LOCAL_STATIC_LIBRARIES := libdiskusage
-LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp
index d1bdded..17eb7ff 100644
--- a/cmds/installd/CacheItem.cpp
+++ b/cmds/installd/CacheItem.cpp
@@ -16,8 +16,9 @@
 
 #include "CacheItem.h"
 
-#include <stdint.h>
 #include <inttypes.h>
+#include <stdint.h>
+#include <sys/xattr.h>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
@@ -29,12 +30,23 @@
 namespace android {
 namespace installd {
 
-CacheItem::CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p) : mParent(parent) {
+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;
-    mName = p->fts_path;
+
+    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() {
@@ -46,7 +58,7 @@
 
 std::string CacheItem::buildPath() {
     std::string res = mName;
-    std::shared_ptr<CacheItem> parent = mParent;
+    CacheItem* parent = mParent;
     while (parent) {
         res.insert(0, parent->mName);
         parent = parent->mParent;
@@ -57,13 +69,47 @@
 int CacheItem::purge() {
     auto path = buildPath();
     if (directory) {
-        return delete_dir_contents_and_dir(path, true);
-    } else {
-        int res = unlink(path.c_str());
-        if (res != 0) {
-            PLOG(WARNING) << "Failed to unlink " << 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 -1;
         }
-        return res;
+        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) {
+                    truncate(p->fts_path, 0);
+                } else {
+                    unlink(p->fts_path);
+                }
+                break;
+            case FTS_DEFAULT:
+            case FTS_SL:
+            case FTS_SLNONE:
+                unlink(p->fts_path);
+                break;
+            case FTS_DP:
+                rmdir(p->fts_path);
+                break;
+            }
+        }
+        return 0;
+    } else {
+        if (tombstone) {
+            return truncate(path.c_str(), 0);
+        } else {
+            return unlink(path.c_str());
+        }
     }
 }
 
diff --git a/cmds/installd/CacheItem.h b/cmds/installd/CacheItem.h
index bec8bc8..84b77aa 100644
--- a/cmds/installd/CacheItem.h
+++ b/cmds/installd/CacheItem.h
@@ -31,12 +31,12 @@
 
 /**
  * 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 atomically
- * deleted.
+ * isolated file, or an entire directory tree that should be deleted as a
+ * group.
  */
 class CacheItem {
 public:
-    CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p);
+    CacheItem(FTSENT* p);
     ~CacheItem();
 
     std::string toString();
@@ -46,11 +46,13 @@
 
     short level;
     bool directory;
+    bool group;
+    bool tombstone;
     int64_t size;
     time_t modified;
 
 private:
-    std::shared_ptr<CacheItem> mParent;
+    CacheItem* mParent;
     std::string mName;
 
     DISALLOW_COPY_AND_ASSIGN(CacheItem);
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
index 23c4330..4bfc834 100644
--- a/cmds/installd/CacheTracker.cpp
+++ b/cmds/installd/CacheTracker.cpp
@@ -20,6 +20,7 @@
 
 #include <fts.h>
 #include <sys/quota.h>
+#include <sys/xattr.h>
 #include <utils/Trace.h>
 
 #include <android-base/logging.h>
@@ -82,34 +83,63 @@
     FTS *fts;
     FTSENT *p;
     char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
         PLOG(WARNING) << "Failed to fts_open " << path;
         return;
     }
-    // TODO: add support for "user.atomic" and "user.tombstone" xattrs
-    while ((p = fts_read(fts)) != NULL) {
+    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:
-            // Track the newest mtime of anything inside so we consider
-            // deleting the directory last
-            p->fts_number = p->fts_statp->st_mtime;
-            break;
-        case FTS_DP:
-            p->fts_statp->st_mtime = p->fts_number;
-
-            // Ignore the actual top-level cache directories
-            if (p->fts_level == 0) break;
         case FTS_DEFAULT:
         case FTS_F:
         case FTS_SL:
-        case FTS_SLNONE:
-            // TODO: optimize path memory footprint
-            items.push_back(std::shared_ptr<CacheItem>(new CacheItem(nullptr, p)));
+        case FTS_SLNONE: {
+            auto item = std::shared_ptr<CacheItem>(new CacheItem(p));
+            p->fts_pointer = static_cast<void*>(item.get());
+            items.push_back(item);
+        }
+        }
 
-            // Track the newest modified item under this tree
-            p->fts_parent->fts_number =
-                    std::max(p->fts_parent->fts_number, p->fts_statp->st_mtime);
-            break;
+        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);
@@ -137,7 +167,7 @@
         }
         return left->directory;
     };
-    std::sort(items.begin(), items.end(), cmp);
+    std::stable_sort(items.begin(), items.end(), cmp);
     ATRACE_END();
 }
 
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b5f0fb2..70acb07 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -72,6 +72,8 @@
 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";
@@ -84,9 +86,10 @@
 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_NOOP = 1 << 14;
+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;
 
-#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
 namespace {
 
 constexpr const char* kDump = "android.permission.DUMP";
@@ -201,14 +204,20 @@
 
     out << "installd is happy!" << endl;
 
-    out << endl << "Devices with quota support:" << endl;
-    for (const auto& n : mQuotaDevices) {
-        out << "    " << n.first << " = " << n.second << endl;
+    {
+        std::lock_guard<std::recursive_mutex> lock(mQuotaDevicesLock);
+        out << endl << "Devices with quota support:" << endl;
+        for (const auto& n : mQuotaDevices) {
+            out << "    " << n.first << " = " << n.second << endl;
+        }
     }
 
-    out << endl << "Per-UID cache quotas:" << endl;
-    for (const auto& n : mCacheQuotas) {
-        out << "    " << n.first << " = " << n.second << endl;
+    {
+        std::lock_guard<std::recursive_mutex> lock(mCacheQuotasLock);
+        out << endl << "Per-UID cache quotas:" << endl;
+        for (const auto& n : mCacheQuotas) {
+            out << "    " << n.first << " = " << n.second << endl;
+        }
     }
 
     out << endl;
@@ -281,6 +290,46 @@
     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) {
@@ -346,10 +395,16 @@
         }
 
         // Consider restorecon over contents if label changed
-        if (restorecon_app_data_lazy(path, seInfo, uid, existing)) {
+        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);
@@ -548,6 +603,111 @@
     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,
@@ -701,6 +861,14 @@
             }
         }
     }
+
+    // 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();
 }
 
@@ -754,24 +922,23 @@
     CHECK_ARGUMENT_UUID(uuid);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    // TODO: remove this once framework is more robust
-    invalidateMounts();
-
     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);
-    int64_t needed = freeStorageSize - free;
     if (free < 0) {
         return error("Failed to determine free space for " + data_path);
-    } else if (free >= freeStorageSize) {
-        return ok();
     }
 
-    LOG(DEBUG) << "Found " << data_path << " with " << free << " free; caller requested "
-            << freeStorageSize;
+    int64_t needed = freeStorageSize - free;
+    LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested "
+            << freeStorageSize << "; needed " << needed;
+
+    if (free >= freeStorageSize) {
+        return ok();
+    }
 
     if (flags & FLAG_FREE_CACHE_V2) {
         // This new cache strategy fairly removes files from UIDs by deleting
@@ -783,12 +950,10 @@
         for (auto user : get_known_users(uuid_)) {
             FTS *fts;
             FTSENT *p;
-            char *argv[] = {
-                    (char*) create_data_user_ce_path(uuid_, user).c_str(),
-                    (char*) create_data_user_de_path(uuid_, user).c_str(),
-                    nullptr
-            };
-            if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+            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)) != NULL) {
@@ -801,9 +966,14 @@
                         auto tracker = std::shared_ptr<CacheTracker>(new CacheTracker(
                                 multiuser_get_user_id(uid), multiuser_get_app_id(uid), device));
                         tracker->addDataPath(p->fts_path);
-                        tracker->cacheQuota = mCacheQuotas[uid];
+                        {
+                            std::lock_guard<std::recursive_mutex> lock(mCacheQuotasLock);
+                            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;
@@ -833,6 +1003,14 @@
         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;
+            }
+
             // 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()
@@ -879,26 +1057,7 @@
         ATRACE_END();
 
     } else {
-        ATRACE_BEGIN("start");
-        cache_t* cache = start_cache_collection();
-        ATRACE_END();
-
-        ATRACE_BEGIN("add");
-        for (auto user : get_known_users(uuid_)) {
-            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()));
-        }
-        ATRACE_END();
-
-        ATRACE_BEGIN("clear");
-        clear_cache_files(data_path, cache, freeStorageSize);
-        ATRACE_END();
-
-        ATRACE_BEGIN("finish");
-        finish_cache_collection(cache);
-        ATRACE_END();
+        return error("Legacy cache logic no longer supported");
     }
 
     free = data_disk_free(data_path);
@@ -963,61 +1122,81 @@
 
     struct dqblk dq;
 
-    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,
+    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 GID " << cacheGid;
+                PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
             }
         } else {
 #if MEASURE_DEBUG
-            LOG(DEBUG) << "quotactl() for GID " << cacheGid << " " << dq.dqb_curspace;
+            LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
 #endif
-            stats->cacheSize += dq.dqb_curspace;
+            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;
+            }
         }
     }
 
-    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 (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;
+                LOG(DEBUG) << "quotactl() for GID " << extGid << " " << dq.dqb_curspace;
 #endif
-            extStats->dataSize += dq.dqb_curspace;
+                extStats->dataSize += dq.dqb_curspace;
+            }
         }
-    }
 
-    int sharedGid = multiuser_get_shared_gid(userId, 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 {
+        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 " << sharedGid << " " << dq.dqb_curspace;
+                LOG(DEBUG) << "quotactl() for GID " << extCacheGid << " " << dq.dqb_curspace;
 #endif
-            stats->codeSize += dq.dqb_curspace;
+                extStats->dataSize += dq.dqb_curspace;
+                extStats->cacheSize += dq.dqb_curspace;
+            }
         }
     }
 }
@@ -1093,9 +1272,10 @@
             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 && (s.st_uid >= AID_APP_START && s.st_uid <= AID_APP_END)) {
+            } 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);
@@ -1109,7 +1289,7 @@
     FTS *fts;
     FTSENT *p;
     char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
         PLOG(ERROR) << "Failed to fts_open " << path;
         return;
     }
@@ -1148,7 +1328,8 @@
     for (auto packageName : packageNames) {
         CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     }
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    // 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
@@ -1193,14 +1374,13 @@
         ATRACE_BEGIN("code");
         for (auto codePath : codePaths) {
             calculate_tree_size(codePath, &stats.codeSize, -1,
-                    multiuser_get_shared_gid(userId, appId));
+                    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) {
@@ -1218,12 +1398,16 @@
             collectManualStats(dePath, &stats);
             ATRACE_END();
 
-            ATRACE_BEGIN("profiles");
-            auto userProfilePath = create_primary_current_profile_package_dir_path(userId, pkgname);
-            calculate_tree_size(userProfilePath, &stats.dataSize);
-            auto refProfilePath = create_primary_reference_profile_package_dir_path(pkgname);
-            calculate_tree_size(refProfilePath, &stats.codeSize);
-            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);
@@ -1233,15 +1417,15 @@
             ATRACE_END();
         }
 
-        ATRACE_BEGIN("dalvik");
-        int32_t sharedGid = multiuser_get_shared_gid(userId, appId);
-        if (sharedGid != -1) {
-            calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
-                    sharedGid, -1);
+        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();
         }
-        calculate_tree_size(create_primary_cur_profile_dir_path(userId), &stats.dataSize,
-                multiuser_get_uid(userId, appId), -1);
-        ATRACE_END();
     }
 
     std::vector<int64_t> ret;
@@ -1263,7 +1447,8 @@
         std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    // 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
@@ -1312,12 +1497,14 @@
         collectManualStatsForUser(dePath, &stats, true);
         ATRACE_END();
 
-        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();
+        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);
@@ -1334,23 +1521,28 @@
         }
         ATRACE_END();
 
-        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();
+        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");
@@ -1369,12 +1561,14 @@
         collectManualStatsForUser(dePath, &stats);
         ATRACE_END();
 
-        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();
+        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);
@@ -1385,10 +1579,12 @@
 #endif
         ATRACE_END();
 
-        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();
+        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;
@@ -1406,10 +1602,12 @@
 }
 
 binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid,
-        int32_t userId, int32_t flags, std::vector<int64_t>* _aidl_return) {
+        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);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    // 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
@@ -1424,6 +1622,7 @@
     int64_t audioSize = 0;
     int64_t videoSize = 0;
     int64_t imageSize = 0;
+    int64_t appSize = 0;
 
     auto device = findQuotaDeviceForUuid(uuid);
     if (device.empty()) {
@@ -1433,6 +1632,7 @@
     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) {
@@ -1441,7 +1641,7 @@
             }
         } else {
 #if MEASURE_DEBUG
-        LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+            LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
 #endif
             totalSize = dq.dqb_curspace;
         }
@@ -1450,7 +1650,7 @@
         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;
+            LOG(DEBUG) << "quotactl() for GID " << audioGid << " " << dq.dqb_curspace;
 #endif
             audioSize = dq.dqb_curspace;
         }
@@ -1458,7 +1658,7 @@
         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;
+            LOG(DEBUG) << "quotactl() for GID " << videoGid << " " << dq.dqb_curspace;
 #endif
             videoSize = dq.dqb_curspace;
         }
@@ -1466,16 +1666,29 @@
         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;
+            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 + extStats.cacheSize;
+        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_XDEV, NULL))) {
+        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) {
@@ -1484,7 +1697,7 @@
             switch (p->fts_info) {
             case FTS_F:
                 // Only categorize files not belonging to apps
-                if (p->fts_statp->st_gid < AID_APP_START) {
+                if (p->fts_parent->fts_number == 0) {
                     ext = strrchr(p->fts_name, '.');
                     if (ext != nullptr) {
                         switch (MatchExtension(++ext)) {
@@ -1496,14 +1709,23 @@
                 }
                 // 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;
@@ -1511,6 +1733,7 @@
     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
@@ -1522,7 +1745,7 @@
         int32_t userId, int32_t appId, int64_t cacheQuota) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    std::lock_guard<std::recursive_mutex> lock(mCacheQuotasLock);
 
     int32_t uid = multiuser_get_uid(userId, appId);
     mCacheQuotas[uid] = cacheQuota;
@@ -1560,7 +1783,8 @@
         const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
         int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
         const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
-        const std::unique_ptr<std::string>& sharedLibraries) {
+        const std::unique_ptr<std::string>& sharedLibraries,
+        const std::unique_ptr<std::string>& seInfo) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     if (packageName && *packageName != "*") {
@@ -1575,9 +1799,9 @@
     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);
+            oat_dir, dexFlags, compiler_filter, volume_uuid, shared_libraries, se_info);
     return res ? error(res, "Failed to dexopt") : ok();
 }
 
@@ -1818,6 +2042,22 @@
     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) {
@@ -1958,7 +2198,7 @@
 
 binder::Status InstalldNativeService::invalidateMounts() {
     ENFORCE_UID(AID_SYSTEM);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
+    std::lock_guard<std::recursive_mutex> lock(mQuotaDevicesLock);
 
     mQuotaDevices.clear();
 
@@ -1981,6 +2221,17 @@
                     reinterpret_cast<char*>(&dq)) == 0) {
                 LOG(DEBUG) << "Found " << source << " with quota";
                 mQuotaDevices[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;
+                }
             }
         }
     }
@@ -1989,9 +2240,16 @@
 
 std::string InstalldNativeService::findQuotaDeviceForUuid(
         const std::unique_ptr<std::string>& uuid) {
+    std::lock_guard<std::recursive_mutex> lock(mQuotaDevicesLock);
     auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
     return mQuotaDevices[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
index 37e0090..7f73c2a 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -58,6 +58,8 @@
     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,
@@ -66,7 +68,8 @@
             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, std::vector<int64_t>* _aidl_return);
+            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);
@@ -80,7 +83,8 @@
             const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
             int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
             const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
-            const std::unique_ptr<std::string>& sharedLibraries);
+            const std::unique_ptr<std::string>& sharedLibraries,
+            const std::unique_ptr<std::string>& seInfo);
 
     binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
 
@@ -92,6 +96,7 @@
 
     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 freeStorageSize,
@@ -110,10 +115,15 @@
         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 mQuotaDevicesLock;
+    std::recursive_mutex mCacheQuotasLock;
+
     /* Map from mount point to underlying device node */
     std::unordered_map<std::string, std::string> mQuotaDevices;
     /* Map from UID to cache quota size */
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 4dbfa91..6b99c1d 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -32,11 +32,13 @@
     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);
+    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);
 
@@ -48,7 +50,8 @@
             @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 sharedLibraries,
+            @nullable @utf8InCpp String seInfo);
 
     void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
 
@@ -58,6 +61,7 @@
     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 freeStorageSize, int flags);
@@ -76,4 +80,5 @@
         int storage_flag);
 
     void invalidateMounts();
+    boolean isQuotaSupported(@nullable @utf8InCpp String uuid);
 }
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 0d89da4..e9d06d1 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -36,6 +36,7 @@
 #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"
@@ -66,14 +67,6 @@
     return unique_fd(-1);
 }
 
-static const char* parse_null(const char* arg) {
-    if (strcmp(arg, "!") == 0) {
-        return nullptr;
-    } else {
-        return arg;
-    }
-}
-
 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) {
@@ -1319,17 +1312,9 @@
     }
     std::string dex_dir = dex_path.substr(0, dirIndex);
 
-    // Assign the gid to the cache gid so that the oat file storage
-    // is counted towards the app cache.
-    int32_t cache_gid = multiuser_get_cache_gid(
-            multiuser_get_user_id(uid), multiuser_get_app_id(uid));
-    // If UID doesn't have a specific cache GID, use UID value
-    if (cache_gid == -1) {
-        cache_gid = uid;
-    }
-
     // Create oat file output directory.
-    if (prepare_app_cache_dir(dex_dir, "oat", 02711, uid, cache_gid) != 0) {
+    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;
     }
@@ -1339,7 +1324,7 @@
     oat_dir_out->assign(oat_dir);
 
     // Create oat/isa output directory.
-    if (prepare_app_cache_dir(*oat_dir_out, instruction_set, 02711, uid, cache_gid) != 0) {
+    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;
     }
@@ -1383,12 +1368,15 @@
 // 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, dexopt_needed_out is assigned a valid OatFileAsssitant::DexOptNeeded
-// code and oat_dir_out is assigned the oat dir path where the oat file should be stored.
+// 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, int* dexopt_needed_out, std::string* oat_dir_out,
-        std::string* dex_path_out) {
+        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) {
@@ -1424,7 +1412,8 @@
     }
 
     // Check if the path exist. If not, there's nothing to do.
-    if (access(dex_path.c_str(), F_OK) != 0) {
+    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
@@ -1435,6 +1424,11 @@
         }
     }
 
+    // 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;
@@ -1475,14 +1469,14 @@
 
 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* 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 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;
@@ -1494,7 +1488,8 @@
     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, &dexopt_needed, &oat_dir_str, &dex_real_path)) {
+                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) {
@@ -1533,6 +1528,19 @@
         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);
 
@@ -1862,19 +1870,5 @@
     return return_value_oat && return_value_art;
 }
 
-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");
-}
-
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index f144be8..cb8aaeb 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -59,13 +59,7 @@
 
 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 constexpr size_t DEXOPT_PARAM_COUNT = 10U;
-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]);
+        const char* volume_uuid, const char* shared_libraries, const char* se_info);
 
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index d5d5236..240aa49 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,2 +1,103 @@
+
 service installd /system/bin/installd
     class main
+
+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/matchgen.py b/cmds/installd/matchgen.py
index b37352b..131487d 100644
--- a/cmds/installd/matchgen.py
+++ b/cmds/installd/matchgen.py
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import collections
+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"],
@@ -22,6 +22,19 @@
     "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
  *
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 82b8cc2..ff838ce 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>
@@ -33,12 +34,14 @@
 #include <android-base/strings.h>
 #include <cutils/fs.h>
 #include <cutils/properties.h>
+#include <dex2oat_return_codes.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.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"
@@ -144,6 +147,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 +262,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 +292,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;
+                    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_SAFEMODE, DEXOPT_SAFEMODE) |
+                            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 +531,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;
@@ -535,14 +761,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 +780,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 +967,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_;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index cec8f68..2030997 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -27,7 +27,6 @@
 
 #include "installd_constants.h"
 #include "otapreopt_utils.h"
-#include "dexopt.h"
 
 #ifndef LOG_TAG
 #define LOG_TAG "otapreopt"
@@ -137,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
index a32df22..630c1f3 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -14,3 +14,41 @@
         "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/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
new file mode 100644
index 0000000..174ce77
--- /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,
+            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,
+            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,
+            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,
+            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,
+            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,
+            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,
+            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,
+            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..4a1f333
--- /dev/null
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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] ATTRIBUTE_UNUSED,
+        const char *src ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+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"));
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index d1e5919..dab3236 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -29,6 +29,7 @@
 #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/"
 
@@ -59,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);
 
@@ -90,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) {
@@ -543,7 +547,7 @@
 
 TEST_F(UtilsTest, CreatePrimaryCurrentProfile) {
     std::string expected =
-        create_primary_current_profile_package_dir_path(1, "com.example") + "/primary.prof";
+        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));
 }
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index bdd62e6..c792082 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -23,16 +23,12 @@
 #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/properties.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 
@@ -42,7 +38,6 @@
 #define LOG_TAG "installd"
 #endif
 
-#define CACHE_NOISY(x) //x
 #define DEBUG_XATTRS 0
 
 using android::base::StringPrintf;
@@ -155,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);
@@ -309,7 +307,7 @@
     FTSENT *p;
     int64_t matchedSize = 0;
     char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) {
         if (errno != ENOENT) {
             PLOG(ERROR) << "Failed to fts_open " << path;
         }
@@ -631,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));
@@ -982,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
@@ -1445,6 +1029,10 @@
     } 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
@@ -1452,25 +1040,25 @@
     FTS *fts;
     FTSENT *p;
     char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+    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_accpath, target_mode) != 0) {
+            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_accpath, -1, gid) != 0) {
+            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_accpath, -1, gid) != 0) {
+            if (lchown(p->fts_path, -1, gid) != 0) {
                 PLOG(WARNING) << "Failed to chown " << p->fts_path;
             }
             break;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index c540c52..dd94da9 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -31,41 +31,17 @@
 #include <installd_constants.h>
 
 #define MEASURE_DEBUG 0
+#define FIXUP_DEBUG 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,
@@ -137,19 +113,11 @@
 
 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);
diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp
index 8cffb3c..b703ed4 100644
--- a/cmds/service/Android.bp
+++ b/cmds/service/Android.bp
@@ -9,5 +9,18 @@
     ],
 
     cflags: ["-DXP_UNIX"],
-    //shared_libs: ["librt"],
+}
+
+cc_binary {
+    name: "vndservice",
+
+    proprietary: true,
+    srcs: ["service.cpp"],
+
+    shared_libs: [
+        "libutils",
+        "libbinder",
+    ],
+
+    cflags: ["-DXP_UNIX", "-DVENDORSERVICES"],
 }
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
index dc8e675..39d92a7 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -34,3 +34,19 @@
     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/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 753aeb5..93a18fc 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -94,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;
@@ -105,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 881ab07..c95b33f 100644
--- a/cmds/servicemanager/binder.h
+++ b/cmds/servicemanager/binder.h
@@ -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 43c4c8b..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 <log/log.h>
 #endif
+#include <log/log.h>
 
 struct audit_data {
     pid_t pid;
@@ -360,14 +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;
     }
 
@@ -381,7 +394,11 @@
     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) {
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/cmds/vr/pose/Android.mk b/cmds/vr/pose/Android.mk
new file mode 100644
index 0000000..8be3214
--- /dev/null
+++ b/cmds/vr/pose/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+  pose.cpp
+
+staticLibraries := \
+  libdvrcommon \
+  libvrsensor \
+  libpdx_default_transport \
+
+sharedLibraries := \
+  libcutils \
+  liblog
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := pose
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/vr/pose/pose.cpp b/cmds/vr/pose/pose.cpp
new file mode 100644
index 0000000..2288a86
--- /dev/null
+++ b/cmds/vr/pose/pose.cpp
@@ -0,0 +1,274 @@
+// pose is a utility to query and manipulate the current pose via the pose
+// service.
+
+#include <cmath>
+#include <cstdio>
+#include <iomanip>
+#include <iostream>
+#include <regex>
+#include <vector>
+
+#include <private/dvr/types.h>
+#include <dvr/pose_client.h>
+
+using android::dvr::vec3;
+using android::dvr::quat;
+
+namespace {
+
+// Prints usage information to stderr.
+void PrintUsage(const char* executable_name) {
+  std::cerr << "Usage: " << executable_name
+            << " [--identity|--set=...|--unfreeze]\n"
+            << "\n"
+            << "  no arguments: display the current pose.\n"
+            << "  --identity: freeze the pose to the identity pose.\n"
+            << "  --set=rx,ry,rz,rw[,px,py,pz]: freeze the pose to the given "
+               "state. rx,ry,rz,rw are interpreted as rotation quaternion. "
+               " px, py, pz as position (0,0,0 if omitted).\n"
+            << "  --mode=mode: sets mode to one of normal, head_turn:slow, "
+               "head_turn:fast, rotate:slow, rotate:medium, rotate:fast, "
+               "circle_strafe.\n"
+            << "  --unfreeze: sets the mode to normal.\n"
+            << "  --log_controller=[true|false]: starts and stops controller"
+               " logs\n"
+            << std::endl;
+}
+
+// If return_code is negative, print out its corresponding string description
+// and exit the program with a non-zero exit code.
+void ExitIfNegative(int return_code) {
+  if (return_code < 0) {
+    std::cerr << "Error: " << strerror(-return_code) << std::endl;
+    std::exit(1);
+  }
+}
+
+// Parses the following command line flags:
+// --identity
+// --set=rx,ry,rz,rw[,px,py,pz]
+// Returns false if parsing fails.
+bool ParseState(const std::string& arg, DvrPoseState* out_state) {
+  if (arg == "--identity") {
+    *out_state = {.head_from_start_rotation = {0.f, 0.f, 0.f, 1.f},
+                  .head_from_start_translation = {0.f, 0.f, 0.f},
+                  .timestamp_ns = 0,
+                  .sensor_from_start_rotation_velocity = {0.f, 0.f, 0.f}};
+    return true;
+  }
+
+  const std::string prefix("--set=");
+  if (arg.size() < 6 || arg.compare(0, prefix.size(), prefix) != 0) {
+    return false;
+  }
+
+  // Tokenize by ','.
+  std::regex split_by_comma("[,]+");
+  std::sregex_token_iterator token_it(arg.begin() + prefix.size(), arg.end(),
+                                      split_by_comma,
+                                      -1 /* return inbetween parts */);
+  std::sregex_token_iterator token_end;
+
+  // Convert to float and store values.
+  std::vector<float> values;
+  for (; token_it != token_end; ++token_it) {
+    std::string token = *(token_it);
+    float value = 0.f;
+    if (sscanf(token.c_str(), "%f", &value) != 1) {
+      std::cerr << "Unable to parse --set value as float: " << token
+                << std::endl;
+      return false;
+    } else {
+      values.push_back(value);
+    }
+  }
+
+  if (values.size() != 4 && values.size() != 7) {
+    std::cerr << "Unable to parse --set, expected either 4 or 7 of values."
+              << std::endl;
+    return false;
+  }
+
+  float norm2 = values[0] * values[0] + values[1] * values[1] +
+                values[2] * values[2] + values[3] * values[3];
+  if (std::abs(norm2 - 1.f) > 1e-4) {
+    if (norm2 < 1e-8) {
+      std::cerr << "--set quaternion norm close to zero." << std::endl;
+      return false;
+    }
+    float norm = std::sqrt(norm2);
+    values[0] /= norm;
+    values[1] /= norm;
+    values[2] /= norm;
+    values[3] /= norm;
+  }
+
+  out_state->head_from_start_rotation = {values[0], values[1], values[2],
+                                         values[3]};
+
+  if (values.size() == 7) {
+    out_state->head_from_start_translation = {values[4], values[5], values[6]};
+  } else {
+    out_state->head_from_start_translation = {0.f, 0.f, 0.f};
+  }
+
+  out_state->timestamp_ns = 0;
+  out_state->sensor_from_start_rotation_velocity = {0.f, 0.f, 0.f};
+
+  return true;
+}
+
+// Parses the command line flag --mode.
+// Returns false if parsing fails.
+bool ParseSetMode(const std::string& arg, DvrPoseMode* mode) {
+  const std::string prefix("--mode=");
+  if (arg.size() < prefix.size() ||
+      arg.compare(0, prefix.size(), prefix) != 0) {
+    return false;
+  }
+
+  std::string value = arg.substr(prefix.size());
+
+  if (value == "normal") {
+    *mode = DVR_POSE_MODE_6DOF;
+    return true;
+  } else if (value == "head_turn:slow") {
+    *mode = DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW;
+    return true;
+  } else if (value == "head_turn:fast") {
+    *mode = DVR_POSE_MODE_MOCK_HEAD_TURN_FAST;
+    return true;
+  } else if (value == "rotate:slow") {
+    *mode = DVR_POSE_MODE_MOCK_ROTATE_SLOW;
+    return true;
+  } else if (value == "rotate:medium") {
+    *mode = DVR_POSE_MODE_MOCK_ROTATE_MEDIUM;
+    return true;
+  } else if (value == "rotate:fast") {
+    *mode = DVR_POSE_MODE_MOCK_ROTATE_FAST;
+    return true;
+  } else if (value == "circle_strafe") {
+    *mode = DVR_POSE_MODE_MOCK_CIRCLE_STRAFE;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// Parses the command line flag --controller_log.
+// Returns false if parsing fails.
+bool ParseLogController(const std::string& arg, bool* log_enabled) {
+  const std::string prefix("--log_controller=");
+  if (arg.size() < prefix.size() ||
+      arg.compare(0, prefix.size(), prefix) != 0) {
+    return false;
+  }
+
+  std::string value = arg.substr(prefix.size());
+
+  if (value == "false") {
+    *log_enabled = false;
+    return true;
+  } else if (value == "true") {
+    *log_enabled = true;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// The different actions that the tool can perform.
+enum class Action {
+  Query,                 // Query the current pose.
+  Set,                   // Set the pose and freeze.
+  Unfreeze,              // Set the pose mode to normal.
+  SetMode,               // Sets the pose mode.
+  LogController,         // Start/stop controller logging in sensord.
+};
+
+// The action to perform when no arguments are passed to the tool.
+constexpr Action kDefaultAction = Action::Query;
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  Action action = kDefaultAction;
+  DvrPoseState state;
+  DvrPoseMode pose_mode = DVR_POSE_MODE_6DOF;
+  bool log_controller = false;
+
+  // Parse command-line arguments.
+  for (int i = 1; i < argc; ++i) {
+    const std::string arg = argv[i];
+    if (ParseState(arg, &state) && action == kDefaultAction) {
+      action = Action::Set;
+    } else if (arg == "--unfreeze" && action == kDefaultAction) {
+      action = Action::Unfreeze;
+    } else if (ParseSetMode(arg, &pose_mode) && action == kDefaultAction) {
+      action = Action::SetMode;
+    } else if (ParseLogController(arg, &log_controller)) {
+      action = Action::LogController;
+    } else {
+      PrintUsage(argv[0]);
+      return 1;
+    }
+  }
+
+  auto pose_client = dvrPoseCreate();
+  if (!pose_client) {
+    std::cerr << "Unable to create pose client." << std::endl;
+    return 1;
+  }
+
+  switch (action) {
+    case Action::Query: {
+      ExitIfNegative(dvrPosePoll(pose_client, &state));
+      uint64_t timestamp = state.timestamp_ns;
+      const auto& rotation = state.head_from_start_rotation;
+      const auto& translation = state.head_from_start_translation;
+      const auto& rotation_velocity = state.sensor_from_start_rotation_velocity;
+      quat q(rotation.w, rotation.x, rotation.y, rotation.z);
+      vec3 angles = q.matrix().eulerAngles(0, 1, 2);
+      angles = angles * 180.f / M_PI;
+      vec3 x = q * vec3(1.0f, 0.0f, 0.0f);
+      vec3 y = q * vec3(0.0f, 1.0f, 0.0f);
+      vec3 z = q * vec3(0.0f, 0.0f, 1.0f);
+
+      std::cout << "timestamp_ns: " << timestamp << std::endl
+                << "rotation_quaternion: " << rotation.x << ", " << rotation.y
+                << ", " << rotation.z << ", " << rotation.w << std::endl
+                << "rotation_angles: " << angles.x() << ", " << angles.y()
+                << ", " << angles.z() << std::endl
+                << "translation: " << translation.x << ", " << translation.y
+                << ", " << translation.z << std::endl
+                << "rotation_velocity: " << rotation_velocity.x << ", "
+                << rotation_velocity.y << ", " << rotation_velocity.z
+                << std::endl
+                << "axes: " << std::setprecision(3)
+                << "x(" << x.x() << ", " << x.y() << ", " << x.z() << "), "
+                << "y(" << y.x() << ", " << y.y() << ", " << y.z() << "), "
+                << "z(" << z.x() << ", " << z.y() << ", " << z.z() << "), "
+                << std::endl;
+      break;
+    }
+    case Action::Set: {
+      ExitIfNegative(dvrPoseFreeze(pose_client, &state));
+      break;
+    }
+    case Action::Unfreeze: {
+      ExitIfNegative(dvrPoseSetMode(pose_client, DVR_POSE_MODE_6DOF));
+      break;
+    }
+    case Action::SetMode: {
+      ExitIfNegative(dvrPoseSetMode(pose_client, pose_mode));
+      break;
+    }
+    case Action::LogController: {
+      ExitIfNegative(
+          dvrPoseLogController(pose_client, log_controller));
+      break;
+    }
+  }
+
+  dvrPoseDestroy(pose_client);
+}
diff --git a/cmds/vr/vrscreencap/Android.mk b/cmds/vr/vrscreencap/Android.mk
new file mode 100644
index 0000000..804afc9
--- /dev/null
+++ b/cmds/vr/vrscreencap/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	vrscreencap.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+	libbufferhub \
+	libdisplay \
+	libimageio \
+	libpdx_default_transport \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libpng \
+	libsync \
+	libui \
+
+LOCAL_MODULE := vrscreencap
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/vr/vrscreencap/vrscreencap.cpp b/cmds/vr/vrscreencap/vrscreencap.cpp
new file mode 100644
index 0000000..3d0d112
--- /dev/null
+++ b/cmds/vr/vrscreencap/vrscreencap.cpp
@@ -0,0 +1,74 @@
+// screencap is a tool for taking screenshots using the screenshot service.
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <private/dvr/image_io.h>
+#include <private/dvr/screenshot_client.h>
+
+namespace {
+
+// Attempt to take a screenshot and save it to |filename|.
+// Returns zero on success, or a non-zero exit code otherwise.
+int TakeScreenshot(const std::string& app_name, const std::string& filename,
+                   int index) {
+  auto error_out = [app_name]() -> std::ostream& {
+    return std::cerr << app_name << ": ";
+  };
+
+  auto info_out = [app_name]() -> std::ostream& {
+    return std::cout << app_name << ": ";
+  };
+
+  auto client = android::dvr::ScreenshotClient::Create();
+
+  if (client->format() != HAL_PIXEL_FORMAT_RGB_888) {
+    error_out() << "The screenshot format for this device is not supported."
+                << std::endl;
+    return 1;
+  }
+
+  std::vector<uint8_t> image;
+  int width = 0;
+  int height = 0;
+  if (client->Take(&image, index, &width, &height) != 0) {
+    error_out() << "Failed to take screenshot." << std::endl;
+    return 1;
+  }
+
+  info_out() << "Got " << width << "x" << height << " screenshot." << std::endl;
+
+  if (!image_io_write_rgb888(filename.c_str(), width, height, image.data())) {
+    error_out() << "Failed to write image to output file " << filename
+                << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  // Parse arguments
+  if (argc != 2 && argc != 3) {
+    std::cerr
+        << "Usage: " << argv[0]
+        << " filename.[" DVR_IMAGE_IO_SUPPORTED_WRITE
+           "] [INDEX]\n"
+           "INDEX: specify 1..n to grab hw_composer layers by index.\n"
+           "       specify -n to grab pre-warp layers (-1 is base layer).\n"
+           "       the default is 1 (the base hw_composer layer).\n"
+           "       an invalid index will result in an error.\n";
+    return 1;
+  }
+  const std::string filename(argv[1]);
+  int index = 1;
+  if (argc > 2)
+    index = atoi(argv[2]);
+
+  // Perform the actual screenshot.
+  return TakeScreenshot(argv[0], filename, index);
+}
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.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.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..a7955e9 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 -->
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/configuration.h b/include/android/configuration.h
index 8e10f67..6287332 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -267,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,
     /**
@@ -300,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,
@@ -426,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.
      */
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 f928c6e..0829989 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -835,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 */
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index be01518..97892f8 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -56,12 +56,10 @@
 /**
  * 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);
 
@@ -75,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);
 
@@ -96,10 +92,8 @@
  *     - 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,
diff --git a/include/android/native_window.h b/include/android/native_window.h
deleted file mode 100644
index b60b9f1..0000000
--- a/include/android/native_window.h
+++ /dev/null
@@ -1,152 +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 <sys/cdefs.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 1ec2a67..23b39aa 100644
--- a/include/android/native_window_jni.h
+++ b/include/android/native_window_jni.h
@@ -54,6 +54,17 @@
 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 6c12972..97b4a2a 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -48,15 +48,22 @@
  *
  */
 
+#include <android/looper.h>
+
 #include <stdbool.h>
 #include <sys/types.h>
-
-#include <android/looper.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.
@@ -64,6 +71,10 @@
  */
 enum {
     /**
+     * Invalid sensor type. Returned by {@link ASensor_getType} as error value.
+     */
+    ASENSOR_TYPE_INVALID = -1,
+    /**
      * {@link ASENSOR_TYPE_ACCELEROMETER}
      * reporting-mode: continuous
      *
@@ -135,6 +146,8 @@
  * Sensor Reporting Modes.
  */
 enum {
+    /** invalid reporting mode */
+    AREPORTING_MODE_INVALID = -1,
     /** continuous reporting */
     AREPORTING_MODE_CONTINUOUS = 0,
     /** reporting on change */
@@ -145,6 +158,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
  */
@@ -373,8 +410,7 @@
  * 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
 
 /**
@@ -392,6 +428,98 @@
  */
 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
 
 /*****************************************************************************/
 
@@ -401,7 +529,7 @@
  * 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.
@@ -442,8 +570,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);
 
 
 /*****************************************************************************/
@@ -503,6 +630,29 @@
 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
 };
 #endif
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
new file mode 100644
index 0000000..8f8a931
--- /dev/null
+++ b/include/android/sharedmem.h
@@ -0,0 +1,120 @@
+/*
+ * 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 ommited).
+ *
+ * \code{.c}
+ *   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.
+ * \endcode
+ *
+ * \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 6cdcfeb..d3b1fb6 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -14,6 +14,13 @@
  * 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
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
new file mode 100644
index 0000000..834dcbd
--- /dev/null
+++ b/include/audiomanager/AudioManager.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_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_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 9154076..b226dd6 100644
--- a/include/batteryservice/IBatteryPropertiesListener.h
+++ b/include/batteryservice/IBatteryPropertiesListener.h
@@ -40,6 +40,12 @@
 
 // ----------------------------------------------------------------------------
 
+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/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/IBinder.h b/include/binder/IBinder.h
deleted file mode 100644
index 9097cb3..0000000
--- a/include/binder/IBinder.h
+++ /dev/null
@@ -1,173 +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;
-
-    // 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/include/binder/IInterface.h b/include/binder/IInterface.h
deleted file mode 100644
index be72d44..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:
-    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/include/binder/Parcel.h b/include/binder/Parcel.h
deleted file mode 100644
index b0d53ef..0000000
--- a/include/binder/Parcel.h
+++ /dev/null
@@ -1,890 +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 <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>
-
-// ---------------------------------------------------------------------------
-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);
-
-    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);
-
-    // 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 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;
-
-    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;
-
-    // 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(
-                            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
-
-    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
-
-    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::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/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/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 9d65fad..0000000
--- a/include/gui/BitTube.h
+++ /dev/null
@@ -1,94 +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 <android/log.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
-
-#endif // ANDROID_GUI_SENSOR_CHANNEL_H
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index 5232d0f..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>
 
@@ -59,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;
 
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 266f0aa..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 {
@@ -62,12 +58,14 @@
     public:
         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 e2bafec..1e22d28 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
@@ -134,7 +135,7 @@
     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 +145,7 @@
     virtual status_t discardFreeBuffers() override;
 
     // dump our state in a String
-    virtual void dumpState(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 b1c730a..cfe716f 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 dumpState(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.
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 79e7af2..5541468 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -29,7 +29,7 @@
 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 9f8b638..7912528 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
@@ -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
@@ -244,6 +248,10 @@
     // 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 cb9b373..9557b4f 100644
--- a/include/gui/DisplayEventReceiver.h
+++ b/include/gui/DisplayEventReceiver.h
@@ -32,9 +32,12 @@
 
 // ----------------------------------------------------------------------------
 
-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 |
@@ -108,15 +111,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
@@ -134,7 +135,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 460a03d..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() { }
+    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)
 };
 
-// ----------------------------------------------------------------------------
-
-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 848368c..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)
 
     /*
-     * 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 acc2f30..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 dcddca4..63254ed 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,157 @@
     //               * 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).
+    // 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.
     virtual status_t discardFreeBuffers() = 0;
 
     // dump state into a string
-    virtual void dumpState(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 c2dba50..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
@@ -295,6 +301,7 @@
     struct QueueBufferInput : public Flattenable<QueueBufferInput> {
         friend class Flattenable<QueueBufferInput>;
         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,19 +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).
+        // 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)
+                uint32_t _sticky = 0, bool _getFrameTimestamps = false)
                 : timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp),
                   dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode),
                   transform(_transform), stickyTransform(_sticky), fence(_fence),
-                  surfaceDamage() { }
+                  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;
@@ -328,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;
@@ -340,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 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,
@@ -481,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).
@@ -581,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/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h
deleted file mode 100644
index 857444b..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 737c430..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 555a0cc..1112973 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -28,9 +28,9 @@
 #include <binder/IInterface.h>
 
 #include <ui/FrameStats.h>
+#include <ui/PixelFormat.h>
 
-#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceComposerClient.h>
+#include <vector>
 
 namespace android {
 // ----------------------------------------------------------------------------
@@ -41,8 +41,10 @@
 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
@@ -75,9 +77,16 @@
      */
     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;
@@ -112,6 +121,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 +163,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 +185,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 +200,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 +221,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 4a4efb6..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)
 
     // 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/Sensor.h b/include/gui/Sensor.h
deleted file mode 100644
index 7506835..0000000
--- a/include/gui/Sensor.h
+++ /dev/null
@@ -1,140 +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 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 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 489d5ea..8b1d106 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.
      */
-    explicit 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,38 @@
     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;
 
 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 +213,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 +232,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 +251,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;
 
@@ -379,46 +423,21 @@
 
     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 f2932f2..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>
@@ -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..712a323 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
@@ -97,8 +140,6 @@
     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);
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/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/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_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/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/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 a22b2cb..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>
 
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 1df15f8..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.
@@ -57,6 +56,12 @@
     // closed.
     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
     // is already signalled, so using this is usually not necessary.
@@ -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/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 d523c4f..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
-
-    struct 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/Rect.h b/include/ui/Rect.h
index e9859fe..b50e4ec 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -22,6 +22,7 @@
 #include <utils/TypeHelpers.h>
 #include <log/log.h>
 
+#include <ui/FloatRect.h>
 #include <ui/Point.h>
 
 #include <android/rect.h>
@@ -44,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;
@@ -179,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/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
index f7347ae..204fdb5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -12,6 +12,11 @@
 // 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",
 
@@ -24,6 +29,7 @@
         "BpBinder.cpp",
         "BufferedTextOutput.cpp",
         "Debug.cpp",
+        "IActivityManager.cpp",
         "IAppOpsCallback.cpp",
         "IAppOpsService.cpp",
         "IBatteryStats.cpp",
@@ -35,6 +41,7 @@
         "IProcessInfoService.cpp",
         "IResultReceiver.cpp",
         "IServiceManager.cpp",
+        "IShellCallback.cpp",
         "MemoryBase.cpp",
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
@@ -46,6 +53,8 @@
         "Static.cpp",
         "Status.cpp",
         "TextOutput.cpp",
+        "IpPrefix.cpp",
+        "Value.cpp",
     ],
 
     cflags: [
@@ -70,6 +79,10 @@
         "libutils",
     ],
 
+    export_include_dirs: [
+        "include",
+    ],
+
     clang: true,
     sanitize: {
         misc_undefined: ["integer"],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 7ce2a31..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,6 +235,8 @@
             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());
 
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/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 6b5b1af..5c1a4f4 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "IMemory"
 
 #include <atomic>
+#include <stdatomic.h>
+
 #include <fcntl.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -28,6 +30,7 @@
 #include <binder/IMemory.h>
 #include <binder/Parcel.h>
 #include <log/log.h>
+
 #include <utils/CallStack.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
@@ -287,7 +290,7 @@
                 mBase   = heap->mBase;
                 mSize   = heap->mSize;
                 mOffset = heap->mOffset;
-                int fd = dup(heap->mHeapId.load(memory_order_relaxed));
+                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);
@@ -322,7 +325,7 @@
 
         Mutex::Autolock _l(mLock);
         if (mHeapId.load(memory_order_relaxed) == -1) {
-            int fd = dup( parcel_fd );
+            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));
 
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 3aeff2e..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>
 
@@ -136,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/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index aed0134..03f00be 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -26,9 +26,9 @@
 #include <unistd.h>
 
 #include <binder/MemoryHeapBase.h>
-#include <log/log.h>
 #include <cutils/ashmem.h>
 #include <cutils/atomic.h>
+#include <log/log.h>
 
 namespace android {
 
@@ -82,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 d753eb5..39bb078 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>
@@ -539,7 +540,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) {
@@ -552,6 +553,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;
@@ -1106,6 +1115,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))
@@ -1142,7 +1155,7 @@
 
 status_t Parcel::writeDupFileDescriptor(int fd)
 {
-    int dupFd = dup(fd);
+    int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
     if (dupFd < 0) {
         return -errno;
     }
@@ -1153,6 +1166,12 @@
     return err;
 }
 
+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());
 }
@@ -1324,6 +1343,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!");
@@ -1427,13 +1560,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;
 }
@@ -1944,6 +2077,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;
@@ -1966,7 +2103,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]);
@@ -1984,7 +2121,6 @@
     return h;
 }
 
-
 int Parcel::readFileDescriptor() const
 {
     const flat_binder_object* flat = readObject(true);
@@ -1996,6 +2132,17 @@
     return BAD_TYPE;
 }
 
+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();
@@ -2004,7 +2151,7 @@
         return BAD_TYPE;
     }
 
-    val->reset(dup(got));
+    val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0));
 
     if (val->get() < 0) {
         return BAD_VALUE;
@@ -2078,11 +2225,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 e7078ba..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>
 
@@ -35,27 +36,13 @@
 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 map<android::String16, T>& map) {
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 5b70501..add5e74 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -71,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;
 }
 
@@ -307,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);
@@ -319,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;
         }
@@ -329,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)
@@ -356,6 +377,7 @@
             ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
             close(mDriverFD);
             mDriverFD = -1;
+            mDriverName.clear();
         }
     }
 
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/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
similarity index 100%
rename from include/binder/Binder.h
rename to libs/binder/include/binder/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/include/binder/IAppOpsCallback.h b/libs/binder/include/binder/IAppOpsCallback.h
similarity index 100%
rename from include/binder/IAppOpsCallback.h
rename to libs/binder/include/binder/IAppOpsCallback.h
diff --git a/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
similarity index 100%
rename from include/binder/IAppOpsService.h
rename to libs/binder/include/binder/IAppOpsService.h
diff --git a/include/binder/IBatteryStats.h b/libs/binder/include/binder/IBatteryStats.h
similarity index 100%
rename from include/binder/IBatteryStats.h
rename to libs/binder/include/binder/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/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h
similarity index 100%
rename from include/binder/IMediaResourceMonitor.h
rename to libs/binder/include/binder/IMediaResourceMonitor.h
diff --git a/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
similarity index 100%
rename from include/binder/IMemory.h
rename to libs/binder/include/binder/IMemory.h
diff --git a/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
similarity index 100%
rename from include/binder/IPCThreadState.h
rename to libs/binder/include/binder/IPCThreadState.h
diff --git a/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h
similarity index 100%
rename from include/binder/IPermissionController.h
rename to libs/binder/include/binder/IPermissionController.h
diff --git a/include/binder/IProcessInfoService.h b/libs/binder/include/binder/IProcessInfoService.h
similarity index 100%
rename from include/binder/IProcessInfoService.h
rename to libs/binder/include/binder/IProcessInfoService.h
diff --git a/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h
similarity index 100%
rename from include/binder/IResultReceiver.h
rename to libs/binder/include/binder/IResultReceiver.h
diff --git a/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
similarity index 100%
rename from include/binder/IServiceManager.h
rename to libs/binder/include/binder/IServiceManager.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/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
similarity index 100%
rename from include/binder/Parcelable.h
rename to libs/binder/include/binder/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/include/binder/PersistableBundle.h b/libs/binder/include/binder/PersistableBundle.h
similarity index 100%
rename from include/binder/PersistableBundle.h
rename to libs/binder/include/binder/PersistableBundle.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/include/binder/Status.h b/libs/binder/include/binder/Status.h
similarity index 100%
rename from include/binder/Status.h
rename to libs/binder/include/binder/Status.h
diff --git a/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
similarity index 100%
rename from include/binder/TextOutput.h
rename to libs/binder/include/binder/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/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
similarity index 100%
rename from include/private/binder/binder_module.h
rename to libs/binder/include/private/binder/binder_module.h
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index acd0538..853ca16 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -26,6 +26,15 @@
 }
 
 cc_test {
+    name: "binderValueTypeTest",
+    srcs: ["binderValueTypeTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+}
+
+cc_test {
     name: "binderLibTest",
     srcs: ["binderLibTest.cpp"],
     shared_libs: [
@@ -71,3 +80,28 @@
         "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/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 54e12b6..757291c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -45,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,
@@ -56,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)
@@ -389,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);
@@ -439,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;
@@ -631,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 */
@@ -670,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(fb->type, BINDER_TYPE_HANDLE);
+    EXPECT_EQ(ProcessState::self()->getStrongProxyForHandle(fb->handle), m_server);
+    EXPECT_EQ(fb->cookie, (binder_uintptr_t)0);
+    EXPECT_EQ(fb->binder >> 32, (binder_uintptr_t)0);
+}
+
+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:
@@ -771,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;
@@ -884,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;
             };
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/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
index 2732071..13f03b1 100644
--- a/libs/binder/tests/schd-dbg.cpp
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -15,6 +15,7 @@
 #include <pthread.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <fstream>
 
 using namespace std;
 using namespace android;
@@ -39,7 +40,9 @@
 // GOOD_SYNC_MIN is considered as good
 #define GOOD_SYNC_MIN (0.6)
 
-#define DUMP_PRICISION 3
+#define DUMP_PRESICION 2
+
+string trace_path = "/sys/kernel/debug/tracing";
 
 // the default value
 int no_process = 2;
@@ -48,6 +51,23 @@
 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;
@@ -197,23 +217,41 @@
   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 (nano > deadline_us * 1000) m_miss++;
+    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?
-    cout << std::setprecision(DUMP_PRICISION) << "{ \"avg\":" << setw(5) << left
-         << average << ", \"wst\":" << setw(5) << left << worst
-         << ", \"bst\":" << setw(5) << left << best << ", \"miss\":" << m_miss
-         << "}";
+    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) << "}";
   }
 };
 
@@ -235,8 +273,15 @@
   }
 }
 
+typedef struct {
+  void* result;
+  int target;
+} thread_priv_t;
+
 static void* thread_start(void* p) {
-  Results* results_fifo = (Results*)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;
 
@@ -244,7 +289,7 @@
   thread_dump("fifo-caller");
 
   sta = tickNow();
-  status_t ret = workers[0]->transact(BINDER_NOP, data, &reply);
+  status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
   end = tickNow();
   results_fifo->add_time(tickNano(sta, end));
 
@@ -254,16 +299,19 @@
 }
 
 // create a fifo thread to transact and wait it to finished
-static void thread_transaction(Results* results_fifo) {
+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, results_fifo));
+  ASSERT(!pthread_create(&thread, &attr, &thread_start, &thread_priv));
   ASSERT(!pthread_join(thread, &dummy));
 }
 
@@ -272,14 +320,16 @@
 void worker_fx(int num, int no_process, int iterations, int payload_size,
                Pipe p) {
   int dummy;
-  Results results_other, results_fifo;
+  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
@@ -301,7 +351,7 @@
     int target = num % server_count;
 
     // 1. transaction by fifo thread
-    thread_transaction(&results_fifo);
+    thread_transaction(target, &results_fifo);
     parcel_fill(data, payload_size, thread_pri(), sched_getcpu());
     thread_dump("other-caller");
 
@@ -319,6 +369,7 @@
   p.wait();
 
   p.send(&dummy);
+  // wait for kill
   p.wait();
   // Client for each pair dump here
   if (is_client(num)) {
@@ -330,10 +381,10 @@
          << "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
          << "\"R\":" << sync_ratio << "," << endl;
 
-    cout << "      \"other_ms\":";
+    cout << "  \"other_ms\":";
     results_other.dump();
     cout << "," << endl;
-    cout << "      \"fifo_ms\": ";
+    cout << "  \"fifo_ms\": ";
     results_fifo.dump();
     cout << endl;
     cout << "}," << endl;
@@ -389,8 +440,28 @@
     }
     if (string(argv[i]) == "-v") {
       verbose = 1;
-      i++;
     }
+    // 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");
@@ -406,12 +477,17 @@
   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
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 7ac03f1..5eafb2c 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -38,9 +38,17 @@
         // Don't warn about struct padding
         "-Wno-padded",
 
-        // android/sensors.h uses nested anonymous unions and anonymous structs
-        "-Wno-nested-anon-types",
-        "-Wno-gnu-anonymous-struct",
+        // 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",
     ],
@@ -58,8 +66,6 @@
     },
 
     srcs: [
-        "IGraphicBufferConsumer.cpp",
-        "IConsumerListener.cpp",
         "BitTube.cpp",
         "BufferItem.cpp",
         "BufferItemConsumer.cpp",
@@ -71,43 +77,57 @@
         "ConsumerBase.cpp",
         "CpuConsumer.cpp",
         "DisplayEventReceiver.cpp",
+        "FrameTimestamps.cpp",
         "GLConsumer.cpp",
-        "GraphicBufferAlloc.cpp",
-        "GraphicsEnv.cpp",
         "GuiConfig.cpp",
         "IDisplayEventConnection.cpp",
-        "IGraphicBufferAlloc.cpp",
+        "IConsumerListener.cpp",
+        "IGraphicBufferConsumer.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",
+        "view/Surface.cpp",
+        "bufferqueue/1.0/B2HProducerListener.cpp",
+        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp"
     ],
 
     shared_libs: [
-        "libnativeloader",
+        "libsync",
         "libbinder",
         "libcutils",
         "libEGL",
         "libGLESv2",
-        "libsync",
         "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"],
+    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/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 1357a4a..69b5962 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -81,6 +81,9 @@
     addAligned(size, mIsDroppable);
     addAligned(size, mAcquireCalled);
     addAligned(size, mTransformToDisplayInverse);
+    addAligned(size, mAutoRefresh);
+    addAligned(size, mQueuedBuffer);
+    addAligned(size, mIsStale);
     return size;
 }
 
@@ -88,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);
@@ -166,6 +169,9 @@
     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;
 }
@@ -198,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);
@@ -227,6 +235,9 @@
     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 ee4c58c..cd8e696 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>(
@@ -674,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) {
@@ -715,9 +717,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,
@@ -733,20 +736,22 @@
     return NO_ERROR;
 }
 
-void BufferQueueConsumer::dumpState(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",
                 static_cast<int32_t>(uid), NULL, 0);
-    } else {
-        mCore->dumpState(result, prefix);
+        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 6e6cce2..cd94253 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -29,12 +29,11 @@
 #include <inttypes.h>
 
 #include <cutils/properties.h>
+#include <cutils/atomic.h>
 
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueCore.h>
-#include <gui/GraphicBufferAlloc.h>
 #include <gui/IConsumerListener.h>
-#include <gui/IGraphicBufferAlloc.h>
 #include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <private/gui/ComposerService.h>
@@ -53,8 +52,7 @@
     return id | counter++;
 }
 
-BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
-    mAllocator(allocator),
+BufferQueueCore::BufferQueueCore() :
     mMutex(),
     mIsAbandoned(false),
     mConsumerControlledByApp(false),
@@ -96,30 +94,6 @@
     mLastQueuedSlot(INVALID_BUFFER_SLOT),
     mUniqueId(getUniqueId())
 {
-    if (allocator == NULL) {
-
-#ifdef HAVE_NO_SURFACE_FLINGER
-        // Without a SurfaceFlinger, allocate in-process.  This only makes
-        // sense in systems with static SELinux configurations and no
-        // applications (since applications need dynamic SELinux policy).
-        mAllocator = new GraphicBufferAlloc();
-#else
-        // Run time check for headless, where we also allocate in-process.
-        char value[PROPERTY_VALUE_MAX];
-        property_get("config.headless", value, "0");
-        if (atoi(value) == 1) {
-            mAllocator = new GraphicBufferAlloc();
-        } else {
-            sp<ISurfaceComposer> composer(ComposerService::getComposerService());
-            mAllocator = composer->createGraphicBufferAlloc();
-        }
-#endif  // HAVE_NO_SURFACE_FLINGER
-
-        if (mAllocator == NULL) {
-            BQ_LOGE("createGraphicBufferAlloc failed");
-        }
-    }
-
     int numStartingBuffers = getMaxBufferCountLocked();
     for (int s = 0; s < numStartingBuffers; s++) {
         mFreeSlots.insert(s);
@@ -132,7 +106,7 @@
 
 BufferQueueCore::~BufferQueueCore() {}
 
-void BufferQueueCore::dumpState(String8& result, const char* prefix) const {
+void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
     Mutex::Autolock lock(mMutex);
 
     String8 fifo;
@@ -147,10 +121,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(),
@@ -160,28 +134,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());
     }
 }
@@ -260,6 +234,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 f0e701e..0f7465b 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,11 +501,13 @@
     } // 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);
 
@@ -552,6 +561,8 @@
             mSlots[*outSlot].mFrameNumber,
             mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
 
+    addAndGetFrameTimestamps(nullptr, outTimestamps);
+
     return returnFlags;
 }
 
@@ -733,23 +744,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:
@@ -764,6 +779,7 @@
     sp<IConsumerListener> frameAvailableListener;
     sp<IConsumerListener> frameReplacedListener;
     int callbackTicket = 0;
+    uint64_t currentFrameNumber = 0;
     BufferItem item;
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
@@ -802,8 +818,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);
@@ -821,11 +838,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;
@@ -835,12 +855,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);
@@ -859,6 +880,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
@@ -885,6 +907,7 @@
                     if (!mSlots[last.mSlot].mBufferState.isShared()) {
                         mCore->mActiveBuffers.erase(last.mSlot);
                         mCore->mFreeBuffers.push_back(last.mSlot);
+                        output->bufferReplaced = true;
                     }
                 }
 
@@ -901,10 +924,11 @@
         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(),
                 static_cast<int32_t>(mCore->mQueue.size()));
@@ -916,14 +940,23 @@
         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
     // lock held so we can ensure that callbacks occur in order
-    {
+
+    int connectedApi;
+    sp<Fence> lastQueuedFence;
+
+    { // scope for the lock
         Mutex::Autolock lock(mCallbackMutex);
         while (callbackTicket != mCurrentCallbackTicket) {
             mCallbackCondition.wait(mCallbackMutex);
@@ -935,20 +968,35 @@
             frameReplacedListener->onFrameReplaced(item);
         }
 
+        connectedApi = mCore->mConnectedApi;
+        lastQueuedFence = std::move(mLastQueueBufferFence);
+
+        mLastQueueBufferFence = std::move(acquireFence);
+        mLastQueuedCrop = item.mCrop;
+        mLastQueuedTransform = item.mTransform;
+
         ++mCurrentCallbackTicket;
         mCallbackCondition.broadcast();
     }
 
     // Wait without lock held
-    if (mCore->mConnectedApi == NATIVE_WINDOW_API_EGL) {
+    if (connectedApi == NATIVE_WINDOW_API_EGL) {
         // Waiting here allows for two full buffers to be queued but not a
         // third. In the event that frames take varying time, this makes a
         // small trade-off in favor of latency rather than throughput.
-        mLastQueueBufferFence->waitForever("Throttling EGL Production");
+        lastQueuedFence->waitForever("Throttling EGL Production");
     }
-    mLastQueueBufferFence = fence;
-    mLastQueuedCrop = item.mCrop;
-    mLastQueuedTransform = item.mTransform;
+
+    // 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;
 }
@@ -1032,6 +1080,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;
@@ -1110,10 +1162,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
@@ -1175,6 +1231,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.
@@ -1209,7 +1268,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;
@@ -1225,6 +1287,7 @@
     // Call back without lock held
     if (listener != NULL) {
         listener->onBuffersReleased();
+        listener->onDisconnect();
     }
 
     return status;
@@ -1278,10 +1341,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);
@@ -1432,20 +1497,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 3cf3078..5c6158c 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());
 
@@ -267,7 +269,9 @@
     result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
 
     if (!mAbandoned) {
-        mConsumer->dumpState(result, prefix);
+        String8 consumerState;
+        mConsumer->dumpState(String8(prefix), &consumerState);
+        result.append(consumerState);
     }
 }
 
@@ -284,6 +288,9 @@
     }
 
     if (item->mGraphicBuffer != NULL) {
+        if (mSlots[item->mSlot].mGraphicBuffer != NULL) {
+            freeBufferLocked(item->mSlot);
+        }
         mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer;
     }
 
@@ -317,16 +324,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 +373,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..1507d51 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -18,13 +18,14 @@
 
 #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 {
@@ -36,7 +37,8 @@
     if (sf != NULL) {
         mEventConnection = sf->createDisplayEventConnection();
         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 a8f40e0..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 <log/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 ff7b83a..85ac304 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -14,147 +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:
     explicit BpConsumerListener(const sp<IBinder>& impl)
-        : BpInterface<IConsumerListener>(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);
+    }
 }
 
-ConsumerListener::~ConsumerListener() = default;
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index b1d3b00..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:
     explicit BpDisplayEventConnection(const sp<IBinder>& impl)
-        : BpInterface<IDisplayEventConnection>(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 2fb380c..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:
-    explicit 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:
-        explicit 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 2401464..568c318 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,
@@ -53,439 +50,173 @@
     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:
     explicit BpGraphicBufferConsumer(const sp<IBinder>& impl)
-        : BpInterface<IGraphicBufferConsumer>(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 setTransformHint(uint32_t hint) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setTransformHint);
+        return callRemote<Signature>(Tag::SET_TRANSFORM_HINT, hint);
     }
 
-    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 getSidebandStream(sp<NativeHandle>* outStream) const override {
+        using Signature = decltype(&IGraphicBufferConsumer::getSidebandStream);
+        return callRemote<Signature>(Tag::GET_SIDEBAND_STREAM, outStream);
     }
 
-    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 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 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 discardFreeBuffers() override {
+        return callRemote<decltype(&IGraphicBufferConsumer::discardFreeBuffers)>(
+                Tag::DISCARD_FREE_BUFFERS);
     }
 
-    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;
-    }
-
-    virtual void dumpState(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)->dumpState(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_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 846c205..74117c8 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>
 
@@ -29,9 +30,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,
@@ -118,24 +124,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;
             }
         }
@@ -211,14 +228,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;
     }
@@ -265,7 +289,7 @@
         if (result != NO_ERROR) {
             return result;
         }
-        memcpy(output, reply.readInplace(sizeof(*output)), sizeof(*output));
+        reply.read(*output);
         result = reply.readInt32();
         return result;
     }
@@ -422,40 +446,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 {
@@ -482,7 +490,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");
 
 // ----------------------------------------------------------------------
 
@@ -522,14 +646,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;
@@ -573,14 +701,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: {
@@ -611,11 +739,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;
         }
@@ -718,26 +844,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: {
@@ -764,16 +878,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 {
@@ -786,6 +905,7 @@
     if (size < getFlattenedSize()) {
         return NO_MEMORY;
     }
+
     FlattenableUtils::write(buffer, size, timestamp);
     FlattenableUtils::write(buffer, size, isAutoTimestamp);
     FlattenableUtils::write(buffer, size, dataSpace);
@@ -793,6 +913,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;
@@ -803,16 +925,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;
     }
 
@@ -823,6 +936,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);
@@ -832,4 +946,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/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp
deleted file mode 100644
index 59ecee7..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:
-    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();
-    }
-};
-
-// 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 07c507a..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:
-    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();
-    }
-};
-
-// 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 0a8e6a5..2516fb8 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,8 +43,6 @@
 
 namespace android {
 
-class IDisplayEventConnection;
-
 class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
 {
 public:
@@ -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,6 +157,50 @@
         return result != 0;
     }
 
+    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()
     {
         Parcel data, reply;
@@ -379,10 +422,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 +488,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 +545,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,6 +565,25 @@
             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());
@@ -631,10 +737,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 47cb047..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:
     explicit BpSurfaceComposerClient(const sp<IBinder>& impl)
-        : BpInterface<ISurfaceComposerClient>(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 2c87562..0000000
--- a/libs/gui/Sensor.cpp
+++ /dev/null
@@ -1,549 +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 <inttypes.h>
-#include <stdint.h>
-#include <sys/limits.h>
-#include <sys/types.h>
-
-#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>
-
-// ----------------------------------------------------------------------------
-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
-        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;
-    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 57c3073..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:
-            explicit 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 d1a9cbb..a6d9e66 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,6 +472,7 @@
     uint32_t reqHeight;
     PixelFormat reqFormat;
     uint32_t reqUsage;
+    bool enableFrameTimestamps;
 
     {
         Mutex::Autolock lock(mMutex);
@@ -281,6 +483,8 @@
         reqFormat = mReqFormat;
         reqUsage = mReqUsage;
 
+        enableFrameTimestamps = mEnableFrameTimestamps;
+
         if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                 BufferItem::INVALID_BUFFER_SLOT) {
             sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -295,8 +499,11 @@
     int buf = -1;
     sp<Fence> fence;
     nsecs_t now = systemTime();
+
+    FrameEventHistoryDelta frameTimestamps;
     status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
-            reqWidth, reqHeight, reqFormat, reqUsage);
+            reqWidth, reqHeight, reqFormat, reqUsage,
+            enableFrameTimestamps ? &frameTimestamps : nullptr);
     mLastDequeueDuration = systemTime() - now;
 
     if (result < 0) {
@@ -317,7 +524,15 @@
         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.clear();
+            mRemovedBuffers.push_back(gbuf);
+        }
         result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
         if (result != NO_ERROR) {
             ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result);
@@ -435,7 +650,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);
@@ -507,17 +722,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
@@ -533,6 +762,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");
@@ -546,9 +798,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;
@@ -595,6 +846,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);
@@ -670,9 +930,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;
@@ -793,18 +1071,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) {
@@ -813,23 +1130,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;
@@ -848,6 +1170,7 @@
     ATRACE_CALL();
     ALOGV("Surface::disconnect");
     Mutex::Autolock lock(mMutex);
+    mRemovedBuffers.clear();
     mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
     mSharedBufferHasBeenQueued = false;
     freeAllBuffers();
@@ -895,9 +1218,16 @@
         *outFence = Fence::NO_FENCE;
     }
 
+    if (mReportRemovedBuffers) {
+        mRemovedBuffers.clear();
+    }
+
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         if (mSlots[i].buffer != NULL &&
                 mSlots[i].buffer->handle == buffer->handle) {
+            if (mReportRemovedBuffers) {
+                mRemovedBuffers.push_back(mSlots[i].buffer);
+            }
             mSlots[i].buffer = NULL;
         }
     }
@@ -923,6 +1253,10 @@
         graphicBuffer->mGenerationNumber = priorGeneration;
         return result;
     }
+    if (mReportRemovedBuffers && (mSlots[attachedSlot].buffer != nullptr)) {
+        mRemovedBuffers.clear();
+        mRemovedBuffers.push_back(mSlots[attachedSlot].buffer);
+    }
     mSlots[attachedSlot].buffer = graphicBuffer;
 
     return NO_ERROR;
@@ -1356,70 +1690,16 @@
     return mGraphicBufferProducer->getUniqueId(outId);
 }
 
-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;
+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 43506e9..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>
@@ -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..bf8a815 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) {
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..eafd296
--- /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->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
index 4779003..778d684 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -9,14 +9,13 @@
     clang: true,
 
     srcs: [
+        "BufferItemConsumer_test.cpp",
         "BufferQueue_test.cpp",
         "CpuConsumer_test.cpp",
         "FillBuffer.cpp",
         "GLTest.cpp",
         "IGraphicBufferProducer_test.cpp",
         "MultiTextureConsumer_test.cpp",
-        "Sensor_test.cpp",
-        "SRGB_test.cpp",
         "StreamSplitter_test.cpp",
         "SurfaceTextureClient_test.cpp",
         "SurfaceTextureFBO_test.cpp",
@@ -29,6 +28,8 @@
     ],
 
     shared_libs: [
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore-utils",
         "liblog",
         "libEGL",
         "libGLESv1_CM",
@@ -36,8 +37,10 @@
         "libbinder",
         "libcutils",
         "libgui",
-        "libsync",
+        "libhidlbase",
+        "libhidltransport",
         "libui",
         "libutils",
+        "libnativewindow"
     ],
 }
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 65df7dc..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->dumpState(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 9c2e838..5848c74 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -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/SRGB_test.cpp b/libs/gui/tests/SRGB_test.cpp
deleted file mode 100644
index c2640cd..0000000
--- a/libs/gui/tests/SRGB_test.cpp
+++ /dev/null
@@ -1,486 +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
-    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/Sensor_test.cpp b/libs/gui/tests/Sensor_test.cpp
deleted file mode 100644
index fbf282d..0000000
--- a/libs/gui/tests/Sensor_test.cpp
+++ /dev/null
@@ -1,88 +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.
- */
-
-#define LOG_TAG "Sensor_test"
-
-#include <gui/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/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 b10d4eb..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>
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 308bd7d..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();
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0de60c9..08d6715 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -18,19 +18,37 @@
 
 #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/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 +60,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 +97,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 +118,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 +163,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 +263,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 +320,1180 @@
     ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
 }
 
+
+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() 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);
+}
+
 }
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..5d7f660
--- /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_available: 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/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
index 9485d5b..9294419 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -17,7 +17,11 @@
 cc_library {
     name: "libinput",
     host_supported: true,
-
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
     srcs: [
         "Input.cpp",
         "InputDevice.cpp",
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/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/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..e2647be
--- /dev/null
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -0,0 +1,456 @@
+/*
+ * 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 <hardware/gralloc1.h>
+#include <grallocusage/GrallocUsageConversion.h>
+
+#include <private/android/AHardwareBufferHelpers.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;
+
+    int format = AHardwareBuffer_convertToPixelFormat(desc->format);
+    if (format == 0) {
+        ALOGE("Invalid pixel format %u", desc->format);
+        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 producerUsage = 0;
+    uint64_t consumerUsage = 0;
+    AHardwareBuffer_convertToGrallocUsageBits(&producerUsage, &consumerUsage, desc->usage0,
+            desc->usage1);
+    uint32_t usage = android_convertGralloc1To0Usage(producerUsage, consumerUsage);
+
+    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();
+
+    uint64_t producerUsage = 0;
+    uint64_t consumerUsage = 0;
+    android_convertGralloc0To1Usage(gbuffer->getUsage(), &producerUsage, &consumerUsage);
+    AHardwareBuffer_convertFromGrallocUsageBits(&outDesc->usage0, &outDesc->usage1,
+            producerUsage, consumerUsage);
+    outDesc->format = AHardwareBuffer_convertFromPixelFormat(
+            static_cast<uint32_t>(gbuffer->getPixelFormat()));
+}
+
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage0,
+        int32_t fence, const ARect* rect, void** outVirtualAddress) {
+    if (!buffer) return BAD_VALUE;
+
+    if (usage0 & ~(AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN |
+                   AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN)) {
+        ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+                " AHARDWAREBUFFER_USAGE0_CPU_* flags are allowed");
+        return BAD_VALUE;
+    }
+
+    uint64_t producerUsage = 0;
+    uint64_t consumerUsage = 0;
+    AHardwareBuffer_convertToGrallocUsageBits(&producerUsage, &consumerUsage, usage0, 0);
+    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(producerUsage, consumerUsage, bounds,
+            outVirtualAddress, fence);
+}
+
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
+    if (!buffer) return BAD_VALUE;
+
+    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    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 = sendmsg(socketFd, &msg, 0);
+    if (result <= 0) {
+        ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)",
+                result, strerror(errno));
+        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 = recvmsg(socketFd, &msg, 0);
+    if (result <= 0) {
+        ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)",
+                result, strerror(errno));
+        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 constexpr UsageMaskMapping kUsage0ProducerMapping[] = {
+    { AHARDWAREBUFFER_USAGE0_CPU_WRITE, GRALLOC1_PRODUCER_USAGE_CPU_WRITE },
+    { AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN, GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN },
+    { AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT, GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET },
+    { AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT, GRALLOC1_PRODUCER_USAGE_PROTECTED },
+    { AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA, GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA },
+};
+
+static constexpr UsageMaskMapping kUsage1ProducerMapping[] = {
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_0, GRALLOC1_PRODUCER_USAGE_PRIVATE_0 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_1, GRALLOC1_PRODUCER_USAGE_PRIVATE_1 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_2, GRALLOC1_PRODUCER_USAGE_PRIVATE_2 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_3, GRALLOC1_PRODUCER_USAGE_PRIVATE_3 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_4, GRALLOC1_PRODUCER_USAGE_PRIVATE_4 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_5, GRALLOC1_PRODUCER_USAGE_PRIVATE_5 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_6, GRALLOC1_PRODUCER_USAGE_PRIVATE_6 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_7, GRALLOC1_PRODUCER_USAGE_PRIVATE_7 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_8, GRALLOC1_PRODUCER_USAGE_PRIVATE_8 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_9, GRALLOC1_PRODUCER_USAGE_PRIVATE_9 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_10, GRALLOC1_PRODUCER_USAGE_PRIVATE_10 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_11, GRALLOC1_PRODUCER_USAGE_PRIVATE_11 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_12, GRALLOC1_PRODUCER_USAGE_PRIVATE_12 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_13, GRALLOC1_PRODUCER_USAGE_PRIVATE_13 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_14, GRALLOC1_PRODUCER_USAGE_PRIVATE_14 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_15, GRALLOC1_PRODUCER_USAGE_PRIVATE_15 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_16, GRALLOC1_PRODUCER_USAGE_PRIVATE_16 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_17, GRALLOC1_PRODUCER_USAGE_PRIVATE_17 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_18, GRALLOC1_PRODUCER_USAGE_PRIVATE_18 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_19, GRALLOC1_PRODUCER_USAGE_PRIVATE_19 },
+};
+
+static constexpr UsageMaskMapping kUsage0ConsumerMapping[] = {
+    { AHARDWAREBUFFER_USAGE0_CPU_READ, GRALLOC1_CONSUMER_USAGE_CPU_READ },
+    { AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN, GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN },
+    { AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE, GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE },
+    { AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER, GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER },
+    { AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE, GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER },
+};
+
+static constexpr UsageMaskMapping kUsage1ConsumerMapping[] = {
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_0, GRALLOC1_CONSUMER_USAGE_PRIVATE_0 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_1, GRALLOC1_CONSUMER_USAGE_PRIVATE_1 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_2, GRALLOC1_CONSUMER_USAGE_PRIVATE_2 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_3, GRALLOC1_CONSUMER_USAGE_PRIVATE_3 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_4, GRALLOC1_CONSUMER_USAGE_PRIVATE_4 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_5, GRALLOC1_CONSUMER_USAGE_PRIVATE_5 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_6, GRALLOC1_CONSUMER_USAGE_PRIVATE_6 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_7, GRALLOC1_CONSUMER_USAGE_PRIVATE_7 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_8, GRALLOC1_CONSUMER_USAGE_PRIVATE_8 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_9, GRALLOC1_CONSUMER_USAGE_PRIVATE_9 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_10, GRALLOC1_CONSUMER_USAGE_PRIVATE_10 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_11, GRALLOC1_CONSUMER_USAGE_PRIVATE_11 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_12, GRALLOC1_CONSUMER_USAGE_PRIVATE_12 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_13, GRALLOC1_CONSUMER_USAGE_PRIVATE_13 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_14, GRALLOC1_CONSUMER_USAGE_PRIVATE_14 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_15, GRALLOC1_CONSUMER_USAGE_PRIVATE_15 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_16, GRALLOC1_CONSUMER_USAGE_PRIVATE_16 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_17, GRALLOC1_CONSUMER_USAGE_PRIVATE_17 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_18, GRALLOC1_CONSUMER_USAGE_PRIVATE_18 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_19, GRALLOC1_CONSUMER_USAGE_PRIVATE_19 },
+};
+
+static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
+    return (mask & bitsToCheck) == bitsToCheck && bitsToCheck;
+}
+
+uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format) {
+    switch (format) {
+        case HAL_PIXEL_FORMAT_RGBA_8888:    return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+        case HAL_PIXEL_FORMAT_RGBX_8888:    return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
+        case HAL_PIXEL_FORMAT_RGB_565:      return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
+        case HAL_PIXEL_FORMAT_RGB_888:      return AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM;
+        case HAL_PIXEL_FORMAT_RGBA_FP16:    return AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
+        case HAL_PIXEL_FORMAT_RGBA_1010102: return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
+        case HAL_PIXEL_FORMAT_BLOB:         return AHARDWAREBUFFER_FORMAT_BLOB;
+        default:ALOGE("Unknown pixel format %u", format);
+            return 0;
+    }
+}
+
+uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format) {
+    switch (format) {
+        case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:         return HAL_PIXEL_FORMAT_RGBA_8888;
+        case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:         return HAL_PIXEL_FORMAT_RGBX_8888;
+        case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:           return HAL_PIXEL_FORMAT_RGB_565;
+        case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:           return HAL_PIXEL_FORMAT_RGB_888;
+        case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:     return HAL_PIXEL_FORMAT_RGBA_FP16;
+        case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:      return HAL_PIXEL_FORMAT_RGBA_1010102;
+        case AHARDWAREBUFFER_FORMAT_BLOB:                   return HAL_PIXEL_FORMAT_BLOB;
+        default:ALOGE("Unknown AHardwareBuffer format %u", format);
+            return 0;
+    }
+}
+
+void AHardwareBuffer_convertToGrallocUsageBits(uint64_t* outProducerUsage,
+    uint64_t* outConsumerUsage, uint64_t usage0, uint64_t usage1) {
+    *outProducerUsage = 0;
+    *outConsumerUsage = 0;
+    for (const UsageMaskMapping& mapping : kUsage0ProducerMapping) {
+        if (containsBits(usage0, mapping.hardwareBufferMask)) {
+            *outProducerUsage |= mapping.grallocMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage1ProducerMapping) {
+        if (containsBits(usage1, mapping.hardwareBufferMask)) {
+            *outProducerUsage |= mapping.grallocMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage0ConsumerMapping) {
+        if (containsBits(usage0, mapping.hardwareBufferMask)) {
+            *outConsumerUsage |= mapping.grallocMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage1ConsumerMapping) {
+        if (containsBits(usage1, mapping.hardwareBufferMask)) {
+            *outConsumerUsage |= mapping.grallocMask;
+        }
+    }
+}
+
+void AHardwareBuffer_convertFromGrallocUsageBits(uint64_t* outUsage0, uint64_t* outUsage1,
+        uint64_t producerUsage, uint64_t consumerUsage) {
+    *outUsage0 = 0;
+    *outUsage1 = 0;
+    for (const UsageMaskMapping& mapping : kUsage0ProducerMapping) {
+        if (containsBits(producerUsage, mapping.grallocMask)) {
+            *outUsage0 |= mapping.hardwareBufferMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage1ProducerMapping) {
+        if (containsBits(producerUsage, mapping.grallocMask)) {
+            *outUsage1 |= mapping.hardwareBufferMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage0ConsumerMapping) {
+        if (containsBits(consumerUsage, mapping.grallocMask)) {
+            *outUsage0 |= mapping.hardwareBufferMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage1ConsumerMapping) {
+        if (containsBits(consumerUsage, mapping.grallocMask)) {
+            *outUsage1 |= mapping.hardwareBufferMask;
+        }
+    }
+}
+
+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..6c67cf8
--- /dev/null
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 usage0, uint64_t usage1) {
+    uint64_t pUsage, cUsage;
+    AHardwareBuffer_convertToGrallocUsageBits(&pUsage, &cUsage, usage0, usage1);
+    return native_window_set_usage(window, android_convertGralloc1To0Usage(pUsage, cUsage));
+}
+
+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..6c8221d
--- /dev/null
+++ b/libs/nativewindow/Android.bp
@@ -0,0 +1,65 @@
+// 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",
+    ],
+
+    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..f5657ff
--- /dev/null
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -0,0 +1,257 @@
+/*
+ * 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 sometimes be read by the CPU */
+    AHARDWAREBUFFER_USAGE0_CPU_READ               = 1ULL << 1,
+    /* The buffer will often be read by the CPU*/
+    AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN         = 1ULL << 2 | AHARDWAREBUFFER_USAGE0_CPU_READ,
+    /* The buffer will sometimes be written to by the CPU */
+    AHARDWAREBUFFER_USAGE0_CPU_WRITE              = 1ULL << 5,
+    /* The buffer will often be written to by the CPU */
+    AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN        = 1ULL << 6 | AHARDWAREBUFFER_USAGE0_CPU_WRITE,
+    /* The buffer will be read from by the GPU */
+    AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE      = 1ULL << 10,
+    /* The buffer will be written to by the GPU */
+    AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT       = 1ULL << 11,
+    /* The buffer will be used as a shader storage or uniform buffer object*/
+    AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER        = 1ULL << 14,
+    /* The buffer must not be used outside of a protected hardware path */
+    AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT      = 1ULL << 18,
+    /** The buffer will be used for sensor direct data */
+    AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA     = 1ULL << 29,
+    /* The buffer will be read by a hardware video encoder */
+    AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE           = 1ULL << 21,
+};
+
+/* These flags are intended only for use by device-specific graphics drivers. */
+enum {
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_19 = 1ULL << 24,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_19 = 1ULL << 25,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_18 = 1ULL << 26,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_18 = 1ULL << 27,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_17 = 1ULL << 28,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_17 = 1ULL << 29,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_16 = 1ULL << 30,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_16 = 1ULL << 31,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_15 = 1ULL << 32,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_15 = 1ULL << 33,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_14 = 1ULL << 34,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_14 = 1ULL << 35,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_13 = 1ULL << 36,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_13 = 1ULL << 37,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_12 = 1ULL << 38,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_12 = 1ULL << 39,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_11 = 1ULL << 40,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_11 = 1ULL << 41,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_10 = 1ULL << 42,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_10 = 1ULL << 43,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_9 = 1ULL << 44,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_9 = 1ULL << 45,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_8 = 1ULL << 46,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_8 = 1ULL << 47,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_7 = 1ULL << 48,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_7 = 1ULL << 49,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_6 = 1ULL << 50,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_6 = 1ULL << 51,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_5 = 1ULL << 52,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_5 = 1ULL << 53,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_4 = 1ULL << 54,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_4 = 1ULL << 55,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_3 = 1ULL << 56,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_3 = 1ULL << 57,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_2 = 1ULL << 58,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_2 = 1ULL << 59,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_1 = 1ULL << 60,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_1 = 1ULL << 61,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_0 = 1ULL << 62,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_0 = 1ULL << 63,
+};
+
+typedef struct AHardwareBuffer_Desc {
+    uint32_t    width;
+    uint32_t    height;
+    uint32_t    layers;
+    uint32_t    format;     // One of AHARDWAREBUFFER_FORMAT_*
+    uint64_t    usage0;     // Combination of AHARDWAREBUFFER_USAGE0_*
+    uint64_t    usage1;     // 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_USAGE0_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 usage0
+ * flags are not a combination of AHARDWAREBUFFER_USAGE0_CPU_*, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage0,
+        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..a12bdd7
--- /dev/null
+++ b/libs/nativewindow/include/android/native_window.h
@@ -0,0 +1,190 @@
+/*
+ * 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 <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;
+/**
+ * {@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 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 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 (AHARDWAREBUFFER_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.
+ *
+ * format: one of AHARDWAREBUFFER_FORMAT_ constants
+ *
+ * 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);
+
+#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 if successful
+ * @return -EINVAL if @param 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..ee5da84
--- /dev/null
+++ b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
@@ -0,0 +1,55 @@
+/*
+ * 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 {
+
+uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
+
+uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
+
+void AHardwareBuffer_convertToGrallocUsageBits(uint64_t* outProducerUsage,
+    uint64_t* outConsumerUsage, uint64_t usage0, uint64_t usage1);
+
+void AHardwareBuffer_convertFromGrallocUsageBits(uint64_t* outUsage0, uint64_t* outUsage1,
+    uint64_t producerUsage, uint64_t consumerUsage);
+
+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..fb67a51
--- /dev/null
+++ b/libs/nativewindow/include/system/window.h
@@ -0,0 +1,966 @@
+/*
+ * 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,
+};
+
+/* 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..dc2dcbe
--- /dev/null
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -0,0 +1,31 @@
+/*
+ * 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);
+
+__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..067046b
--- /dev/null
+++ b/libs/nativewindow/include/vndk/window.h
@@ -0,0 +1,413 @@
+/*
+ * 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_USAGE0_* constant
+ *  privateUsage: one of AHARDWAREBUFFER_USAGE1_*_PRIVATE_* constant
+ *
+ *  By default (if this function is never called), a usage of
+ *      AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT
+ *  is assumed.
+ *
+ *  Calling this function will usually cause following buffers to be
+ *  reallocated.
+ */
+int ANativeWindow_setUsage(ANativeWindow* window, uint64_t usage0, uint64_t usage1);
+
+
+/*
+ * 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..1099043
--- /dev/null
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -0,0 +1,186 @@
+/*
+ * 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 <hardware/gralloc1.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+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 grallocProducerUsage, uint64_t grallocConsumerUsage,
+        uint64_t hardwareBufferUsage0, uint64_t hardwareBufferUsage1) {
+    uint64_t producerUsage = 0;
+    uint64_t consumerUsage = 0;
+    uint64_t usage0 = 0;
+    uint64_t usage1 = 0;
+
+    AHardwareBuffer_convertToGrallocUsageBits(
+            &producerUsage, &consumerUsage, hardwareBufferUsage0, hardwareBufferUsage1);
+    if (producerUsage != grallocProducerUsage)
+        return BuildHexFailureMessage(grallocProducerUsage, producerUsage,
+                "producer");
+    if (consumerUsage != grallocConsumerUsage)
+        return BuildHexFailureMessage(grallocConsumerUsage, consumerUsage,
+                "consumer");
+
+    AHardwareBuffer_convertFromGrallocUsageBits(
+            &usage0, &usage1, grallocProducerUsage, grallocConsumerUsage);
+    if (usage0 != hardwareBufferUsage0)
+        return BuildHexFailureMessage(hardwareBufferUsage0, usage0, "usage0");
+    if (usage1 != hardwareBufferUsage1)
+        return BuildHexFailureMessage(hardwareBufferUsage1, usage1, "usage1");
+
+    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(0, GRALLOC1_CONSUMER_USAGE_CPU_READ,
+            AHARDWAREBUFFER_USAGE0_CPU_READ, 0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN,
+            AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_CPU_WRITE, 0,
+            AHARDWAREBUFFER_USAGE0_CPU_WRITE, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN, 0,
+            AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN, 0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE,
+            AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET,
+            0, AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT, 0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER,
+            AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PROTECTED, 0,
+            AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA,
+            0, AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA, 0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
+            AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE, 0));
+
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_0, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_1, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_1));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_2, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_2));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_3, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_3));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_4, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_4));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_5, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_5));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_6, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_6));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_7, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_7));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_8, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_8));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_9, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_9));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_10, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_10));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_11, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_11));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_12, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_12));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_13, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_13));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_14, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_14));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_15, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_15));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_16, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_16));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_17, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_17));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_18, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_18));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_19, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_19));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_0,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_1,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_1));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_2,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_2));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_3,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_3));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_4,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_4));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_5,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_5));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_6,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_6));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_7,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_7));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_8,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_8));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_9,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_9));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_10,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_10));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_11,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_11));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_12,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_12));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_13,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_13));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_14,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_14));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_15,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_15));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_16,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_16));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_17,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_17));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_18,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_18));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_19,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_19));
+
+    // Test some more complex flag combinations.
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_CPU_WRITE,
+            GRALLOC1_CONSUMER_USAGE_CPU_READ,
+            AHARDWAREBUFFER_USAGE0_CPU_READ | AHARDWAREBUFFER_USAGE0_CPU_WRITE,
+            0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN, 0,
+            AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET,
+            GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE |
+                    GRALLOC1_CONSUMER_USAGE_PRIVATE_17,
+            AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT |
+                    AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE,
+            AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_17));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA,
+            GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER,
+            AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER |
+                    AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA, 0));
+}
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
new file mode 100644
index 0000000..6d78770
--- /dev/null
+++ b/libs/nativewindow/tests/Android.bp
@@ -0,0 +1,23 @@
+//
+// 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"],
+    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
index d5ff753..5ccf178 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -28,6 +28,9 @@
         // 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",
@@ -41,13 +44,16 @@
     },
 
     srcs: [
+        "ColorSpace.cpp",
+        "DebugUtils.cpp",
         "Fence.cpp",
+        "FenceTime.cpp",
         "FrameStats.cpp",
-        "Gralloc1.cpp",
-        "Gralloc1On0Adapter.cpp",
+        "Gralloc2.cpp",
         "GraphicBuffer.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicBufferMapper.cpp",
+        "GraphicsEnv.cpp",
         "HdrCapabilities.cpp",
         "PixelFormat.cpp",
         "Rect.cpp",
@@ -56,13 +62,31 @@
     ],
 
     shared_libs: [
-        "libbinder",
+        "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",
         "libsync",
         "libutils",
         "liblog",
     ],
+
+    static_libs: [
+        "libarect",
+        "libgrallocusage",
+        "libmath",
+    ],
+
+    export_static_lib_headers: [
+        "libarect",
+        "libmath",
+    ],
 }
 
 subdirs = ["tests"]
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 7cf8233..02d4137 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..f8d9401
--- /dev/null
+++ b/libs/ui/Gralloc2.cpp
@@ -0,0 +1,249 @@
+/*
+ * 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 <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;
+            });
+
+    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 75dd8df..eaba1ed 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -18,13 +18,19 @@
 #define LOG_TAG "GraphicBufferAllocator"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#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 {
 // ---------------------------------------------------------------------------
@@ -36,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() {}
 
@@ -54,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",
+            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.format, rec.usage,
-                    rec.requestorName.c_str());
+                    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.0);
     result.append(buffer);
-    std::string deviceDump = mDevice->dump();
+
+    std::string deviceDump = mAllocator->dumpDebugInfo();
     result.append(deviceDump.c_str(), deviceDump.size());
 }
 
@@ -81,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();
 
@@ -91,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);
@@ -139,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..1d20424
--- /dev/null
+++ b/libs/ui/GraphicsEnv.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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>
+
+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/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/tests/Android.bp b/libs/ui/tests/Android.bp
index 8cdab8c..6733505 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -21,11 +21,7 @@
 }
 
 cc_test {
-    name: "vec_test",
-    srcs: ["vec_test.cpp"],
-}
-
-cc_test {
-    name: "mat_test",
-    srcs: ["mat_test.cpp"],
+    name: "colorspace_test",
+    shared_libs: ["libui"],
+    srcs: ["colorspace_test.cpp"],
 }
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..68b9c81
--- /dev/null
+++ b/libs/vr/libbufferhub/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_client.cpp",
+    "buffer_hub_rpc.cpp",
+    "ion_buffer.cpp",
+]
+
+localIncludeFiles = [
+    "include",
+]
+
+staticLibraries = [
+    "libdvrcommon",
+    "libpdx_default_transport",
+    "libgrallocusage",
+]
+
+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..a1f952e
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -0,0 +1,454 @@
+#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 <private/dvr/platform_defines.h>
+
+#include "include/private/dvr/bufferhub_rpc.h"
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::WrapBuffer;
+using android::pdx::Status;
+
+namespace {
+
+// TODO(hendrikw): These flags can not be hard coded.
+constexpr int kUncachedBlobUsageFlags = GRALLOC_USAGE_SW_READ_RARELY |
+                                        GRALLOC_USAGE_SW_WRITE_RARELY |
+                                        GRALLOC_USAGE_PRIVATE_UNCACHED;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      id_(-1) {}
+BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+          endpoint_path)},
+      id_(-1) {}
+
+BufferHubBuffer::~BufferHubBuffer() {}
+
+Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
+  Status<LocalChannelHandle> status =
+      InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+  ALOGE_IF(!status,
+           "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+           status.GetErrorMessage().c_str());
+  return status;
+}
+
+int BufferHubBuffer::ImportBuffer() {
+  ATRACE_NAME("BufferHubBuffer::ImportBuffer");
+
+  Status<std::vector<NativeBufferHandle<LocalHandle>>> status =
+      InvokeRemoteMethod<BufferHubRPC::GetBuffers>();
+  if (!status) {
+    ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffers: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  } else if (status.get().empty()) {
+    ALOGE(
+        "BufferHubBuffer::ImportBuffer: Expected to receive at least one "
+        "buffer handle but got zero!");
+    return -EIO;
+  }
+
+  auto buffer_handles = status.take();
+
+  // Stash the buffer id to replace the value in id_. All sub-buffers of a
+  // buffer hub buffer have the same id.
+  const int new_id = buffer_handles[0].id();
+
+  // Import all of the buffers.
+  std::vector<IonBuffer> ion_buffers;
+  for (auto& handle : buffer_handles) {
+    const size_t i = &handle - buffer_handles.data();
+    ALOGD_IF(
+        TRACE,
+        "BufferHubBuffer::ImportBuffer: i=%zu id=%d FdCount=%zu IntCount=%zu",
+        i, handle.id(), handle.FdCount(), handle.IntCount());
+
+    IonBuffer buffer;
+    const int ret = handle.Import(&buffer);
+    if (ret < 0)
+      return ret;
+
+    ion_buffers.emplace_back(std::move(buffer));
+  }
+
+  // If all imports succeed, replace the previous buffers and id.
+  slices_ = std::move(ion_buffers);
+  id_ = new_id;
+  return 0;
+}
+
+int BufferHubBuffer::Poll(int timeout_ms) {
+  ATRACE_NAME("BufferHubBuffer::Poll");
+  pollfd p = {event_fd(), POLLIN, 0};
+  return poll(&p, 1, timeout_ms);
+}
+
+int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
+                          void** address, size_t index) {
+  return slices_[index].Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBuffer::Unlock(size_t index) { return slices_[index].Unlock(); }
+
+int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
+  int width = static_cast<int>(size);
+  int height = 1;
+  // TODO(hendrikw): These flags can not be hard coded.
+  constexpr int usage = GRALLOC_USAGE_SW_READ_RARELY |
+                        GRALLOC_USAGE_SW_WRITE_RARELY |
+                        GRALLOC_USAGE_PRIVATE_UNCACHED;
+  int ret = Lock(usage, 0, 0, width, height, addr);
+  if (ret == 0)
+    Unlock();
+  return ret;
+}
+
+int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
+  int width = static_cast<int>(size);
+  int height = 1;
+  constexpr int usage =
+      GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_PRIVATE_UNCACHED;
+  int ret = Lock(usage, 0, 0, width, height, addr);
+  if (ret == 0)
+    Unlock();
+  return ret;
+}
+
+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,
+                               size_t slice_count)
+    : BufferProducer(width, height, format, usage, usage, metadata_size,
+                     slice_count) {}
+
+BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+                               uint64_t producer_usage, uint64_t consumer_usage,
+                               size_t metadata_size, size_t slice_count)
+    : 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 slice_count=%zu",
+           event_fd(), width, height, format, producer_usage, consumer_usage,
+           metadata_size, slice_count);
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, producer_usage, consumer_usage, metadata_size,
+      slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
+        status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, uint32_t width, uint32_t height,
+                               uint32_t format, uint32_t usage,
+                               size_t meta_size_bytes, size_t slice_count)
+    : BufferProducer(name, user_id, group_id, width, height, format, usage,
+                     usage, meta_size_bytes, slice_count) {}
+
+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,
+                               size_t slice_count)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d "
+           "group_id=%d width=%u height=%u format=%u producer_usage=%" PRIx64
+           " consumer_usage=%" PRIx64 " meta_size_bytes=%zu slice_count=%zu",
+           event_fd(), name.c_str(), user_id, group_id, width, height, format,
+           producer_usage, consumer_usage, meta_size_bytes, slice_count);
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+      name, user_id, group_id, width, height, format, producer_usage,
+      consumer_usage, meta_size_bytes, slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create/get persistent "
+        "buffer \"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(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;
+  const size_t slice_count = 1;
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, producer_usage, consumer_usage, meta_size_bytes,
+      slice_count);
+  if (!status) {
+    ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, 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;
+  const size_t slice_count = 1;
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+      name, user_id, group_id, width, height, format, producer_usage,
+      consumer_usage, meta_size_bytes, slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create persistent "
+        "buffer \"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str());
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::GetPersistentBuffer>(name);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to get producer buffer by name "
+        "\"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
+                         size_t meta_size_bytes) {
+  ATRACE_NAME("BufferProducer::Post");
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+      BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
+}
+
+int BufferProducer::Gain(LocalHandle* release_fence) {
+  ATRACE_NAME("BufferProducer::Gain");
+  auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+  if (!status)
+    return -status.error();
+  if (release_fence)
+    *release_fence = status.take().take();
+  return 0;
+}
+
+int BufferProducer::GainAsync() {
+  ATRACE_NAME("BufferProducer::GainAsync");
+  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+    LocalChannelHandle channel) {
+  ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
+  return BufferProducer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferProducer::MakePersistent(const std::string& name, int user_id,
+                                   int group_id) {
+  ATRACE_NAME("BufferProducer::MakePersistent");
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ProducerMakePersistent>(name, user_id,
+                                                               group_id));
+}
+
+int BufferProducer::RemovePersistence() {
+  ATRACE_NAME("BufferProducer::RemovePersistence");
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ProducerRemovePersistence>());
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreateUncachedBlob(
+    size_t size) {
+  return BufferProducer::Create(kUncachedBlobUsageFlags, size);
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreatePersistentUncachedBlob(
+    const std::string& name, int user_id, int group_id, size_t size) {
+  return BufferProducer::Create(name, user_id, group_id,
+                                kUncachedBlobUsageFlags, size);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_rpc.cpp b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
new file mode 100644
index 0000000..9a67faa
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/bufferhub_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char BufferHubRPC::kClientPath[];
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
new file mode 100644
index 0000000..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..c772ed3
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -0,0 +1,344 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+#include <vector>
+
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubBuffer : public pdx::Client {
+ public:
+  using LocalHandle = pdx::LocalHandle;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  template <typename T>
+  using Status = pdx::Status<T>;
+
+  // Create a new consumer channel that is attached to the producer. Returns
+  // a file descriptor for the new channel or a negative error code.
+  Status<LocalChannelHandle> CreateConsumer();
+
+  // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
+  int Poll(int timeout_ms);
+
+  // Locks the area specified by (x, y, width, height) for a specific usage. If
+  // the usage is software then |addr| will be updated to point to the address
+  // of the buffer in virtual memory. The caller should only access/modify the
+  // pixels in the specified area. anything else is undefined behavior.
+  int Lock(int usage, int x, int y, int width, int height, void** addr,
+           size_t index);
+
+  // Must be called after Lock() when the caller has finished changing the
+  // buffer.
+  int Unlock(size_t index);
+
+  // Helper for when index is 0.
+  int Lock(int usage, int x, int y, int width, int height, void** addr) {
+    return Lock(usage, x, y, width, height, addr, 0);
+  }
+
+  // Helper for when index is 0.
+  int Unlock() { return Unlock(0); }
+
+  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadWritePointer(size_t size, void** addr);
+
+  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadOnlyPointer(size_t size, void** addr);
+
+  // Returns a dup'd file descriptor for accessing the blob shared memory. The
+  // caller takes ownership of the file descriptor and must close it or pass on
+  // ownership. Some GPU API extensions can take file descriptors to bind shared
+  // memory gralloc buffers to GPU buffer objects.
+  LocalHandle GetBlobFd() const {
+    // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+    // vendors and this is the wrong fd, late-latching and EDS will very clearly
+    // stop working and we will need to correct this. The alternative is to use
+    // a GL context in the pose service to allocate this buffer or to use the
+    // ION API directly instead of gralloc.
+    return LocalHandle(dup(native_handle()->data[0]));
+  }
+
+  // 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*>(slices_[0].handle());
+  }
+  // If index is greater than or equal to slice_count(), the result is
+  // undefined.
+  native_handle_t* native_handle(size_t index) const {
+    return const_cast<native_handle_t*>(slices_[index].handle());
+  }
+
+  IonBuffer* buffer() { return &slices_[0]; }
+  const IonBuffer* buffer() const { return &slices_[0]; }
+
+  // If index is greater than or equal to slice_count(), the result is
+  // undefined.
+  IonBuffer* slice(size_t index) { return &slices_[index]; }
+  const IonBuffer* slice(size_t index) const { return &slices_[index]; }
+
+  int slice_count() const { return static_cast<int>(slices_.size()); }
+  int id() const { return id_; }
+
+  // The following methods return settings of the first buffer. Currently,
+  // it is only possible to create multi-buffer BufferHubBuffers with the same
+  // settings.
+  uint32_t width() const { return slices_[0].width(); }
+  uint32_t height() const { return slices_[0].height(); }
+  uint32_t stride() const { return slices_[0].stride(); }
+  uint32_t format() const { return slices_[0].format(); }
+  uint32_t usage() const { return slices_[0].usage(); }
+  uint32_t layer_count() const { return slices_[0].layer_count(); }
+  uint64_t producer_usage() const { return slices_[0].producer_usage(); }
+  uint64_t consumer_usage() const { return slices_[0].consumer_usage(); }
+
+ protected:
+  explicit BufferHubBuffer(LocalChannelHandle channel);
+  explicit BufferHubBuffer(const std::string& endpoint_path);
+  virtual ~BufferHubBuffer();
+
+  // Initialization helper.
+  int ImportBuffer();
+
+ private:
+  BufferHubBuffer(const BufferHubBuffer&) = delete;
+  void operator=(const BufferHubBuffer&) = delete;
+
+  // Global id for the buffer that is consistent across processes. It is meant
+  // for logging and debugging purposes only and should not be used for lookup
+  // or any other functional purpose as a security precaution.
+  int id_;
+
+  // A BufferHubBuffer may contain multiple slices of IonBuffers with same
+  // configurations.
+  std::vector<IonBuffer> slices_;
+};
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of BufferProducer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (BufferConsumers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
+ public:
+  // Create a buffer designed to hold arbitrary bytes that can be read and
+  // written from CPU, GPU and DSP. The buffer is mapped uncached so that CPU
+  // reads and writes are predictable.
+  static std::unique_ptr<BufferProducer> CreateUncachedBlob(size_t size);
+
+  // Creates a persistent uncached buffer with the given name and access.
+  static std::unique_ptr<BufferProducer> CreatePersistentUncachedBlob(
+      const std::string& name, int user_id, int group_id, size_t size);
+
+  // Imports a bufferhub producer channel, assuming ownership of its handle.
+  static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<BufferProducer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+  // |meta| are passed unaltered to the consumers. The producer must not modify
+  // the buffer until it is re-gained.
+  // This returns zero or a negative unix error code.
+  int Post(const LocalHandle& ready_fence, const void* meta,
+           size_t meta_size_bytes);
+
+  template <typename Meta,
+            typename = typename std::enable_if<std::is_void<Meta>::value>::type>
+  int Post(const LocalHandle& ready_fence) {
+    return Post(ready_fence, nullptr, 0);
+  }
+  template <typename Meta, typename = typename std::enable_if<
+                               !std::is_void<Meta>::value>::type>
+  int Post(const LocalHandle& ready_fence, const Meta& meta) {
+    return Post(ready_fence, &meta, sizeof(meta));
+  }
+
+  // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+  // must be waited on before using the buffer. If it is not valid then the
+  // buffer is free for immediate use. This call will only succeed if the buffer
+  // is in the released state.
+  // This returns zero or a negative unix error code.
+  int Gain(LocalHandle* release_fence);
+
+  // Asynchronously marks a released buffer as gained. This method is similar to
+  // the synchronous version above, except that it does not wait for BufferHub
+  // to acknowledge success or failure, nor does it transfer a release fence to
+  // the client. This version may be used in situations where a release fence is
+  // not needed. Because of the asynchronous nature of the underlying message,
+  // no error is returned if this method is called when the buffer is in an
+  // incorrect state. Returns zero if sending the message succeeded, or a
+  // negative errno code otherwise.
+  int GainAsync();
+
+  // Attaches the producer to |name| so that it becomes a persistent buffer that
+  // may be retrieved by name at a later time. This may be used in cases where a
+  // shared memory buffer should persist across the life of the producer process
+  // (i.e. the buffer may be held by clients across a service restart). The
+  // buffer may be associated with a user and/or group id to restrict access to
+  // the buffer. If user_id or group_id is -1 then checks for the respective id
+  // are disabled. If user_id or group_id is 0 then the respective id of the
+  // calling process is used instead.
+  int MakePersistent(const std::string& name, int user_id, int group_id);
+
+  // Removes the persistence of the producer.
+  int RemovePersistence();
+
+ private:
+  friend BASE;
+
+  // Constructors are automatically exposed through BufferProducer::Create(...)
+  // static template methods inherited from ClientBase, which take the same
+  // arguments as the constructors.
+
+  // Constructs a buffer with the given geometry and parameters.
+  BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+                 uint32_t usage, size_t metadata_size = 0,
+                 size_t slice_count = 1);
+  BufferProducer(uint32_t width, uint32_t height, uint32_t format,
+                 uint64_t producer_usage, uint64_t consumer_usage,
+                 size_t metadata_size, size_t slice_count);
+
+  // 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,
+                 size_t slice_count = 1);
+  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 metadata_size, size_t slice_count);
+
+  // 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..b6302f1
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -0,0 +1,254 @@
+#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
+#define ANDROID_DVR_BUFFERHUB_RPC_H_
+
+#include <cutils/native_handle.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/types.h>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FileHandleType>
+class NativeBufferHandle {
+ public:
+  NativeBufferHandle() { Clear(); }
+  NativeBufferHandle(const IonBuffer& buffer, int id)
+      : id_(id),
+        stride_(buffer.stride()),
+        width_(buffer.width()),
+        height_(buffer.height()),
+        format_(buffer.format()),
+        producer_usage_(buffer.producer_usage()),
+        consumer_usage_(buffer.consumer_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_, stride_, format_,
+                       producer_usage_, consumer_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 format_;
+  uint64_t producer_usage_;
+  uint64_t consumer_usage_;
+  std::vector<int> opaque_ints_;
+  std::vector<FileHandleType> fds_;
+
+  void Clear() {
+    id_ = -1;
+    stride_ = width_ = height_ = format_ = producer_usage_ = consumer_usage_ =
+        0;
+  }
+
+  PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle<FileHandleType>, id_, stride_,
+                           width_, height_, format_, producer_usage_,
+                           consumer_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 producer_set_mask;
+  uint64_t producer_clear_mask;
+  uint64_t producer_deny_set_mask;
+  uint64_t producer_deny_clear_mask;
+  uint64_t consumer_set_mask;
+  uint64_t consumer_clear_mask;
+  uint64_t consumer_deny_set_mask;
+  uint64_t consumer_deny_clear_mask;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(UsagePolicy, producer_set_mask, producer_clear_mask,
+                           producer_deny_set_mask, producer_deny_clear_mask,
+                           consumer_set_mask, consumer_clear_mask,
+                           consumer_deny_set_mask, consumer_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,
+    kOpGetBuffers,
+    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 producer_usage, uint64_t consumer_usage,
+                         size_t meta_size_bytes, size_t slice_count));
+  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 producer_usage, uint64_t consumer_usage,
+                         size_t meta_size_bytes, size_t slice_count));
+  PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer,
+                    void(const std::string& name));
+  PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer,
+                    NativeBufferHandle<LocalHandle>(unsigned index));
+  PDX_REMOTE_METHOD(GetBuffers, kOpGetBuffers,
+                    std::vector<NativeBufferHandle<LocalHandle>>(Void));
+  PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void));
+  PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent,
+                    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 format,
+                        uint64_t producer_usage, uint64_t consumer_usage,
+                        size_t slice_count, 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..e167a17
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -0,0 +1,118 @@
+#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, uint32_t usage);
+  IonBuffer(uint32_t width, uint32_t height, uint32_t format,
+            uint64_t producer_usage, uint64_t consumer_usage);
+  IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+            uint32_t stride, uint32_t format, uint32_t usage);
+  IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+            uint32_t stride, uint32_t format, uint64_t producer_usage,
+            uint64_t consumer_usage);
+  IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+            uint32_t layer_count, uint32_t stride, uint32_t layer_stride,
+            uint32_t format, uint32_t usage);
+  IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+            uint32_t layer_count, uint32_t stride, uint32_t layer_stride,
+            uint32_t format, uint64_t producer_usage, uint64_t consumer_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 format, uint32_t usage);
+  int Alloc(uint32_t width, uint32_t height, uint32_t format,
+            uint64_t producer_usage, uint64_t consumer_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 stride, uint32_t format, uint32_t usage);
+  void Reset(buffer_handle_t handle, uint32_t width, uint32_t height,
+             uint32_t stride, uint32_t format, uint64_t producer_usage,
+             uint64_t consumer_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 stride, uint32_t format, uint32_t usage);
+  int Import(buffer_handle_t handle, uint32_t width, uint32_t height,
+             uint32_t stride, uint32_t format, uint64_t producer_usage,
+             uint64_t consumer_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 stride,
+             uint32_t format, uint32_t usage);
+  int Import(const int* fd_array, int fd_count, const int* int_array,
+             int int_count, uint32_t width, uint32_t height, uint32_t stride,
+             uint32_t format, uint64_t producer_usage, uint64_t consumer_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 layer_stride() const { return 0; }
+  uint32_t format() const {
+    return buffer_.get() ? buffer_->getPixelFormat() : 0;
+  }
+  uint64_t producer_usage() const { return producer_usage_; }
+  uint64_t consumer_usage() const { return consumer_usage_; }
+  uint32_t usage() const { return buffer_.get() ? buffer_->getUsage() : 0; }
+
+ private:
+  sp<GraphicBuffer> buffer_;
+
+  // GraphicBuffer doesn't expose these separately. Keep these values cached for
+  // BufferHub to check policy against. Clients that import these buffers won't
+  // get the full picture, which is okay.
+  uint64_t producer_usage_;
+  uint64_t consumer_usage_;
+
+  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..f9b6975
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -0,0 +1,224 @@
+#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;
+};
+
+// NativeBufferProducerSlice is an implementation of ANativeWindowBuffer backed
+// by a buffer slice of a BufferProducer.
+class NativeBufferProducerSlice
+    : public android::ANativeObjectBase<
+          ANativeWindowBuffer, NativeBufferProducerSlice,
+          android::LightRefBase<NativeBufferProducerSlice>> {
+ public:
+  NativeBufferProducerSlice(const std::shared_ptr<BufferProducer>& buffer,
+                            int buffer_index)
+      : BASE(), buffer_(buffer) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle(buffer_index);
+  }
+
+  virtual ~NativeBufferProducerSlice() {}
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducerSlice>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+
+  NativeBufferProducerSlice(const NativeBufferProducerSlice&) = delete;
+  void operator=(NativeBufferProducerSlice&) = delete;
+};
+
+// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a
+// BufferProducer.
+class NativeBufferProducer : public android::ANativeObjectBase<
+  ANativeWindowBuffer, NativeBufferProducer,
+  android::LightRefBase<NativeBufferProducer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer,
+                       EGLDisplay display, uint32_t surface_buffer_index)
+      : BASE(),
+        buffer_(buffer),
+        surface_buffer_index_(surface_buffer_index),
+        display_(display) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle();
+    for (int i = 0; i < buffer->slice_count(); ++i) {
+      // display == null means don't create an EGL image. This is used by our
+      // Vulkan code.
+      slices_.push_back(new NativeBufferProducerSlice(buffer, i));
+      if (display_ != nullptr) {
+        egl_images_.push_back(eglCreateImageKHR(
+            display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+            static_cast<ANativeWindowBuffer*>(slices_.back().get()), nullptr));
+        if (egl_images_.back() == EGL_NO_IMAGE_KHR) {
+          ALOGE("NativeBufferProducer: eglCreateImageKHR failed");
+        }
+      }
+    }
+  }
+
+  explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer)
+      : NativeBufferProducer(buffer, nullptr, 0) {}
+
+  virtual ~NativeBufferProducer() {
+    for (EGLImageKHR egl_image : egl_images_) {
+      if (egl_image != EGL_NO_IMAGE_KHR)
+        eglDestroyImageKHR(display_, egl_image);
+    }
+  }
+
+  EGLImageKHR image_khr(int index) const { return egl_images_[index]; }
+  std::shared_ptr<BufferProducer> buffer() const { return buffer_; }
+  int release_fence() const { return release_fence_.Get(); }
+  uint32_t surface_buffer_index() const { return surface_buffer_index_; }
+
+  // Return the release fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); }
+
+  // Post the buffer consumer, closing the acquire and release fences.
+  int Post(int acquire_fence, uint64_t sequence) {
+    release_fence_.Close();
+    return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence);
+  }
+
+  // Gain the buffer producer, closing the previous release fence if valid.
+  int Gain() { return buffer_->Gain(&release_fence_); }
+
+  // Asynchronously gain the buffer, closing the previous release fence.
+  int GainAsync() {
+    release_fence_.Close();
+    return buffer_->GainAsync();
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducer>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+  pdx::LocalHandle release_fence_;
+  std::vector<android::sp<NativeBufferProducerSlice>> slices_;
+  std::vector<EGLImageKHR> egl_images_;
+  uint32_t surface_buffer_index_;
+  EGLDisplay display_;
+
+  NativeBufferProducer(const NativeBufferProducer&) = delete;
+  void operator=(NativeBufferProducer&) = delete;
+};
+
+// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a
+// BufferConsumer.
+class NativeBufferConsumer : public android::ANativeObjectBase<
+                                 ANativeWindowBuffer, NativeBufferConsumer,
+                                 android::LightRefBase<NativeBufferConsumer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer,
+                                int index)
+      : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    LOG_ALWAYS_FATAL_IF(buffer_->slice_count() <= index);
+    handle = buffer_->slice(index)->handle();
+  }
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer)
+      : NativeBufferConsumer(buffer, 0) {}
+
+  virtual ~NativeBufferConsumer() {}
+
+  std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+  int acquire_fence() const { return acquire_fence_.Get(); }
+  uint64_t sequence() const { return sequence_; }
+
+  // Return the acquire fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); }
+
+  // Acquire the underlying buffer consumer, closing the previous acquire fence
+  // if valid.
+  int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); }
+
+  // Release the buffer consumer, closing the acquire and release fences if
+  // valid.
+  int Release(int release_fence) {
+    acquire_fence_.Close();
+    sequence_ = 0;
+    return buffer_->Release(pdx::LocalHandle(release_fence));
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferConsumer>;
+
+  std::shared_ptr<BufferConsumer> buffer_;
+  pdx::LocalHandle acquire_fence_;
+  uint64_t sequence_;
+
+  NativeBufferConsumer(const NativeBufferConsumer&) = delete;
+  void operator=(NativeBufferConsumer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NATIVE_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
new file mode 100644
index 0000000..0a6996e
--- /dev/null
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -0,0 +1,292 @@
+#include <private/dvr/ion_buffer.h>
+
+#include <log/log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <grallocusage/GrallocUsageConversion.h>
+#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, 0) {}
+
+IonBuffer::IonBuffer(uint32_t width, uint32_t height, uint32_t format,
+                     uint32_t usage)
+    : IonBuffer(width, height, format, usage, usage) {}
+
+IonBuffer::IonBuffer(uint32_t width, uint32_t height, uint32_t format,
+                     uint64_t producer_usage, uint64_t consumer_usage)
+    : IonBuffer() {
+  Alloc(width, height, format, producer_usage, consumer_usage);
+}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+                     uint32_t stride, uint32_t format, uint32_t usage)
+    : IonBuffer(handle, width, height, 1, stride, 0, format, usage) {}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+                     uint32_t layer_count, uint32_t stride,
+                     uint32_t layer_stride, uint32_t format, uint32_t usage)
+    : IonBuffer(handle, width, height, layer_count, stride, layer_stride,
+                format, usage, usage) {}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, uint32_t width, uint32_t height,
+                     uint32_t layer_count, uint32_t stride,
+                     uint32_t layer_stride, uint32_t format,
+                     uint64_t producer_usage, uint64_t consumer_usage)
+    : buffer_(nullptr) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::IonBuffer: handle=%p width=%u height=%u layer_count=%u "
+           "stride=%u layer stride=%u format=%u producer_usage=%" PRIx64
+           " consumer_usage=%" PRIx64,
+           handle, width, height, layer_count, stride, layer_stride, format,
+           producer_usage, consumer_usage);
+  if (handle != 0) {
+    Import(handle, width, height, stride, format, producer_usage,
+           consumer_usage);
+  }
+}
+
+IonBuffer::~IonBuffer() {
+  ALOGD_IF(TRACE,
+           "IonBuffer::~IonBuffer: handle=%p width=%u height=%u stride=%u "
+           "format=%u usage=%x",
+           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;
+    producer_usage_ = 0;
+    consumer_usage_ = 0;
+  }
+}
+
+int IonBuffer::Alloc(uint32_t width, uint32_t height, uint32_t format,
+                     uint32_t usage) {
+  return Alloc(width, height, format, usage, usage);
+}
+
+int IonBuffer::Alloc(uint32_t width, uint32_t height, uint32_t format,
+                     uint64_t producer_usage, uint64_t consumer_usage) {
+  ALOGD_IF(
+      TRACE,
+      "IonBuffer::Alloc: width=%u height=%u format=%u producer_usage=%" PRIx64
+      " consumer_usage=%" PRIx64,
+      width, height, format, producer_usage, consumer_usage);
+
+  // TODO: forget about split producer/consumer usage
+  sp<GraphicBuffer> buffer = new GraphicBuffer(
+      width, height, format, kDefaultGraphicBufferLayerCount,
+      android_convertGralloc1To0Usage(producer_usage, consumer_usage));
+  if (buffer->initCheck() != OK) {
+    ALOGE("IonBuffer::Aloc: Failed to allocate buffer");
+    return -EINVAL;
+  } else {
+    buffer_ = buffer;
+    producer_usage_ = producer_usage;
+    consumer_usage_ = consumer_usage;
+    return 0;
+  }
+}
+
+void IonBuffer::Reset(buffer_handle_t handle, uint32_t width, uint32_t height,
+                      uint32_t stride, uint32_t format, uint32_t usage) {
+  Reset(handle, width, height, stride, format, usage, usage);
+}
+
+void IonBuffer::Reset(buffer_handle_t handle, uint32_t width, uint32_t height,
+                      uint32_t stride, uint32_t format, uint64_t producer_usage,
+                      uint64_t consumer_usage) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::Reset: handle=%p width=%u height=%u stride=%u format=%u "
+           "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64,
+           handle, width, height, stride, format, producer_usage,
+           consumer_usage);
+  Import(handle, width, height, stride, format, producer_usage, consumer_usage);
+}
+
+int IonBuffer::Import(buffer_handle_t handle, uint32_t width, uint32_t height,
+                      uint32_t stride, uint32_t format, uint32_t usage) {
+  return Import(handle, width, height, stride, format, usage, usage);
+}
+
+int IonBuffer::Import(buffer_handle_t handle, uint32_t width, uint32_t height,
+                      uint32_t stride, uint32_t format, uint64_t producer_usage,
+                      uint64_t consumer_usage) {
+  ATRACE_NAME("IonBuffer::Import1");
+  ALOGD_IF(
+      TRACE,
+      "IonBuffer::Import: handle=%p width=%u height=%u stride=%u format=%u "
+      "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64,
+      handle, width, height, stride, format, producer_usage, consumer_usage);
+  FreeHandle();
+  sp<GraphicBuffer> buffer =
+      new GraphicBuffer(handle, GraphicBuffer::TAKE_UNREGISTERED_HANDLE, width,
+                        height, format, kDefaultGraphicBufferLayerCount,
+                        static_cast<uint64_t>(android_convertGralloc1To0Usage(
+                            producer_usage, consumer_usage)),
+                        stride);
+  if (buffer->initCheck() != OK) {
+    ALOGE("IonBuffer::Import: Failed to import buffer");
+    return -EINVAL;
+  } else {
+    buffer_ = buffer;
+    producer_usage_ = producer_usage;
+    consumer_usage_ = consumer_usage;
+    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 stride, uint32_t format, uint32_t usage) {
+  return Import(fd_array, fd_count, int_array, int_count, width, height, stride,
+                format, usage, usage);
+}
+
+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 stride, uint32_t format, uint64_t producer_usage,
+                      uint64_t consumer_usage) {
+  ATRACE_NAME("IonBuffer::Import2");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Import: fd_count=%d int_count=%d width=%u height=%u "
+           "stride=%u format=%u producer_usage=%" PRIx64
+           " consumer_usage=%" PRIx64,
+           fd_count, int_count, width, height, stride, format, producer_usage,
+           consumer_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, stride, format, producer_usage,
+                         consumer_usage);
+  if (ret < 0) {
+    ALOGE("IonBuffer::Import: failed to import raw native handle: %s",
+          strerror(-ret));
+    native_handle_close(handle);
+    native_handle_delete(handle);
+  }
+
+  return ret;
+}
+
+int IonBuffer::Duplicate(const IonBuffer* other) {
+  if (!other->handle())
+    return -EINVAL;
+
+  const int fd_count = other->handle()->numFds;
+  const int int_count = other->handle()->numInts;
+
+  if (fd_count < 0 || int_count < 0)
+    return -EINVAL;
+
+  native_handle_t* handle = native_handle_create(fd_count, int_count);
+  if (!handle) {
+    ALOGE("IonBuffer::Duplicate: Failed to create new native handle.");
+    return -ENOMEM;
+  }
+
+  // Duplicate the file descriptors from the other native handle.
+  for (int i = 0; i < fd_count; i++)
+    handle->data[i] = dup(other->handle()->data[i]);
+
+  // Copy the ints after the file descriptors.
+  memcpy(handle->data + fd_count, other->handle()->data + fd_count,
+         sizeof(int) * int_count);
+
+  const int ret =
+      Import(handle, other->width(), other->height(), other->stride(),
+             other->format(), other->producer_usage(), other->consumer_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/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..33816fa
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
@@ -0,0 +1,57 @@
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_  // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+// TODO(jwcai) mock not need for now
+class native_handle_t;
+
+namespace android {
+namespace dvr {
+
+// TODO(jwcai) mock not need for now
+class IonBuffer;
+
+class BufferHubBuffer {
+ public:
+  MOCK_METHOD1(Poll, int(int timeout_ms));
+  MOCK_METHOD6(Lock, bool(int usage, int x, int y, int width, int height,
+                          void** addr));
+  MOCK_METHOD0(Unlock, int());
+
+  MOCK_METHOD0(native_handle, native_handle_t*());
+  MOCK_METHOD0(buffer, IonBuffer*());
+  MOCK_METHOD0(event_fd, int());
+
+  MOCK_METHOD0(id, int());
+  MOCK_METHOD0(width, int());
+  MOCK_METHOD0(height, int());
+  MOCK_METHOD0(stride, int());
+  MOCK_METHOD0(format, int());
+  MOCK_METHOD0(usage, int());
+};
+
+class BufferProducer : public BufferHubBuffer {
+ public:
+  // Note that static method |CreateBuffer| and |Import| are not mocked
+  // here, they are just implementation details and thus not needed.
+  MOCK_METHOD2(Post, int(int ready_fence, uint64_t sequence));
+  MOCK_METHOD1(Gain, int(int* release_fence));
+
+  static BufferProducer* staticObject;
+};
+
+class BufferConsumer : public BufferHubBuffer {
+ public:
+  MOCK_METHOD2(Acquire, int(int* ready_fence, uint64_t* sequence));
+  MOCK_METHOD1(Release, int(int release_fence));
+  MOCK_METHOD0(Discard, int());
+  MOCK_METHOD3(DoAcquire,
+               int(int* ready_fence, void* meta, size_t meta_size_bytes));
+
+  static BufferConsumer* staticObject;
+};
+
+}  // namespace dvr
+}  // namespace android
+#endif  // LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_  //NOLINT
diff --git a/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
new file mode 100644
index 0000000..9674c7c
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
@@ -0,0 +1,23 @@
+config("gralloc_config") {
+  include_dirs = [ "." ]
+}
+
+static_library("gralloc") {
+  testonly = true
+
+  sources = [
+    "gralloc.cpp",
+    "gralloc.h",
+  ]
+
+  include_dirs = [
+    "//system/core/include",
+    "//hardware/libhardware/include",
+  ]
+
+  public_deps = [
+    "//dreamos/external/gmock",
+  ]
+
+  public_configs = [ ":gralloc_config" ]
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
new file mode 100644
index 0000000..4a923ec
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
@@ -0,0 +1,68 @@
+#include <gralloc_mock.h>
+#include <hardware/gralloc.h>
+
+static alloc_device_t sdevice;
+
+static int local_registerBuffer(struct gralloc_module_t const*,
+                                buffer_handle_t handle) {
+  return GrallocMock::staticObject->registerBuffer(handle);
+}
+
+static int local_unregisterBuffer(struct gralloc_module_t const*,
+                                  buffer_handle_t handle) {
+  return GrallocMock::staticObject->unregisterBuffer(handle);
+}
+
+static int local_unlock(struct gralloc_module_t const*,
+                        buffer_handle_t handle) {
+  return GrallocMock::staticObject->unlock(handle);
+}
+
+static int local_lock(struct gralloc_module_t const*, buffer_handle_t handle,
+                      int usage, int l, int t, int w, int h, void** vaddr) {
+  return GrallocMock::staticObject->lock(handle, usage, l, t, w, h, vaddr);
+}
+
+static int local_alloc(struct alloc_device_t*, int w, int h, int format,
+                       int usage, buffer_handle_t* handle, int* stride) {
+  return GrallocMock::staticObject->alloc(w, h, format, usage, handle, stride);
+}
+
+static int local_free(struct alloc_device_t*, buffer_handle_t handle) {
+  return GrallocMock::staticObject->free(handle);
+}
+
+static int local_open(const struct hw_module_t*, const char*,
+                      struct hw_device_t** device) {
+  sdevice.alloc = local_alloc;
+  sdevice.free = local_free;
+  *device = reinterpret_cast<hw_device_t*>(&sdevice);
+  return 0;
+}
+
+static hw_module_methods_t smethods;
+
+static gralloc_module_t smodule;
+
+int hw_get_module(const char*, const struct hw_module_t** module) {
+  smodule.registerBuffer = local_registerBuffer;
+  smodule.unregisterBuffer = local_unregisterBuffer;
+  smodule.lock = local_lock;
+  smodule.unlock = local_unlock;
+  smethods.open = local_open;
+  smodule.common.methods = &smethods;
+  *module = reinterpret_cast<hw_module_t*>(&smodule);
+  return 0;
+}
+
+int native_handle_close(const native_handle_t* handle) {
+  return GrallocMock::staticObject->native_handle_close(handle);
+}
+
+int native_handle_delete(native_handle_t* handle) {
+  return GrallocMock::staticObject->native_handle_delete(handle);
+}
+
+native_handle_t* native_handle_create(int numFds, int numInts) {
+  return GrallocMock::staticObject->native_handle_create(numFds, numInts);
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
new file mode 100644
index 0000000..f62f579
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
@@ -0,0 +1,23 @@
+#ifndef LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#define LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/gralloc.h>
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class GrallocMock {
+ public:
+  // Add methods here.
+  MOCK_METHOD1(native_handle_close, int(const native_handle_t*));
+  MOCK_METHOD1(native_handle_delete, int(native_handle_t*));
+  MOCK_METHOD2(native_handle_create, native_handle_t*(int, int));
+  MOCK_METHOD1(registerBuffer, int(buffer_handle_t));
+  MOCK_METHOD1(unregisterBuffer, int(buffer_handle_t));
+  MOCK_METHOD7(lock, int(buffer_handle_t, int, int, int, int, int, void**));
+  MOCK_METHOD1(unlock, int(buffer_handle_t));
+  MOCK_METHOD6(alloc, int(int, int, int, int, buffer_handle_t*, int*));
+  MOCK_METHOD1(free, int(buffer_handle_t));
+  static GrallocMock* staticObject;
+};
+
+#endif  // LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
diff --git a/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..fac6db0
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
@@ -0,0 +1,78 @@
+// This file has a big hack, it "mocks" the actual IonBuffer by redefining
+// it with mock methods and using the same header guard to prevent the original
+// definition from being included in the same context.
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_  // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBufferMock {
+ public:
+  IonBufferMock() {}
+  MOCK_METHOD0(GetGrallocModuleImpl, gralloc_module_t const*());
+  MOCK_METHOD6(Import, int(buffer_handle_t handle, int width, int height,
+                           int stride, int format, int usage));
+  MOCK_METHOD9(Import, int(const int* fd_array, int fd_count,
+                           const int* int_array, int int_count, int width,
+                           int height, int stride, int format, int usage));
+  MOCK_METHOD6(Lock, int(int usage, int x, int y, int width, int height,
+                         void** address));
+  MOCK_METHOD0(Unlock, int());
+  MOCK_CONST_METHOD0(handle, buffer_handle_t());
+  MOCK_CONST_METHOD0(width, int());
+  MOCK_CONST_METHOD0(height, int());
+  MOCK_CONST_METHOD0(layer_count, int());
+  MOCK_CONST_METHOD0(stride, int());
+  MOCK_CONST_METHOD0(layer_stride, int());
+  MOCK_CONST_METHOD0(format, int());
+  MOCK_CONST_METHOD0(usage, int());
+};
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+  IonBuffer() : mock_(new IonBufferMock) {
+    if (initializer) {
+      initializer(mock_.get());
+    }
+  }
+  IonBuffer(IonBuffer&& other) = default;
+  static gralloc_module_t const* GetGrallocModule() {
+    return staticObject->GetGrallocModuleImpl();
+  }
+  int Import(buffer_handle_t handle, int width, int height, int stride,
+             int format, int usage) {
+    return mock_->Import(handle, width, height, stride, format, usage);
+  }
+  int Import(const int* fd_array, int fd_count, const int* int_array,
+             int int_count, int width, int height, int stride, int format,
+             int usage) {
+    return mock_->Import(fd_array, fd_count, int_array, int_count, width,
+                         height, stride, format, usage);
+  }
+  int Lock(int usage, int x, int y, int width, int height, void** address) {
+    return mock_->Lock(usage, x, y, width, height, address);
+  }
+  int Unlock() { return mock_->Unlock(); }
+  buffer_handle_t handle() const { return mock_->handle(); }
+  int width() const { return mock_->width(); }
+  int height() const { return mock_->height(); }
+  int layer_count() const { return mock_->layer_count(); }
+  int stride() const { return mock_->stride(); }
+  int layer_stride() const { return mock_->layer_stride(); }
+  int format() const { return mock_->format(); }
+  int usage() const { return mock_->usage(); }
+  std::unique_ptr<IonBufferMock> mock_;
+  static IonBufferMock* staticObject;
+  static void (*initializer)(IonBufferMock* target);
+};
+
+}  // namespace dvr
+}  // namespace android
+#endif  // LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_ - NOLINT
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
new file mode 100644
index 0000000..0fa1f01
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -0,0 +1,56 @@
+// 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_core.cpp",
+    "buffer_hub_queue_consumer.cpp",
+    "buffer_hub_queue_producer.cpp",
+]
+
+includeFiles = [
+    "include",
+]
+
+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,
+    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..b431d2f
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -0,0 +1,557 @@
+#include "include/private/dvr/buffer_hub_queue_client.h"
+
+#include <inttypes.h>
+#include <log/log.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>
+
+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;
+}
+
+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;
+
+  while (count() == 0) {
+    int ret = epoll_fd_.Wait(events.data(), events.size(), timeout);
+
+    if (ret == 0) {
+      ALOGD_IF(TRACE, "Wait on epoll returns nothing before timeout.");
+      return false;
+    }
+
+    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, "New BufferHubQueue event %d: index=%" PRId64, i, index);
+
+      if (is_buffer_event_index(index)) {
+        HandleBufferEvent(static_cast<size_t>(index), events[i]);
+      } else if (is_queue_event_index(index)) {
+        HandleQueueEvent(events[i]);
+      } else {
+        ALOGW("BufferHubQueue::WaitForBuffers: Unknown event index: %" PRId64,
+              index);
+      }
+    }
+  }
+
+  return true;
+}
+
+void BufferHubQueue::HandleBufferEvent(size_t slot, const epoll_event& event) {
+  auto buffer = buffers_[slot];
+  if (!buffer) {
+    ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
+    return;
+  }
+
+  auto status = buffer->GetEventMask(event.events);
+  if (!status) {
+    ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
+          status.GetErrorMessage().c_str());
+    return;
+  }
+
+  int events = status.get();
+  if (events & EPOLLIN) {
+    int ret = OnBufferReady(buffer, &fences_[slot]);
+    if (ret < 0) {
+      ALOGE("BufferHubQueue::HandleBufferEvent: Failed to set buffer ready: %s",
+            strerror(-ret));
+      return;
+    }
+    Enqueue(buffer, slot);
+  } 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(const epoll_event& event) {
+  auto status = GetEventMask(event.events);
+  if (!status) {
+    ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
+          status.GetErrorMessage().c_str());
+    return;
+  }
+
+  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 {
+    ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%d", 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(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_);
+  // Swap buffer into vector.
+  std::swap(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));
+}
+
+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 (count() == 0 && !WaitForBuffers(timeout))
+    return nullptr;
+
+  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 nullptr;
+  }
+
+  if (meta) {
+    std::copy(meta_buffer_tmp_.get(), meta_buffer_tmp_.get() + meta_size_,
+              reinterpret_cast<uint8_t*>(meta));
+  }
+
+  return buf;
+}
+
+ProducerQueue::ProducerQueue(size_t meta_size)
+    : ProducerQueue(meta_size, 0, 0, 0, 0, 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 producer_usage_set_mask,
+                             uint64_t producer_usage_clear_mask,
+                             uint64_t producer_usage_deny_set_mask,
+                             uint64_t producer_usage_deny_clear_mask,
+                             uint64_t consumer_usage_set_mask,
+                             uint64_t consumer_usage_clear_mask,
+                             uint64_t consumer_usage_deny_set_mask,
+                             uint64_t consumer_usage_deny_clear_mask)
+    : BASE(BufferHubRPC::kClientPath) {
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+      meta_size,
+      UsagePolicy{producer_usage_set_mask, producer_usage_clear_mask,
+                  producer_usage_deny_set_mask, producer_usage_deny_clear_mask,
+                  consumer_usage_set_mask, consumer_usage_clear_mask,
+                  consumer_usage_deny_set_mask,
+                  consumer_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 format, uint32_t usage,
+                                  size_t slice_count, size_t* out_slot) {
+  return AllocateBuffer(width, height, format, usage, usage, slice_count,
+                        out_slot);
+}
+
+int ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
+                                  uint32_t format, uint64_t producer_usage,
+                                  uint64_t consumer_usage, size_t slice_count,
+                                  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, format, producer_usage, consumer_usage, slice_count,
+          kBufferCount);
+  if (!status) {
+    ALOGE(
+        "ProducerQueue::AllocateBuffer failed to create producer buffer "
+        "through BufferHub.");
+    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) {
+  // 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 through "
+        "BufferHub, error: %s",
+        status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  return BufferHubQueue::DetachBuffer(slot);
+}
+
+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 nullptr;
+  }
+
+  auto buf = BufferHubQueue::Dequeue(timeout, slot, nullptr, release_fence);
+  return std::static_pointer_cast<BufferProducer>(buf);
+}
+
+int ProducerQueue::OnBufferReady(std::shared_ptr<BufferHubBuffer> buf,
+                                 LocalHandle* release_fence) {
+  auto buffer = std::static_pointer_cast<BufferProducer>(buf);
+  return buffer->Gain(release_fence);
+}
+
+ConsumerQueue::ConsumerQueue(LocalChannelHandle handle)
+    : BufferHubQueue(std::move(handle)) {
+  auto status = ImportQueue();
+  if (!status) {
+    ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+  }
+
+  // TODO(b/34387835) Import buffers in case the ProducerQueue we are
+  // based on was not empty.
+}
+
+Status<size_t> ConsumerQueue::ImportBuffers() {
+  auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
+  if (!status) {
+    ALOGE(
+        "ConsumerQueue::ImportBuffers failed to import consumer buffer through "
+        "BufferBub, error: %s",
+        status.GetErrorMessage().c_str());
+    return ErrorStatus(status.error());
+  }
+
+  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, new buffer, buffer_handle: %d",
+             buffer_handle_slot.first.value());
+
+    std::unique_ptr<BufferConsumer> buffer_consumer =
+        BufferConsumer::Import(std::move(buffer_handle_slot.first));
+    int ret = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
+    if (ret < 0) {
+      ALOGE("ConsumerQueue::ImportBuffers failed to add buffer, ret: %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) {
+  // Consumer queue starts with all buffers in unavailable state.
+  return BufferHubQueue::AddBuffer(buf, slot);
+}
+
+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 nullptr;
+  }
+
+  if (slot == nullptr || meta == nullptr || acquire_fence == nullptr) {
+    ALOGE(
+        "ConsumerQueue::Dequeue: Invalid parameter, slot=%p, meta=%p, "
+        "acquire_fence=%p",
+        slot, meta, acquire_fence);
+    return nullptr;
+  }
+
+  auto buf = BufferHubQueue::Dequeue(timeout, slot, meta, acquire_fence);
+  return std::static_pointer_cast<BufferConsumer>(buf);
+}
+
+int ConsumerQueue::OnBufferReady(std::shared_ptr<BufferHubBuffer> buf,
+                                 LocalHandle* acquire_fence) {
+  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, "Imported %zu consumer buffers.", status.get());
+    return {};
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
new file mode 100644
index 0000000..1ea3994
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_consumer.cpp
@@ -0,0 +1,11 @@
+#include "include/private/dvr/buffer_hub_queue_consumer.h"
+
+namespace android {
+namespace dvr {
+
+BufferHubQueueConsumer::BufferHubQueueConsumer(
+    const std::shared_ptr<BufferHubQueueCore>& core)
+    : core_(core) {}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
new file mode 100644
index 0000000..a826a69
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
@@ -0,0 +1,75 @@
+#include "include/private/dvr/buffer_hub_queue_core.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+/* static */
+std::shared_ptr<BufferHubQueueCore> BufferHubQueueCore::Create() {
+  auto core = std::shared_ptr<BufferHubQueueCore>(new BufferHubQueueCore());
+  core->producer_ = ProducerQueue::Create<BufferMetadata>();
+  return core;
+}
+
+/* static */
+std::shared_ptr<BufferHubQueueCore> BufferHubQueueCore::Create(
+    const std::shared_ptr<ProducerQueue>& producer) {
+  if (producer->metadata_size() != sizeof(BufferMetadata)) {
+    ALOGE(
+        "BufferHubQueueCore::Create producer's metadata size is different than "
+        "the size of BufferHubQueueCore::BufferMetadata");
+    return nullptr;
+  }
+
+  auto core = std::shared_ptr<BufferHubQueueCore>(new BufferHubQueueCore());
+  core->producer_ = producer;
+  return core;
+}
+
+BufferHubQueueCore::BufferHubQueueCore()
+    : generation_number_(0),
+      dequeue_timeout_ms_(BufferHubQueue::kNoTimeOut),
+      unique_id_(getUniqueId()) {}
+
+status_t BufferHubQueueCore::AllocateBuffer(uint32_t width, uint32_t height,
+                                            PixelFormat format, uint32_t usage,
+                                            size_t slice_count) {
+  size_t slot;
+
+  // Allocate new buffer through BufferHub and add it into |producer_| queue for
+  // bookkeeping.
+  if (producer_->AllocateBuffer(width, height, format, usage, slice_count,
+                                &slot) < 0) {
+    ALOGE("Failed to allocate new buffer in BufferHub.");
+    return NO_MEMORY;
+  }
+
+  auto buffer_producer = producer_->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 BufferHubQueueCore::DetachBuffer(size_t slot) {
+  // Detach the buffer producer via BufferHubRPC.
+  int ret = producer_->DetachBuffer(slot);
+  if (ret < 0) {
+    ALOGE("BufferHubQueueCore::DetachBuffer 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/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
new file mode 100644
index 0000000..e236c31
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -0,0 +1,553 @@
+#include "include/private/dvr/buffer_hub_queue_producer.h"
+
+#include <inttypes.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+BufferHubQueueProducer::BufferHubQueueProducer(
+    const std::shared_ptr<BufferHubQueueCore>& core)
+    : core_(core) {}
+
+status_t BufferHubQueueProducer::requestBuffer(int slot,
+                                               sp<GraphicBuffer>* buf) {
+  ALOGD_IF(TRACE, "requestBuffer: slot=%d", slot);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (core_->connected_api_ == BufferHubQueueCore::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 (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  } else if (core_->buffers_[slot].mGraphicBuffer != nullptr) {
+    ALOGE("requestBuffer: slot %d is not empty.", slot);
+    return BAD_VALUE;
+  } else if (core_->buffers_[slot].mBufferProducer == nullptr) {
+    ALOGE("requestBuffer: slot %d is not dequeued.", slot);
+    return BAD_VALUE;
+  }
+
+  const auto& buffer_producer = core_->buffers_[slot].mBufferProducer;
+  sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer();
+
+  core_->buffers_[slot].mGraphicBuffer = graphic_buffer;
+  core_->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(core_->mutex_);
+
+  if (max_dequeued_buffers <= 0 ||
+      max_dequeued_buffers >
+          static_cast<int>(BufferHubQueue::kMaxQueueCapacity)) {
+    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 : core_->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(core_->mutex_);
+
+  if (core_->connected_api_ == BufferHubQueueCore::kNoConnectedApi) {
+    ALOGE("dequeueBuffer: BufferQueue has no connected producer");
+    return NO_INIT;
+  }
+
+  if (static_cast<int32_t>(core_->producer_->capacity()) <
+      max_dequeued_buffer_count_) {
+    // Lazy allocation. When the capacity of |core_->producer_| has not reach
+    // |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 = core_->AllocateBuffer(width, height, format, usage, 1);
+    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;
+    buffer_producer =
+        core_->producer_->Dequeue(core_->dequeue_timeout_ms_, &slot, &fence);
+    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.
+    core_->buffers_[slot].mIsReallocating = true;
+
+    // Detach the old buffer once the allocation before allocating its
+    // replacement.
+    core_->DetachBuffer(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 |core_->producer_->Dequeue| may not be the new buffer we just
+    // reallocated. Retry up to BufferHubQueue::kMaxQueueCapacity times.
+    ret = core_->AllocateBuffer(width, height, format, usage, 1);
+    if (ret < 0)
+      return ret;
+  }
+
+  // With the BufferHub backed solution. Buffer slot returned from
+  // |core_->producer_->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((!core_->buffers_[slot].mBufferState.isFree() &&
+                       !core_->buffers_[slot].mBufferState.isQueued()),
+                      "dequeueBuffer: slot %zu is not free or queued.", slot);
+
+  core_->buffers_[slot].mBufferState.freeQueued();
+  core_->buffers_[slot].mBufferState.dequeue();
+  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 (core_->buffers_[slot].mIsReallocating) {
+    ret |= BUFFER_NEEDS_REALLOCATION;
+    core_->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;
+  int scaling_mode;
+  sp<Fence> fence;
+  Rect crop(Rect::EMPTY_RECT);
+
+  // TODO(jwcai) The following attributes are ignored.
+  bool is_auto_timestamp;
+  android_dataspace data_space;
+  uint32_t transform;
+
+  input.deflate(&timestamp, &is_auto_timestamp, &data_space, &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(core_->mutex_);
+
+  if (core_->connected_api_ == BufferHubQueueCore::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 (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  } else if ((!core_->buffers_[slot].mRequestBufferCalled ||
+              core_->buffers_[slot].mGraphicBuffer == nullptr)) {
+    ALOGE(
+        "queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, "
+        "mGraphicBuffer=%p)",
+        slot, core_->buffers_[slot].mRequestBufferCalled,
+        core_->buffers_[slot].mGraphicBuffer.get());
+    return BAD_VALUE;
+  }
+
+  // Post the buffer producer with timestamp in the metadata.
+  const auto& buffer_producer = core_->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);
+
+  BufferHubQueueCore::BufferMetadata meta_data = {.timestamp = timestamp};
+  buffer_producer->Post(fence_fd, &meta_data, sizeof(meta_data));
+  core_->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(core_->mutex_);
+
+  if (core_->connected_api_ == BufferHubQueueCore::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 (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  } else if (fence == nullptr) {
+    ALOGE("cancelBuffer: fence is NULL");
+    return BAD_VALUE;
+  }
+
+  auto buffer_producer = core_->buffers_[slot].mBufferProducer;
+  core_->producer_->Enqueue(buffer_producer, slot);
+  core_->buffers_[slot].mBufferState.cancel();
+  core_->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(core_->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:
+      value = 0;
+      break;
+    case NATIVE_WINDOW_BUFFER_AGE:
+      value = 0;
+      break;
+    case NATIVE_WINDOW_WIDTH:
+      value = core_->producer_->default_width();
+      break;
+    case NATIVE_WINDOW_HEIGHT:
+      value = core_->producer_->default_height();
+      break;
+    case NATIVE_WINDOW_FORMAT:
+      value = core_->producer_->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;
+    // The following queries are currently considered as unsupported.
+    // TODO(jwcai) Need to carefully check the whether they should be
+    // supported after all.
+    case NATIVE_WINDOW_STICKY_TRANSFORM:
+    case NATIVE_WINDOW_DEFAULT_DATASPACE:
+    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(core_->mutex_);
+
+  if (core_->connected_api_ != BufferHubQueueCore::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:
+      core_->connected_api_ = api;
+      // TODO(jwcai) Fill output.
+      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(core_->mutex_);
+
+  if (BufferHubQueueCore::kNoConnectedApi == core_->connected_api_) {
+    return NO_INIT;
+  } else if (api != core_->connected_api_) {
+    return BAD_VALUE;
+  }
+
+  core_->connected_api_ = BufferHubQueueCore::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(core_->mutex_);
+  core_->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 */) {
+  ALOGE("BufferHubQueueProducer::setSharedBufferMode not implemented.");
+  // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow.
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setAutoRefresh(bool /* auto_refresh */) {
+  ALOGE("BufferHubQueueProducer::setAutoRefresh not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) {
+  ALOGD_IF(TRACE, __FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+  core_->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 = core_->unique_id_;
+  return NO_ERROR;
+}
+
+IBinder* BufferHubQueueProducer::onAsBinder() {
+  // BufferHubQueueProducer is a non-binder implementation of
+  // IGraphicBufferProducer.
+  ALOGW("BufferHubQueueProducer::onAsBinder is not supported.");
+  return nullptr;
+}
+
+}  // 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..255793f
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -0,0 +1,436 @@
+#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();
+
+  // 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);
+    }
+  }
+
+  // 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(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_; }
+
+ 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.
+  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, const epoll_event& event);
+  void HandleQueueEvent(const epoll_event& event);
+
+  virtual int OnBufferReady(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 avaiable 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, consuemr 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_;
+
+  // 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));
+  }
+
+  // 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,
+                        usage_set_mask, usage_clear_mask, usage_deny_set_mask,
+                        usage_deny_clear_mask);
+  }
+  template <typename Meta>
+  static std::unique_ptr<ProducerQueue> Create(
+      uint64_t producer_usage_set_mask, uint64_t producer_usage_clear_mask,
+      uint64_t producer_usage_deny_set_mask,
+      uint64_t producer_usage_deny_clear_mask, uint64_t consumer_usage_set_mask,
+      uint64_t consumer_usage_clear_mask, uint64_t consumer_usage_deny_set_mask,
+      uint64_t consumer_usage_deny_clear_mask) {
+    return BASE::Create(sizeof(Meta), producer_usage_set_mask,
+                        producer_usage_clear_mask, producer_usage_deny_set_mask,
+                        producer_usage_deny_clear_mask, consumer_usage_set_mask,
+                        consumer_usage_clear_mask, consumer_usage_deny_set_mask,
+                        consumer_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 format,
+                     uint32_t usage, size_t slice_count, size_t* out_slot);
+  int AllocateBuffer(uint32_t width, uint32_t height, uint32_t format,
+                     uint64_t producer_usage, uint64_t consumer_usage,
+                     size_t slice_count, 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.
+  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 producer_usage_set_mask,
+                uint64_t producer_usage_clear_mask,
+                uint64_t producer_usage_deny_set_mask,
+                uint64_t producer_usage_deny_clear_mask,
+                uint64_t consumer_usage_set_mask,
+                uint64_t consumer_usage_clear_mask,
+                uint64_t consumer_usage_deny_set_mask,
+                uint64_t consumer_usage_deny_clear_mask);
+
+  int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf,
+                    LocalHandle* release_fence) override;
+};
+
+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 it 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.
+  static std::unique_ptr<ConsumerQueue> Import(LocalChannelHandle handle) {
+    return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(std::move(handle)));
+  }
+
+  // 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>
+  std::shared_ptr<BufferConsumer> Dequeue(int timeout, size_t* slot, Meta* meta,
+                                          LocalHandle* acquire_fence) {
+    return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
+  }
+
+  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);
+
+  // 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(std::shared_ptr<BufferHubBuffer> buf,
+                    LocalHandle* acquire_fence) override;
+
+  Status<void> OnBufferAllocated() override;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+// Concrete C type definition for DVR API.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct DvrWriteBufferQueue {
+  std::shared_ptr<android::dvr::ProducerQueue> producer_queue_;
+  ANativeWindow* native_window_{nullptr};
+};
+
+struct DvrReadBufferQueue {
+  std::shared_ptr<android::dvr::ConsumerQueue> consumer_queue_;
+};
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_consumer.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_consumer.h
new file mode 100644
index 0000000..8d7bfcc
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_consumer.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CONSUMER_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_CONSUMER_H_
+
+#include <private/dvr/buffer_hub_queue_core.h>
+
+#include <gui/IGraphicBufferConsumer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubQueueConsumer : public IGraphicBufferConsumer {
+ public:
+  BufferHubQueueConsumer(const std::shared_ptr<BufferHubQueueCore>& core);
+
+ private:
+  std::shared_ptr<BufferHubQueueCore> core_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_CONSUMER_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_core.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_core.h
new file mode 100644
index 0000000..e353187
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_core.h
@@ -0,0 +1,121 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CORE_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_CORE_H_
+
+#include <private/dvr/buffer_hub_queue_client.h>
+
+#include <gui/BufferSlot.h>
+#include <utils/Atomic.h>
+#include <utils/String8.h>
+
+#include <mutex>
+
+namespace android {
+namespace dvr {
+
+class BufferHubQueueCore {
+ private:
+  friend class BufferHubQueueProducer;
+
+ public:
+  static constexpr int kNoConnectedApi = -1;
+
+  // Create a BufferHubQueueCore instance by creating a new producer queue.
+  static std::shared_ptr<BufferHubQueueCore> Create();
+
+  // Create a BufferHubQueueCore instance by importing an existing prodcuer queue.
+  static std::shared_ptr<BufferHubQueueCore> Create(
+      const std::shared_ptr<ProducerQueue>& producer);
+
+  struct BufferMetadata {
+    // Timestamp of the frame.
+    int64_t timestamp;
+  };
+
+  class NativeBuffer
+      : public ANativeObjectBase<ANativeWindowBuffer, NativeBuffer, RefBase> {
+   public:
+    explicit NativeBuffer(const std::shared_ptr<BufferHubBuffer>& buffer)
+        : buffer_(buffer) {
+      ANativeWindowBuffer::width = buffer_->width();
+      ANativeWindowBuffer::height = buffer_->height();
+      ANativeWindowBuffer::stride = buffer_->stride();
+      ANativeWindowBuffer::format = buffer_->format();
+      ANativeWindowBuffer::usage = buffer_->usage();
+      ANativeWindowBuffer::handle = buffer_->buffer()->handle();
+    }
+
+    std::shared_ptr<BufferHubBuffer> buffer() { return buffer_; }
+
+   private:
+    std::shared_ptr<BufferHubBuffer> buffer_;
+  };
+
+  // Get the unique buffer producer queue backing this BufferHubQueue.
+  std::shared_ptr<ProducerQueue> GetProducerQueue() { return producer_; }
+
+ private:
+  using LocalHandle = pdx::LocalHandle;
+
+  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;
+  };
+
+  static String8 getUniqueName() {
+    static volatile int32_t counter = 0;
+    return String8::format("unnamed-%d-%d", getpid(),
+                           android_atomic_inc(&counter));
+  }
+
+  static uint64_t getUniqueId() {
+    static std::atomic<uint32_t> counter{0};
+    static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+    return id | counter++;
+  }
+
+  // Private constructor to force use of |Create|.
+  BufferHubQueueCore();
+
+  // Allocate a new buffer producer through BufferHub.
+  int AllocateBuffer(uint32_t width, uint32_t height, PixelFormat format,
+                     uint32_t usage, size_t slice_count);
+
+  // Detach a buffer producer through BufferHub.
+  int DetachBuffer(size_t slot);
+
+  // Mutex for thread safety.
+  std::mutex mutex_;
+
+  // Connect client API, should be one of the NATIVE_WINDOW_API_* flags.
+  int connected_api_{kNoConnectedApi};
+
+  // |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.
+  BufferHubSlot buffers_[BufferHubQueue::kMaxQueueCapacity];
+
+  // Concreate implementation backed by BufferHubBuffer.
+  std::shared_ptr<ProducerQueue> producer_;
+
+  // |generation_number_| stores the current generation number of the attached
+  // producer. Any attempt to attach a buffer with a different generation
+  // number will fail.
+  uint32_t generation_number_;
+
+  // 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_;
+
+  const uint64_t unique_id_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_CORE_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..43e5ce3
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
@@ -0,0 +1,120 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
+
+#include <private/dvr/buffer_hub_queue_core.h>
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubQueueProducer : public IGraphicBufferProducer {
+ public:
+  BufferHubQueueProducer(const std::shared_ptr<BufferHubQueueCore>& core);
+
+  // 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;
+
+ protected:
+  IBinder* onAsBinder() override;
+
+ private:
+  using LocalHandle = pdx::LocalHandle;
+
+  // |core_| holds the actually buffer slots.
+  std::shared_ptr<BufferHubQueueCore> core_;
+
+  // |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};
+};
+
+}  // 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..171577d
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -0,0 +1,305 @@
+#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 kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
+constexpr int kBufferSliceCount = 1;  // number of slices in each buffer
+
+class BufferHubQueueTest : public ::testing::Test {
+ public:
+  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) {
+    producer_queue_ =
+        ProducerQueue::Create<Meta>(usage_set_mask, usage_clear_mask,
+                                    usage_deny_set_mask, usage_deny_clear_mask);
+    if (!producer_queue_)
+      return false;
+
+    consumer_queue_ = producer_queue_->CreateConsumerQueue();
+    if (!consumer_queue_)
+      return false;
+
+    return true;
+  }
+
+  void AllocateBuffer() {
+    // Create producer buffer.
+    size_t slot;
+    int ret = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                              kBufferFormat, kBufferUsage,
+                                              kBufferSliceCount, &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 = producer_queue_->Dequeue(0, &slot, &fence);
+    ASSERT_NE(nullptr, p1);
+    size_t mi = i;
+    ASSERT_EQ(p1->Post(LocalHandle(), &mi, sizeof(mi)), 0);
+    size_t mo;
+    auto c1 = consumer_queue_->Dequeue(100, &slot, &mo, &fence);
+    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 nullptr since no buffer is ready to consumer, but
+    // this implicitly triggers buffer import and bump up |capacity|.
+    LocalHandle fence;
+    auto consumer_null = consumer_queue_->Dequeue(0, &slot, &seq, &fence);
+    ASSERT_EQ(nullptr, consumer_null);
+    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 buffer_null = consumer_queue_->Dequeue(0, &slot, &seq, &fence);
+    ASSERT_EQ(nullptr, buffer_null);
+
+    // Make sure Producer buffer is Post()'ed so that it's ready to Accquire
+    // in the consumer's Dequeue() function.
+    auto producer = producer_queue_->Dequeue(0, &slot, &fence);
+    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;
+    auto consumer = consumer_queue_->Dequeue(0, &slot, &seq_out, &fence);
+    ASSERT_NE(nullptr, consumer);
+    ASSERT_EQ(seq_in, seq_out);
+  }
+}
+
+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 = producer_queue_->Dequeue(0, &slot, &fence);
+    ASSERT_NE(nullptr, p1);
+    ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0);
+    TestMetadata mo;
+    auto c1 = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+    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 = producer_queue_->Dequeue(0, &slot, &fence);
+  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 = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+  ASSERT_EQ(nullptr, c1);
+}
+
+TEST_F(BufferHubQueueTest, TestEnqueue) {
+  ASSERT_TRUE(CreateQueues<int64_t>());
+  AllocateBuffer();
+
+  size_t slot;
+  LocalHandle fence;
+  auto p1 = producer_queue_->Dequeue(0, &slot, &fence);
+  ASSERT_NE(nullptr, p1);
+
+  int64_t mo;
+  producer_queue_->Enqueue(p1, slot);
+  auto c1 = consumer_queue_->Dequeue(0, &slot, &mo, &fence);
+  ASSERT_EQ(nullptr, c1);
+}
+
+TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
+  ASSERT_TRUE(CreateQueues<int64_t>());
+
+  size_t s1;
+  AllocateBuffer();
+  LocalHandle fence;
+  auto p1 = producer_queue_->Dequeue(0, &s1, &fence);
+  ASSERT_NE(nullptr, p1);
+
+  // producer queue is exhausted
+  size_t s2;
+  auto p2_null = producer_queue_->Dequeue(0, &s2, &fence);
+  ASSERT_EQ(nullptr, p2_null);
+
+  // dynamically add buffer.
+  AllocateBuffer();
+  ASSERT_EQ(producer_queue_->count(), 1U);
+  ASSERT_EQ(producer_queue_->capacity(), 2U);
+
+  // now we can dequeue again
+  auto p2 = producer_queue_->Dequeue(0, &s2, &fence);
+  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 = consumer_queue_->Dequeue(0, &cs1, &seq, &fence);
+  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 = consumer_queue_->Dequeue(0, &cs2, &seq, &fence);
+  ASSERT_NE(nullptr, c2);
+  ASSERT_EQ(cs2, s2);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageSetMask) {
+  const int 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, kBufferUsage & ~set_mask,
+      kBufferSliceCount, &slot);
+  ASSERT_EQ(ret, 0);
+
+  LocalHandle fence;
+  auto p1 = producer_queue_->Dequeue(0, &slot, &fence);
+  ASSERT_EQ(p1->usage() & set_mask, set_mask);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageClearMask) {
+  const int 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, kBufferFormat, kBufferUsage | clear_mask,
+      kBufferSliceCount, &slot);
+  ASSERT_EQ(ret, 0);
+
+  LocalHandle fence;
+  auto p1 = producer_queue_->Dequeue(0, &slot, &fence);
+  ASSERT_EQ(p1->usage() & clear_mask, 0);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
+  const int 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, kBufferFormat, kBufferUsage & ~deny_set_mask,
+      kBufferSliceCount, &slot);
+  ASSERT_EQ(ret, 0);
+
+  // While allocation with those bits should fail.
+  ret = producer_queue_->AllocateBuffer(
+      kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage | deny_set_mask,
+      kBufferSliceCount, &slot);
+  ASSERT_EQ(ret, -EINVAL);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) {
+  const int 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, kBufferFormat,
+      kBufferUsage | deny_clear_mask, kBufferSliceCount, &slot);
+  ASSERT_EQ(ret, 0);
+
+  // While allocation without those bits should fail.
+  ret = producer_queue_->AllocateBuffer(
+      kBufferWidth, kBufferHeight, kBufferFormat,
+      kBufferUsage & ~deny_clear_mask, kBufferSliceCount, &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..64034e8
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -0,0 +1,513 @@
+#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());
+
+    auto core = BufferHubQueueCore::Create();
+    mProducer = new BufferHubQueueProducer(core);
+    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..de2a56e
--- /dev/null
+++ b/libs/vr/libdisplay/Android.bp
@@ -0,0 +1,102 @@
+// 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 = [
+    "native_buffer_queue.cpp",
+    "display_client.cpp",
+    "display_manager_client_impl.cpp",
+    "display_rpc.cpp",
+    "dummy_native_window.cpp",
+    "gl_fenced_flush.cpp",
+    "graphics.cpp",
+    "late_latch.cpp",
+    "video_mesh_surface_client.cpp",
+    "vsync_client.cpp",
+    "screenshot_client.cpp",
+    "frame_history.cpp",
+]
+
+localIncludeFiles = [
+    "include",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "liblog",
+    "libutils",
+    "libEGL",
+    "libGLESv2",
+    "libvulkan",
+    "libui",
+    "libgui",
+    "libhardware",
+    "libsync",
+    "libnativewindow",
+]
+
+staticLibraries = [
+    "libbufferhub",
+    "libbufferhubqueue",
+    "libdvrcommon",
+    "libdvrgraphics",
+    "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",
+}
+
+graphicsAppTestFiles = ["tests/graphics_app_tests.cpp"]
+
+cc_test {
+    name: "graphics_app_tests",
+    tags: ["optional"],
+
+    srcs: graphicsAppTestFiles,
+
+    shared_libs: sharedLibraries,
+
+    static_libs: ["libdisplay"] + staticLibraries,
+}
+
+dummyNativeWindowTestFiles = ["tests/dummy_native_window_tests.cpp"]
+
+cc_test {
+    name: "dummy_native_window_tests",
+    tags: [ "optional" ],
+    srcs: dummyNativeWindowTestFiles,
+    shared_libs: sharedLibraries,
+    static_libs: [ "libdisplay" ] + staticLibraries,
+}
+
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
new file mode 100644
index 0000000..ef50a0f
--- /dev/null
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -0,0 +1,280 @@
+#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_rpc.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/native_buffer.h>
+
+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 {
+
+SurfaceClient::SurfaceClient(LocalChannelHandle channel_handle,
+                             SurfaceType type)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      type_(type) {}
+
+SurfaceClient::SurfaceClient(const std::string& endpoint_path, SurfaceType type)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+                 endpoint_path),
+             kInfiniteTimeout},
+      type_(type) {}
+
+int SurfaceClient::GetMetadataBufferFd(LocalHandle* out_fd) {
+  auto buffer_producer = GetMetadataBuffer();
+  if (!buffer_producer)
+    return -ENOMEM;
+
+  *out_fd = buffer_producer->GetBlobFd();
+  return 0;
+}
+
+std::shared_ptr<BufferProducer> SurfaceClient::GetMetadataBuffer() {
+  if (!metadata_buffer_) {
+    auto status = InvokeRemoteMethod<DisplayRPC::GetMetadataBuffer>();
+    if (!status) {
+      ALOGE(
+          "SurfaceClient::AllocateMetadataBuffer: Failed to allocate buffer: "
+          "%s",
+          status.GetErrorMessage().c_str());
+      return nullptr;
+    }
+
+    metadata_buffer_ = BufferProducer::Import(status.take());
+  }
+
+  return metadata_buffer_;
+}
+
+DisplaySurfaceClient::DisplaySurfaceClient(int width, int height, int format,
+                                           int usage, int flags)
+    : BASE(DisplayRPC::kClientPath, SurfaceTypeEnum::Normal),
+      width_(width),
+      height_(height),
+      format_(format),
+      usage_(usage),
+      flags_(flags),
+      z_order_(0),
+      visible_(true),
+      exclude_from_blur_(false),
+      blur_behind_(true),
+      mapped_metadata_buffer_(nullptr) {
+  auto status = InvokeRemoteMethod<DisplayRPC::CreateSurface>(
+      width, height, format, usage, flags);
+  if (!status) {
+    ALOGE(
+        "DisplaySurfaceClient::DisplaySurfaceClient: Failed to create display "
+        "surface: %s",
+        status.GetErrorMessage().c_str());
+    Close(status.error());
+  }
+}
+
+void DisplaySurfaceClient::SetVisible(bool visible) {
+  SetAttributes({{DisplaySurfaceAttributeEnum::Visible,
+                  DisplaySurfaceAttributeValue{visible}}});
+}
+
+void DisplaySurfaceClient::SetZOrder(int z_order) {
+  SetAttributes({{DisplaySurfaceAttributeEnum::ZOrder,
+                  DisplaySurfaceAttributeValue{z_order}}});
+}
+
+void DisplaySurfaceClient::SetExcludeFromBlur(bool exclude_from_blur) {
+  SetAttributes({{DisplaySurfaceAttributeEnum::ExcludeFromBlur,
+                  DisplaySurfaceAttributeValue{exclude_from_blur}}});
+}
+
+void DisplaySurfaceClient::SetBlurBehind(bool blur_behind) {
+  SetAttributes({{DisplaySurfaceAttributeEnum::BlurBehind,
+                  DisplaySurfaceAttributeValue{blur_behind}}});
+}
+
+void DisplaySurfaceClient::SetAttributes(
+    const DisplaySurfaceAttributes& attributes) {
+  Status<int> status =
+      InvokeRemoteMethod<DisplayRPC::SetAttributes>(attributes);
+  if (!status) {
+    ALOGE(
+        "DisplaySurfaceClient::SetAttributes: Failed to set display surface "
+        "attributes: %s",
+        status.GetErrorMessage().c_str());
+    return;
+  }
+
+  // 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 DisplaySurfaceAttributeEnum::Visible:
+        invalid_value =
+            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
+        break;
+      case DisplaySurfaceAttributeEnum::ZOrder:
+        invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
+        break;
+      case DisplaySurfaceAttributeEnum::ExcludeFromBlur:
+        invalid_value =
+            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &exclude_from_blur_);
+        break;
+      case DisplaySurfaceAttributeEnum::BlurBehind:
+        invalid_value =
+            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &blur_behind_);
+        break;
+    }
+
+    if (invalid_value) {
+      ALOGW(
+          "DisplaySurfaceClient::SetAttributes: Failed to set display "
+          "surface attribute '%s' because of incompatible type: %d",
+          DisplaySurfaceAttributeEnum::ToString(key).c_str(), variant->index());
+    }
+  }
+}
+
+std::shared_ptr<ProducerQueue> DisplaySurfaceClient::GetProducerQueue() {
+  if (producer_queue_ == nullptr) {
+    // Create producer queue through DisplayRPC
+    auto status = InvokeRemoteMethod<DisplayRPC::CreateBufferQueue>();
+    if (!status) {
+      ALOGE(
+          "DisplaySurfaceClient::GetProducerQueue: failed to create producer "
+          "queue: %s",
+          status.GetErrorMessage().c_str());
+      return nullptr;
+    }
+
+    producer_queue_ = ProducerQueue::Import(status.take());
+  }
+  return producer_queue_;
+}
+
+volatile DisplaySurfaceMetadata* DisplaySurfaceClient::GetMetadataBufferPtr() {
+  if (!mapped_metadata_buffer_) {
+    if (auto buffer_producer = GetMetadataBuffer()) {
+      void* addr = nullptr;
+      const int ret = buffer_producer->GetBlobReadWritePointer(
+          sizeof(DisplaySurfaceMetadata), &addr);
+      if (ret < 0) {
+        ALOGE(
+            "DisplaySurfaceClient::GetMetadataBufferPtr: Failed to map surface "
+            "metadata: %s",
+            strerror(-ret));
+        return nullptr;
+      }
+      mapped_metadata_buffer_ = static_cast<DisplaySurfaceMetadata*>(addr);
+    }
+  }
+
+  return mapped_metadata_buffer_;
+}
+
+LocalChannelHandle DisplaySurfaceClient::CreateVideoMeshSurface() {
+  auto status = InvokeRemoteMethod<DisplayRPC::CreateVideoMeshSurface>();
+  if (!status) {
+    ALOGE(
+        "DisplaySurfaceClient::CreateVideoMeshSurface: Failed to create "
+        "video mesh surface: %s",
+        status.GetErrorMessage().c_str());
+  }
+  return status.take();
+}
+
+DisplayClient::DisplayClient(int* error)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+               DisplayRPC::kClientPath),
+           kInfiniteTimeout) {
+  if (error)
+    *error = Client::error();
+}
+
+int DisplayClient::GetDisplayMetrics(SystemDisplayMetrics* metrics) {
+  auto status = InvokeRemoteMethod<DisplayRPC::GetMetrics>();
+  if (!status) {
+    ALOGE("DisplayClient::GetDisplayMetrics: Failed to get metrics: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *metrics = status.get();
+  return 0;
+}
+
+pdx::Status<void> DisplayClient::SetViewerParams(
+    const ViewerParams& viewer_params) {
+  auto status = InvokeRemoteMethod<DisplayRPC::SetViewerParams>(viewer_params);
+  if (!status) {
+    ALOGE("DisplayClient::SetViewerParams: Failed to set viewer params: %s",
+          status.GetErrorMessage().c_str());
+  }
+  return status;
+}
+
+int DisplayClient::GetLastFrameEdsTransform(LateLatchOutput* ll_out) {
+  auto status = InvokeRemoteMethod<DisplayRPC::GetEdsCapture>();
+  if (!status) {
+    ALOGE(
+        "DisplayClient::GetLastFrameLateLatch: Failed to get most recent late"
+        " latch: %s",
+        status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  if (status.get().size() != sizeof(LateLatchOutput)) {
+    ALOGE(
+        "DisplayClient::GetLastFrameLateLatch: Error expected to receive %zu "
+        "bytes but received %zu",
+        sizeof(LateLatchOutput), status.get().size());
+    return -EIO;
+  }
+
+  *ll_out = *reinterpret_cast<const LateLatchOutput*>(status.get().data());
+  return 0;
+}
+
+std::unique_ptr<DisplaySurfaceClient> DisplayClient::CreateDisplaySurface(
+    int width, int height, int format, int usage, int flags) {
+  return DisplaySurfaceClient::Create(width, height, format, usage, flags);
+}
+
+std::unique_ptr<IonBuffer> DisplayClient::GetNamedBuffer(
+    const std::string& name) {
+  auto status = InvokeRemoteMethod<DisplayRPC::GetNamedBuffer>(name);
+  if (!status) {
+    ALOGE(
+        "DisplayClient::GetNamedBuffer: Failed to get pose buffer. name=%s, "
+        "error=%s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    return nullptr;
+  }
+
+  auto ion_buffer = std::make_unique<IonBuffer>();
+  status.take().Import(ion_buffer.get());
+  return ion_buffer;
+}
+
+bool DisplayClient::IsVrAppRunning() {
+  auto status = InvokeRemoteMethod<DisplayRPC::IsVrAppRunning>();
+  if (!status)
+    return 0;
+  return static_cast<bool>(status.get());
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client_impl.cpp b/libs/vr/libdisplay/display_manager_client_impl.cpp
new file mode 100644
index 0000000..44b3c4b
--- /dev/null
+++ b/libs/vr/libdisplay/display_manager_client_impl.cpp
@@ -0,0 +1,53 @@
+#include "include/private/dvr/display_manager_client_impl.h"
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/display_rpc.h>
+#include <utils/Log.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+DisplayManagerClient::DisplayManagerClient()
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DisplayManagerRPC::kClientPath)) {}
+
+DisplayManagerClient::~DisplayManagerClient() {}
+
+int DisplayManagerClient::GetSurfaceList(
+    std::vector<DisplaySurfaceInfo>* surface_list) {
+  auto status = InvokeRemoteMethod<DisplayManagerRPC::GetSurfaceList>();
+  if (!status) {
+    ALOGE(
+        "DisplayManagerClient::GetSurfaceList: Failed to get surface info: %s",
+        status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *surface_list = status.take();
+  return 0;
+}
+
+std::unique_ptr<IonBuffer> DisplayManagerClient::SetupNamedBuffer(
+    const std::string& name, size_t size, uint64_t producer_usage,
+    uint64_t consumer_usage) {
+  auto status = InvokeRemoteMethod<DisplayManagerRPC::SetupNamedBuffer>(
+      name, size, producer_usage, consumer_usage);
+  if (!status) {
+    ALOGE(
+        "DisplayManagerClient::SetupNamedBuffer: Failed to create the named "
+        "buffer: name=%s, error=%s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    return {};
+  }
+
+  auto ion_buffer = std::make_unique<IonBuffer>();
+  status.take().Import(ion_buffer.get());
+  return ion_buffer;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/display_rpc.cpp b/libs/vr/libdisplay/display_rpc.cpp
new file mode 100644
index 0000000..f5693bd
--- /dev/null
+++ b/libs/vr/libdisplay/display_rpc.cpp
@@ -0,0 +1,12 @@
+#include "include/private/dvr/display_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char DisplayRPC::kClientPath[];
+constexpr char DisplayManagerRPC::kClientPath[];
+constexpr char DisplayScreenshotRPC::kClientPath[];
+constexpr char DisplayVSyncRPC::kClientPath[];
+
+} // namespace dvr
+} // namespace android
diff --git a/libs/vr/libdisplay/dummy_native_window.cpp b/libs/vr/libdisplay/dummy_native_window.cpp
new file mode 100644
index 0000000..4628b8e
--- /dev/null
+++ b/libs/vr/libdisplay/dummy_native_window.cpp
@@ -0,0 +1,80 @@
+#include "include/private/dvr/dummy_native_window.h"
+
+#include <utils/Errors.h>
+
+namespace {
+// Dummy functions required for an ANativeWindow Implementation.
+int F1(struct ANativeWindow*, int) { return 0; }
+int F2(struct ANativeWindow*, struct ANativeWindowBuffer**) { return 0; }
+int F3(struct ANativeWindow*, struct ANativeWindowBuffer*) { return 0; }
+int F4(struct ANativeWindow*, struct ANativeWindowBuffer**, int*) { return 0; }
+int F5(struct ANativeWindow*, struct ANativeWindowBuffer*, int) { return 0; }
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+DummyNativeWindow::DummyNativeWindow() {
+  ANativeWindow::setSwapInterval = F1;
+  ANativeWindow::dequeueBuffer = F4;
+  ANativeWindow::cancelBuffer = F5;
+  ANativeWindow::queueBuffer = F5;
+  ANativeWindow::query = Query;
+  ANativeWindow::perform = Perform;
+
+  ANativeWindow::dequeueBuffer_DEPRECATED = F2;
+  ANativeWindow::cancelBuffer_DEPRECATED = F3;
+  ANativeWindow::lockBuffer_DEPRECATED = F3;
+  ANativeWindow::queueBuffer_DEPRECATED = F3;
+}
+
+int DummyNativeWindow::Query(const ANativeWindow*, int what, int* value) {
+  switch (what) {
+    // This must be 1 in order for eglCreateWindowSurface to not trigger an
+    // error
+    case NATIVE_WINDOW_IS_VALID:
+      *value = 1;
+      return NO_ERROR;
+    case NATIVE_WINDOW_WIDTH:
+    case NATIVE_WINDOW_HEIGHT:
+    case NATIVE_WINDOW_FORMAT:
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+    case NATIVE_WINDOW_CONCRETE_TYPE:
+    case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+    case NATIVE_WINDOW_DEFAULT_WIDTH:
+    case NATIVE_WINDOW_DEFAULT_HEIGHT:
+    case NATIVE_WINDOW_TRANSFORM_HINT:
+      *value = 0;
+      return NO_ERROR;
+  }
+
+  *value = 0;
+  return BAD_VALUE;
+}
+
+int DummyNativeWindow::Perform(ANativeWindow*, int operation, ...) {
+  switch (operation) {
+    case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
+    case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+    case NATIVE_WINDOW_SET_USAGE:
+    case NATIVE_WINDOW_CONNECT:
+    case NATIVE_WINDOW_DISCONNECT:
+    case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+    case NATIVE_WINDOW_API_CONNECT:
+    case NATIVE_WINDOW_API_DISCONNECT:
+    case NATIVE_WINDOW_SET_BUFFER_COUNT:
+    case NATIVE_WINDOW_SET_BUFFERS_DATASPACE:
+    case NATIVE_WINDOW_SET_SCALING_MODE:
+      return NO_ERROR;
+    case NATIVE_WINDOW_LOCK:
+    case NATIVE_WINDOW_UNLOCK_AND_POST:
+    case NATIVE_WINDOW_SET_CROP:
+    case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+      return INVALID_OPERATION;
+  }
+  return NAME_NOT_FOUND;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/frame_history.cpp b/libs/vr/libdisplay/frame_history.cpp
new file mode 100644
index 0000000..154afbe
--- /dev/null
+++ b/libs/vr/libdisplay/frame_history.cpp
@@ -0,0 +1,147 @@
+#include <private/dvr/frame_history.h>
+
+#include <errno.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/sync_util.h>
+
+using android::pdx::LocalHandle;
+
+constexpr int kNumFramesToUseForSchedulePrediction = 10;
+constexpr int kDefaultVsyncIntervalPrediction = 1;
+constexpr int kMaxVsyncIntervalPrediction = 4;
+constexpr int kDefaultPendingFrameBufferSize = 10;
+
+namespace android {
+namespace dvr {
+
+FrameHistory::PendingFrame::PendingFrame()
+    : start_ns(0), scheduled_vsync(0), scheduled_finish_ns(0) {}
+
+FrameHistory::PendingFrame::PendingFrame(int64_t start_ns,
+                                         uint32_t scheduled_vsync,
+                                         int64_t scheduled_finish_ns,
+                                         LocalHandle&& fence)
+    : start_ns(start_ns), scheduled_vsync(scheduled_vsync),
+      scheduled_finish_ns(scheduled_finish_ns), fence(std::move(fence)) {}
+
+FrameHistory::FrameHistory() : FrameHistory(kDefaultPendingFrameBufferSize) {}
+
+FrameHistory::FrameHistory(int pending_frame_buffer_size)
+    : pending_frames_(pending_frame_buffer_size),
+      finished_frames_(pending_frame_buffer_size),
+      frame_duration_history_(kNumFramesToUseForSchedulePrediction) {}
+
+void FrameHistory::Reset(int pending_frame_buffer_size) {
+  pending_frames_.Reset(pending_frame_buffer_size);
+  finished_frames_.Reset(pending_frame_buffer_size);
+  frame_duration_history_.Clear();
+}
+
+void FrameHistory::OnFrameStart(uint32_t scheduled_vsync,
+                                int64_t scheduled_finish_ns) {
+  if (!pending_frames_.IsEmpty() && !pending_frames_.Back().fence) {
+    // If we don't have a fence set for the previous frame it's because
+    // OnFrameStart() was called twice in a row with no OnFrameSubmit() call. In
+    // that case throw out the pending frame data for the last frame.
+    pending_frames_.PopBack();
+  }
+
+  if (pending_frames_.IsFull()) {
+    ALOGW("Pending frames buffer is full. Discarding pending frame data.");
+  }
+
+  pending_frames_.Append(PendingFrame(GetSystemClockNs(), scheduled_vsync,
+                                      scheduled_finish_ns, LocalHandle()));
+}
+
+void FrameHistory::OnFrameSubmit(LocalHandle&& fence) {
+  // Add the fence to the previous frame data in pending_frames so we can
+  // track when it finishes.
+  if (!pending_frames_.IsEmpty() && !pending_frames_.Back().fence) {
+    if (fence && pending_frames_.Back().scheduled_vsync != UINT32_MAX)
+      pending_frames_.Back().fence = std::move(fence);
+    else
+      pending_frames_.PopBack();
+  }
+}
+
+void FrameHistory::CheckForFinishedFrames() {
+  if (pending_frames_.IsEmpty())
+    return;
+
+  android::dvr::FenceInfoBuffer fence_info_buffer;
+  while (!pending_frames_.IsEmpty()) {
+    const auto& pending_frame = pending_frames_.Front();
+    if (!pending_frame.fence) {
+      // The frame hasn't been submitted yet, so there's nothing more to do
+      break;
+    }
+
+    int64_t fence_signaled_time = -1;
+    int fence = pending_frame.fence.Get();
+    int sync_result = sync_wait(fence, 0);
+    if (sync_result == 0) {
+      int fence_signaled_result =
+          GetFenceSignaledTimestamp(fence, &fence_info_buffer,
+                                    &fence_signaled_time);
+      if (fence_signaled_result < 0) {
+        ALOGE("Failed getting signaled timestamp from fence");
+      } else {
+        // The frame is finished. Record the duration and move the frame data
+        // from pending_frames_ to finished_frames_.
+        DvrFrameScheduleResult schedule_result = {};
+        schedule_result.vsync_count = pending_frame.scheduled_vsync;
+        schedule_result.scheduled_frame_finish_ns =
+            pending_frame.scheduled_finish_ns;
+        schedule_result.frame_finish_offset_ns =
+            fence_signaled_time - pending_frame.scheduled_finish_ns;
+        finished_frames_.Append(schedule_result);
+        frame_duration_history_.Append(
+            fence_signaled_time - pending_frame.start_ns);
+      }
+      pending_frames_.PopFront();
+    } else {
+      if (errno != ETIME) {
+        ALOGE("sync_wait on frame fence failed. fence=%d errno=%d (%s).",
+              fence, errno, strerror(errno));
+      }
+      break;
+    }
+  }
+}
+
+int FrameHistory::PredictNextFrameVsyncInterval(int64_t vsync_period_ns) const {
+  if (frame_duration_history_.IsEmpty())
+    return kDefaultVsyncIntervalPrediction;
+
+  double total = 0;
+  for (size_t i = 0; i < frame_duration_history_.GetSize(); ++i)
+    total += frame_duration_history_.Get(i);
+  double avg_duration = total / frame_duration_history_.GetSize();
+
+  return std::min(kMaxVsyncIntervalPrediction,
+                  static_cast<int>(avg_duration / vsync_period_ns) + 1);
+}
+
+int FrameHistory::GetPreviousFrameResults(DvrFrameScheduleResult* results,
+                                          int in_result_count) {
+  int out_result_count =
+      std::min(in_result_count, static_cast<int>(finished_frames_.GetSize()));
+  for (int i = 0; i < out_result_count; ++i) {
+    results[i] = finished_frames_.Get(0);
+    finished_frames_.PopFront();
+  }
+  return out_result_count;
+}
+
+uint32_t FrameHistory::GetCurrentFrameVsync() const {
+  return pending_frames_.IsEmpty() ?
+      UINT32_MAX : pending_frames_.Back().scheduled_vsync;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/gl_fenced_flush.cpp b/libs/vr/libdisplay/gl_fenced_flush.cpp
new file mode 100644
index 0000000..c70d554
--- /dev/null
+++ b/libs/vr/libdisplay/gl_fenced_flush.cpp
@@ -0,0 +1,39 @@
+#include "include/private/dvr/gl_fenced_flush.h"
+
+#include <EGL/eglext.h>
+#include <GLES3/gl31.h>
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <log/log.h>
+
+using android::pdx::LocalHandle;
+
+namespace android {
+namespace dvr {
+
+LocalHandle CreateGLSyncAndFlush(EGLDisplay display) {
+  ATRACE_NAME("CreateGLSyncAndFlush");
+
+  EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
+                      EGL_NO_NATIVE_FENCE_FD_ANDROID, EGL_NONE};
+  EGLSyncKHR sync_point =
+      eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+  glFlush();
+  if (sync_point == EGL_NO_SYNC_KHR) {
+    ALOGE("sync_point == EGL_NO_SYNC_KHR");
+    return LocalHandle();
+  }
+  EGLint fence_fd = eglDupNativeFenceFDANDROID(display, sync_point);
+  eglDestroySyncKHR(display, sync_point);
+
+  if (fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+    ALOGE("fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID");
+    return LocalHandle();
+  }
+  return LocalHandle(fence_fd);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/graphics.cpp b/libs/vr/libdisplay/graphics.cpp
new file mode 100644
index 0000000..2abdf8e
--- /dev/null
+++ b/libs/vr/libdisplay/graphics.cpp
@@ -0,0 +1,1576 @@
+#include <dvr/graphics.h>
+
+#include <inttypes.h>
+#include <sys/timerfd.h>
+#include <array>
+#include <vector>
+
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR 1
+#endif
+#include <vulkan/vulkan.h>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/frame_history.h>
+#include <private/dvr/gl_fenced_flush.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+#include <private/dvr/graphics_private.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/native_buffer_queue.h>
+#include <private/dvr/sensor_constants.h>
+#include <private/dvr/video_mesh_surface_client.h>
+#include <private/dvr/vsync_client.h>
+#include <private/dvr/platform_defines.h>
+
+#include <android/native_window.h>
+
+#ifndef EGL_CONTEXT_MAJOR_VERSION
+#define EGL_CONTEXT_MAJOR_VERSION 0x3098
+#define EGL_CONTEXT_MINOR_VERSION 0x30FB
+#endif
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+
+using android::dvr::DisplaySurfaceAttributeEnum;
+using android::dvr::DisplaySurfaceAttributeValue;
+
+namespace {
+
+// TODO(urbanus): revisit once we have per-platform usage config in place.
+constexpr int kDefaultDisplaySurfaceUsage =
+    GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+    GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION;
+constexpr int kDefaultDisplaySurfaceFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+// TODO(alexst): revisit this count when HW encode is available for casting.
+constexpr int kDefaultBufferCount = 4;
+
+// Use with dvrBeginRenderFrame to disable EDS for the current frame.
+constexpr float32x4_t DVR_POSE_NO_EDS = {10.0f, 0.0f, 0.0f, 0.0f};
+
+// Use with dvrBeginRenderFrame to indicate that GPU late-latching is being used
+// for determining the render pose.
+constexpr float32x4_t DVR_POSE_LATE_LATCH = {20.0f, 0.0f, 0.0f, 0.0f};
+
+#ifndef NDEBUG
+
+static const char* GetGlCallbackType(GLenum type) {
+  switch (type) {
+    case GL_DEBUG_TYPE_ERROR_KHR:
+      return "ERROR";
+    case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR:
+      return "DEPRECATED_BEHAVIOR";
+    case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR:
+      return "UNDEFINED_BEHAVIOR";
+    case GL_DEBUG_TYPE_PORTABILITY_KHR:
+      return "PORTABILITY";
+    case GL_DEBUG_TYPE_PERFORMANCE_KHR:
+      return "PERFORMANCE";
+    case GL_DEBUG_TYPE_OTHER_KHR:
+      return "OTHER";
+    default:
+      return "UNKNOWN";
+  }
+}
+
+static void on_gl_error(GLenum /*source*/, GLenum type, GLuint /*id*/,
+                        GLenum severity, GLsizei /*length*/,
+                        const char* message, const void* /*user_param*/) {
+  char msg[400];
+  snprintf(msg, sizeof(msg), "[" __FILE__ ":%u] GL %s: %s", __LINE__,
+           GetGlCallbackType(type), message);
+  switch (severity) {
+    case GL_DEBUG_SEVERITY_LOW_KHR:
+      ALOGI("%s", msg);
+      break;
+    case GL_DEBUG_SEVERITY_MEDIUM_KHR:
+      ALOGW("%s", msg);
+      break;
+    case GL_DEBUG_SEVERITY_HIGH_KHR:
+      ALOGE("%s", msg);
+      break;
+  }
+  fprintf(stderr, "%s\n", msg);
+}
+
+#endif
+
+int DvrToHalSurfaceFormat(int dvr_surface_format) {
+  switch (dvr_surface_format) {
+    case DVR_SURFACE_FORMAT_RGBA_8888:
+      return HAL_PIXEL_FORMAT_RGBA_8888;
+    case DVR_SURFACE_FORMAT_RGB_565:
+      return HAL_PIXEL_FORMAT_RGB_565;
+    default:
+      return HAL_PIXEL_FORMAT_RGBA_8888;
+  }
+}
+
+int SelectEGLConfig(EGLDisplay dpy, EGLint* attr, unsigned format,
+                    EGLConfig* config) {
+  std::array<EGLint, 4> desired_rgba;
+  switch (format) {
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+      desired_rgba = {{8, 8, 8, 8}};
+      break;
+    case HAL_PIXEL_FORMAT_RGB_565:
+      desired_rgba = {{5, 6, 5, 0}};
+      break;
+    default:
+      ALOGE("Unsupported framebuffer pixel format %d", format);
+      return -1;
+  }
+
+  EGLint max_configs = 0;
+  if (eglGetConfigs(dpy, NULL, 0, &max_configs) == EGL_FALSE) {
+    ALOGE("No EGL configurations available?!");
+    return -1;
+  }
+
+  std::vector<EGLConfig> configs(max_configs);
+
+  EGLint num_configs;
+  if (eglChooseConfig(dpy, attr, &configs[0], max_configs, &num_configs) ==
+      EGL_FALSE) {
+    ALOGE("eglChooseConfig failed");
+    return -1;
+  }
+
+  std::array<EGLint, 4> config_rgba;
+  for (int i = 0; i < num_configs; i++) {
+    eglGetConfigAttrib(dpy, configs[i], EGL_RED_SIZE, &config_rgba[0]);
+    eglGetConfigAttrib(dpy, configs[i], EGL_GREEN_SIZE, &config_rgba[1]);
+    eglGetConfigAttrib(dpy, configs[i], EGL_BLUE_SIZE, &config_rgba[2]);
+    eglGetConfigAttrib(dpy, configs[i], EGL_ALPHA_SIZE, &config_rgba[3]);
+    if (config_rgba == desired_rgba) {
+      *config = configs[i];
+      return 0;
+    }
+  }
+
+  ALOGE("Cannot find a matching EGL config");
+  return -1;
+}
+
+void DestroyEglContext(EGLDisplay egl_display, EGLContext* egl_context) {
+  if (*egl_context != EGL_NO_CONTEXT) {
+    eglDestroyContext(egl_display, *egl_context);
+    *egl_context = EGL_NO_CONTEXT;
+  }
+}
+
+// Perform internal initialization. A GL context must be bound to the current
+// thread.
+// @param internally_created_context True if we created and own the GL context,
+//        false if it was supplied by the application.
+// @return 0 if init was successful, or a negative error code on failure.
+int InitGl(bool internally_created_context) {
+  EGLDisplay egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  if (egl_display == EGL_NO_DISPLAY) {
+    ALOGE("eglGetDisplay failed");
+    return -EINVAL;
+  }
+
+  EGLContext egl_context = eglGetCurrentContext();
+  if (egl_context == EGL_NO_CONTEXT) {
+    ALOGE("No GL context bound");
+    return -EINVAL;
+  }
+
+  glGetError();  // Clear the error state
+  GLint major_version, minor_version;
+  glGetIntegerv(GL_MAJOR_VERSION, &major_version);
+  glGetIntegerv(GL_MINOR_VERSION, &minor_version);
+  if (glGetError() != GL_NO_ERROR) {
+    // GL_MAJOR_VERSION and GL_MINOR_VERSION were added in GLES 3. If we get an
+    // error querying them it's almost certainly because it's GLES 1 or 2.
+    ALOGE("Error getting GL version. Must be GLES 3.2 or greater.");
+    return -EINVAL;
+  }
+
+  if (major_version < 3 || (major_version == 3 && minor_version < 2)) {
+    ALOGE("Invalid GL version: %d.%d. Must be GLES 3.2 or greater.",
+          major_version, minor_version);
+    return -EINVAL;
+  }
+
+#ifndef NDEBUG
+  if (internally_created_context) {
+    // Enable verbose GL debug output.
+    glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
+    glDebugMessageCallbackKHR(on_gl_error, NULL);
+    GLuint unused_ids = 0;
+    glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0,
+                             &unused_ids, GL_TRUE);
+  }
+#else
+  (void)internally_created_context;
+#endif
+
+  load_gl_extensions();
+  return 0;
+}
+
+int CreateEglContext(EGLDisplay egl_display, DvrSurfaceParameter* parameters,
+                     EGLContext* egl_context) {
+  *egl_context = EGL_NO_CONTEXT;
+
+  EGLint major, minor;
+  if (!eglInitialize(egl_display, &major, &minor)) {
+    ALOGE("Failed to initialize EGL");
+    return -ENXIO;
+  }
+
+  ALOGI("EGL version: %d.%d\n", major, minor);
+
+  int buffer_format = kDefaultDisplaySurfaceFormat;
+
+  for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+    switch (p->key) {
+      case DVR_SURFACE_PARAMETER_FORMAT_IN:
+        buffer_format = DvrToHalSurfaceFormat(p->value);
+        break;
+    }
+  }
+
+  EGLint config_attrs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+                           EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE};
+  EGLConfig config = {0};
+
+  int ret = SelectEGLConfig(egl_display, config_attrs, buffer_format, &config);
+  if (ret < 0)
+    return ret;
+
+  ALOGI("EGL SelectEGLConfig ok.\n");
+
+  EGLint context_attrs[] = {EGL_CONTEXT_MAJOR_VERSION,
+                            3,
+                            EGL_CONTEXT_MINOR_VERSION,
+                            2,
+#ifndef NDEBUG
+                            EGL_CONTEXT_FLAGS_KHR,
+                            EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
+#endif
+                            EGL_NONE};
+
+  *egl_context =
+      eglCreateContext(egl_display, config, EGL_NO_CONTEXT, context_attrs);
+  if (*egl_context == EGL_NO_CONTEXT) {
+    ALOGE("eglCreateContext failed");
+    return -ENXIO;
+  }
+
+  ALOGI("eglCreateContext ok.\n");
+
+  if (!eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                      *egl_context)) {
+    ALOGE("eglMakeCurrent failed");
+    DestroyEglContext(egl_display, egl_context);
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+}  // anonymous namespace
+
+// TODO(hendrikw): When we remove the calls to this in native_window.cpp, move
+// this back into the anonymous namespace
+std::shared_ptr<android::dvr::DisplaySurfaceClient> CreateDisplaySurfaceClient(
+    struct DvrSurfaceParameter* parameters,
+    /*out*/ android::dvr::SystemDisplayMetrics* metrics) {
+  auto client = android::dvr::DisplayClient::Create();
+  if (!client) {
+    ALOGE("Failed to create display client!");
+    return nullptr;
+  }
+
+  const int ret = client->GetDisplayMetrics(metrics);
+  if (ret < 0) {
+    ALOGE("Failed to get display metrics: %s", strerror(-ret));
+    return nullptr;
+  }
+
+  // Parameters that may be modified by the parameters array. Some of these are
+  // here for future expansion.
+  int request_width = -1;
+  int request_height = -1;
+  int request_flags = 0;
+  bool disable_distortion = false;
+  bool disable_stabilization = false;
+  bool disable_cac = false;
+  bool request_visible = true;
+  bool vertical_flip = false;
+  int request_z_order = 0;
+  bool request_exclude_from_blur = false;
+  bool request_blur_behind = true;
+  int request_format = kDefaultDisplaySurfaceFormat;
+  int request_usage = kDefaultDisplaySurfaceUsage;
+  int geometry_type = DVR_SURFACE_GEOMETRY_SINGLE;
+
+  // Handle parameter inputs.
+  for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+    switch (p->key) {
+      case DVR_SURFACE_PARAMETER_WIDTH_IN:
+        request_width = p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_HEIGHT_IN:
+        request_height = p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_DISABLE_DISTORTION_IN:
+        disable_distortion = !!p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_DISABLE_STABILIZATION_IN:
+        disable_stabilization = !!p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_DISABLE_CAC_IN:
+        disable_cac = !!p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_VISIBLE_IN:
+        request_visible = !!p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_Z_ORDER_IN:
+        request_z_order = p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_EXCLUDE_FROM_BLUR_IN:
+        request_exclude_from_blur = !!p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_BLUR_BEHIND_IN:
+        request_blur_behind = !!p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_VERTICAL_FLIP_IN:
+        vertical_flip = !!p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_GEOMETRY_IN:
+        geometry_type = p->value;
+        break;
+      case DVR_SURFACE_PARAMETER_FORMAT_IN:
+        request_format = DvrToHalSurfaceFormat(p->value);
+        break;
+      case DVR_SURFACE_PARAMETER_ENABLE_LATE_LATCH_IN:
+      case DVR_SURFACE_PARAMETER_CREATE_GL_CONTEXT_IN:
+      case DVR_SURFACE_PARAMETER_DISPLAY_WIDTH_OUT:
+      case DVR_SURFACE_PARAMETER_DISPLAY_HEIGHT_OUT:
+      case DVR_SURFACE_PARAMETER_SURFACE_WIDTH_OUT:
+      case DVR_SURFACE_PARAMETER_SURFACE_HEIGHT_OUT:
+      case DVR_SURFACE_PARAMETER_INTER_LENS_METERS_OUT:
+      case DVR_SURFACE_PARAMETER_LEFT_FOV_LRBT_OUT:
+      case DVR_SURFACE_PARAMETER_RIGHT_FOV_LRBT_OUT:
+      case DVR_SURFACE_PARAMETER_VSYNC_PERIOD_OUT:
+      case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_TYPE_OUT:
+      case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_ID_OUT:
+      case DVR_SURFACE_PARAMETER_GRAPHICS_API_IN:
+      case DVR_SURFACE_PARAMETER_VK_INSTANCE_IN:
+      case DVR_SURFACE_PARAMETER_VK_PHYSICAL_DEVICE_IN:
+      case DVR_SURFACE_PARAMETER_VK_DEVICE_IN:
+      case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_IN:
+      case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_FAMILY_IN:
+      case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_COUNT_OUT:
+      case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_FORMAT_OUT:
+        break;
+      default:
+        ALOGE("Invalid display surface parameter: key=%d value=%" PRId64,
+              p->key, p->value);
+        return nullptr;
+    }
+  }
+
+  request_flags |= disable_distortion
+                       ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION
+                       : 0;
+  request_flags |=
+      disable_stabilization ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS : 0;
+  request_flags |=
+      disable_cac ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC : 0;
+  request_flags |= vertical_flip ? DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP : 0;
+  request_flags |= (geometry_type == DVR_SURFACE_GEOMETRY_SEPARATE_2)
+                       ? DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2
+                       : 0;
+
+  if (request_width == -1) {
+    request_width = disable_distortion ? metrics->display_native_width
+                                       : metrics->distorted_width;
+    if (!disable_distortion &&
+        geometry_type == DVR_SURFACE_GEOMETRY_SEPARATE_2) {
+      // The metrics always return the single wide buffer resolution.
+      // When split between eyes, we need to halve the width of the surface.
+      request_width /= 2;
+    }
+  }
+  if (request_height == -1) {
+    request_height = disable_distortion ? metrics->display_native_height
+                                        : metrics->distorted_height;
+  }
+
+  std::shared_ptr<android::dvr::DisplaySurfaceClient> surface =
+      client->CreateDisplaySurface(request_width, request_height,
+                                   request_format, request_usage,
+                                   request_flags);
+  surface->SetAttributes(
+      {{DisplaySurfaceAttributeEnum::Visible,
+        DisplaySurfaceAttributeValue{request_visible}},
+       {DisplaySurfaceAttributeEnum::ZOrder,
+        DisplaySurfaceAttributeValue{request_z_order}},
+       {DisplaySurfaceAttributeEnum::ExcludeFromBlur,
+        DisplaySurfaceAttributeValue{request_exclude_from_blur}},
+       {DisplaySurfaceAttributeEnum::BlurBehind,
+        DisplaySurfaceAttributeValue{request_blur_behind}}});
+
+  // Handle parameter output requests down here so we can return surface info.
+  for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+    switch (p->key) {
+      case DVR_SURFACE_PARAMETER_DISPLAY_WIDTH_OUT:
+        *static_cast<int32_t*>(p->value_out) = metrics->display_native_width;
+        break;
+      case DVR_SURFACE_PARAMETER_DISPLAY_HEIGHT_OUT:
+        *static_cast<int32_t*>(p->value_out) = metrics->display_native_height;
+        break;
+      case DVR_SURFACE_PARAMETER_SURFACE_WIDTH_OUT:
+        *static_cast<int32_t*>(p->value_out) = surface->width();
+        break;
+      case DVR_SURFACE_PARAMETER_SURFACE_HEIGHT_OUT:
+        *static_cast<int32_t*>(p->value_out) = surface->height();
+        break;
+      case DVR_SURFACE_PARAMETER_INTER_LENS_METERS_OUT:
+        *static_cast<float*>(p->value_out) = metrics->inter_lens_distance_m;
+        break;
+      case DVR_SURFACE_PARAMETER_LEFT_FOV_LRBT_OUT:
+        for (int i = 0; i < 4; ++i) {
+          float* float_values_out = static_cast<float*>(p->value_out);
+          float_values_out[i] = metrics->left_fov_lrbt[i];
+        }
+        break;
+      case DVR_SURFACE_PARAMETER_RIGHT_FOV_LRBT_OUT:
+        for (int i = 0; i < 4; ++i) {
+          float* float_values_out = static_cast<float*>(p->value_out);
+          float_values_out[i] = metrics->right_fov_lrbt[i];
+        }
+        break;
+      case DVR_SURFACE_PARAMETER_VSYNC_PERIOD_OUT:
+        *static_cast<uint64_t*>(p->value_out) = metrics->vsync_period_ns;
+        break;
+      default:
+        break;
+    }
+  }
+
+  return surface;
+}
+
+extern "C" int dvrGetNativeDisplayDimensions(int* native_width,
+                                             int* native_height) {
+  int error = 0;
+  auto client = android::dvr::DisplayClient::Create(&error);
+  if (!client) {
+    ALOGE("Failed to create display client!");
+    return error;
+  }
+
+  android::dvr::SystemDisplayMetrics metrics;
+  const int ret = client->GetDisplayMetrics(&metrics);
+
+  if (ret != 0) {
+    ALOGE("Failed to get display metrics!");
+    return ret;
+  }
+
+  *native_width = static_cast<int>(metrics.display_native_width);
+  *native_height = static_cast<int>(metrics.display_native_height);
+  return 0;
+}
+
+struct DvrGraphicsContext : public android::ANativeObjectBase<
+                                ANativeWindow, DvrGraphicsContext,
+                                android::LightRefBase<DvrGraphicsContext>> {
+ public:
+  DvrGraphicsContext();
+  ~DvrGraphicsContext();
+
+  int graphics_api;  // DVR_SURFACE_GRAPHICS_API_*
+
+  // GL specific members.
+  struct {
+    EGLDisplay egl_display;
+    EGLContext egl_context;
+    bool owns_egl_context;
+    GLuint texture_id[kSurfaceViewMaxCount];
+    int texture_count;
+    GLenum texture_target_type;
+  } gl;
+
+  // VK specific members
+  struct {
+    // These objects are passed in by the application, and are NOT owned
+    // by the context.
+    VkInstance instance;
+    VkPhysicalDevice physical_device;
+    VkDevice device;
+    VkQueue present_queue;
+    uint32_t present_queue_family;
+    const VkAllocationCallbacks* allocation_callbacks;
+    // These objects are owned by the context.
+    ANativeWindow* window;
+    VkSurfaceKHR surface;
+    VkSwapchainKHR swapchain;
+    std::vector<VkImage> swapchain_images;
+    std::vector<VkImageView> swapchain_image_views;
+  } vk;
+
+  // Display surface, metrics, and buffer management members.
+  std::shared_ptr<android::dvr::DisplaySurfaceClient> display_surface;
+  android::dvr::SystemDisplayMetrics display_metrics;
+  std::unique_ptr<android::dvr::NativeBufferQueue> buffer_queue;
+  android::dvr::NativeBufferProducer* current_buffer;
+  bool buffer_already_posted;
+
+  // Synchronization members.
+  std::unique_ptr<android::dvr::VSyncClient> vsync_client;
+  LocalHandle timerfd;
+
+  android::dvr::FrameHistory frame_history;
+
+  // Mapped surface metadata (ie: for pose delivery with presented frames).
+  volatile android::dvr::DisplaySurfaceMetadata* surface_metadata;
+
+  // LateLatch support.
+  std::unique_ptr<android::dvr::LateLatch> late_latch;
+
+  // Video mesh support.
+  std::vector<std::shared_ptr<android::dvr::VideoMeshSurfaceClient>>
+      video_mesh_surfaces;
+
+ private:
+  // ANativeWindow function implementations
+  std::mutex lock_;
+  int Post(android::dvr::NativeBufferProducer* buffer, int fence_fd);
+  static int SetSwapInterval(ANativeWindow* window, int interval);
+  static int DequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                           int* fence_fd);
+  static int QueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                         int fence_fd);
+  static int CancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                          int fence_fd);
+  static int Query(const ANativeWindow* window, int what, int* value);
+  static int Perform(ANativeWindow* window, int operation, ...);
+  static int DequeueBuffer_DEPRECATED(ANativeWindow* window,
+                                      ANativeWindowBuffer** buffer);
+  static int CancelBuffer_DEPRECATED(ANativeWindow* window,
+                                     ANativeWindowBuffer* buffer);
+  static int QueueBuffer_DEPRECATED(ANativeWindow* window,
+                                    ANativeWindowBuffer* buffer);
+  static int LockBuffer_DEPRECATED(ANativeWindow* window,
+                                   ANativeWindowBuffer* buffer);
+
+  DvrGraphicsContext(const DvrGraphicsContext&) = delete;
+  void operator=(const DvrGraphicsContext&) = delete;
+};
+
+DvrGraphicsContext::DvrGraphicsContext()
+    : graphics_api(DVR_GRAPHICS_API_GLES),
+      gl{},
+      vk{},
+      current_buffer(nullptr),
+      buffer_already_posted(false),
+      surface_metadata(nullptr) {
+  gl.egl_display = EGL_NO_DISPLAY;
+  gl.egl_context = EGL_NO_CONTEXT;
+  gl.owns_egl_context = true;
+  gl.texture_target_type = GL_TEXTURE_2D;
+
+  ANativeWindow::setSwapInterval = SetSwapInterval;
+  ANativeWindow::dequeueBuffer = DequeueBuffer;
+  ANativeWindow::cancelBuffer = CancelBuffer;
+  ANativeWindow::queueBuffer = QueueBuffer;
+  ANativeWindow::query = Query;
+  ANativeWindow::perform = Perform;
+
+  ANativeWindow::dequeueBuffer_DEPRECATED = DequeueBuffer_DEPRECATED;
+  ANativeWindow::cancelBuffer_DEPRECATED = CancelBuffer_DEPRECATED;
+  ANativeWindow::lockBuffer_DEPRECATED = LockBuffer_DEPRECATED;
+  ANativeWindow::queueBuffer_DEPRECATED = QueueBuffer_DEPRECATED;
+}
+
+DvrGraphicsContext::~DvrGraphicsContext() {
+  if (graphics_api == DVR_GRAPHICS_API_GLES) {
+    glDeleteTextures(gl.texture_count, gl.texture_id);
+    if (gl.owns_egl_context)
+      DestroyEglContext(gl.egl_display, &gl.egl_context);
+  } else if (graphics_api == DVR_GRAPHICS_API_VULKAN) {
+    if (vk.swapchain != VK_NULL_HANDLE) {
+      for (auto view : vk.swapchain_image_views) {
+        vkDestroyImageView(vk.device, view, vk.allocation_callbacks);
+      }
+      vkDestroySwapchainKHR(vk.device, vk.swapchain, vk.allocation_callbacks);
+      vkDestroySurfaceKHR(vk.instance, vk.surface, vk.allocation_callbacks);
+      delete vk.window;
+    }
+  }
+}
+
+int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters,
+                             DvrGraphicsContext** return_graphics_context) {
+  std::unique_ptr<DvrGraphicsContext> context(new DvrGraphicsContext);
+
+  // See whether we're using GL or Vulkan
+  for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+    switch (p->key) {
+      case DVR_SURFACE_PARAMETER_GRAPHICS_API_IN:
+        context->graphics_api = p->value;
+        break;
+    }
+  }
+
+  if (context->graphics_api == DVR_GRAPHICS_API_GLES) {
+    context->gl.egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (context->gl.egl_display == EGL_NO_DISPLAY) {
+      ALOGE("eglGetDisplay failed");
+      return -ENXIO;
+    }
+
+    // See if we should create a GL context
+    for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+      switch (p->key) {
+        case DVR_SURFACE_PARAMETER_CREATE_GL_CONTEXT_IN:
+          context->gl.owns_egl_context = p->value != 0;
+          break;
+      }
+    }
+
+    if (context->gl.owns_egl_context) {
+      int ret = CreateEglContext(context->gl.egl_display, parameters,
+                                 &context->gl.egl_context);
+      if (ret < 0)
+        return ret;
+    } else {
+      context->gl.egl_context = eglGetCurrentContext();
+    }
+
+    int ret = InitGl(context->gl.owns_egl_context);
+    if (ret < 0)
+      return ret;
+  } else if (context->graphics_api == DVR_GRAPHICS_API_VULKAN) {
+    for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+      switch (p->key) {
+        case DVR_SURFACE_PARAMETER_VK_INSTANCE_IN:
+          context->vk.instance = reinterpret_cast<VkInstance>(p->value);
+          break;
+        case DVR_SURFACE_PARAMETER_VK_PHYSICAL_DEVICE_IN:
+          context->vk.physical_device =
+              reinterpret_cast<VkPhysicalDevice>(p->value);
+          break;
+        case DVR_SURFACE_PARAMETER_VK_DEVICE_IN:
+          context->vk.device = reinterpret_cast<VkDevice>(p->value);
+          break;
+        case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_IN:
+          context->vk.present_queue = reinterpret_cast<VkQueue>(p->value);
+          break;
+        case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_FAMILY_IN:
+          context->vk.present_queue_family = static_cast<uint32_t>(p->value);
+          break;
+      }
+    }
+  } else {
+    ALOGE("Error: invalid graphics API type");
+    return -EINVAL;
+  }
+
+  context->display_surface =
+      CreateDisplaySurfaceClient(parameters, &context->display_metrics);
+  if (!context->display_surface) {
+    ALOGE("Error: failed to create display surface client");
+    return -ECOMM;
+  }
+
+  context->buffer_queue.reset(new android::dvr::NativeBufferQueue(
+      context->gl.egl_display, context->display_surface, kDefaultBufferCount));
+
+  // The way the call sequence works we need 1 more than the buffer queue
+  // capacity to store data for all pending frames
+  context->frame_history.Reset(context->buffer_queue->GetQueueCapacity() + 1);
+
+  context->vsync_client = android::dvr::VSyncClient::Create();
+  if (!context->vsync_client) {
+    ALOGE("Error: failed to create vsync client");
+    return -ECOMM;
+  }
+
+  context->timerfd.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
+  if (!context->timerfd) {
+    ALOGE("Error: timerfd_create failed because: %s", strerror(errno));
+    return -EPERM;
+  }
+
+  context->surface_metadata = context->display_surface->GetMetadataBufferPtr();
+  if (!context->surface_metadata) {
+    ALOGE("Error: surface metadata allocation failed");
+    return -ENOMEM;
+  }
+
+  ALOGI("buffer: %d x %d\n", context->display_surface->width(),
+        context->display_surface->height());
+
+  if (context->graphics_api == DVR_GRAPHICS_API_GLES) {
+    context->gl.texture_count = (context->display_surface->flags() &
+                                 DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2)
+                                    ? 2
+                                    : 1;
+
+    // Create the GL textures.
+    glGenTextures(context->gl.texture_count, context->gl.texture_id);
+
+    // We must make sure that we have at least one buffer allocated at this time
+    // so that anyone who tries to bind an FBO to context->texture_id
+    // will not get an incomplete buffer.
+    context->current_buffer = context->buffer_queue->Dequeue();
+    LOG_ALWAYS_FATAL_IF(context->gl.texture_count !=
+                        context->current_buffer->buffer()->slice_count());
+    for (int i = 0; i < context->gl.texture_count; ++i) {
+      glBindTexture(context->gl.texture_target_type, context->gl.texture_id[i]);
+      glEGLImageTargetTexture2DOES(context->gl.texture_target_type,
+                                   context->current_buffer->image_khr(i));
+    }
+    glBindTexture(context->gl.texture_target_type, 0);
+    CHECK_GL();
+
+    bool is_late_latch = false;
+
+    // Pass back the texture target type and id.
+    for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+      switch (p->key) {
+        case DVR_SURFACE_PARAMETER_ENABLE_LATE_LATCH_IN:
+          is_late_latch = !!p->value;
+          break;
+        case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_TYPE_OUT:
+          *static_cast<GLenum*>(p->value_out) = context->gl.texture_target_type;
+          break;
+        case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_ID_OUT:
+          for (int i = 0; i < context->gl.texture_count; ++i) {
+            *(static_cast<GLuint*>(p->value_out) + i) =
+                context->gl.texture_id[i];
+          }
+          break;
+      }
+    }
+
+    // Initialize late latch.
+    if (is_late_latch) {
+      LocalHandle fd;
+      int ret = context->display_surface->GetMetadataBufferFd(&fd);
+      if (ret == 0) {
+        context->late_latch.reset(
+            new android::dvr::LateLatch(true, std::move(fd)));
+      } else {
+        ALOGE("Error: failed to get surface metadata buffer fd for late latch");
+      }
+    }
+  } else if (context->graphics_api == DVR_GRAPHICS_API_VULKAN) {
+    VkResult result = VK_SUCCESS;
+    // Create a VkSurfaceKHR from the ANativeWindow.
+    VkAndroidSurfaceCreateInfoKHR android_surface_ci = {};
+    android_surface_ci.sType =
+        VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+    android_surface_ci.window = context.get();
+    result = vkCreateAndroidSurfaceKHR(
+        context->vk.instance, &android_surface_ci,
+        context->vk.allocation_callbacks, &context->vk.surface);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    VkBool32 surface_supports_present = VK_FALSE;
+    result = vkGetPhysicalDeviceSurfaceSupportKHR(
+        context->vk.physical_device, context->vk.present_queue_family,
+        context->vk.surface, &surface_supports_present);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    if (!surface_supports_present) {
+      ALOGE("Error: provided queue family (%u) does not support presentation",
+            context->vk.present_queue_family);
+      return -EPERM;
+    }
+    VkSurfaceCapabilitiesKHR surface_capabilities = {};
+    result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+        context->vk.physical_device, context->vk.surface,
+        &surface_capabilities);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    // Determine the swapchain image format.
+    uint32_t device_surface_format_count = 0;
+    result = vkGetPhysicalDeviceSurfaceFormatsKHR(
+        context->vk.physical_device, context->vk.surface,
+        &device_surface_format_count, nullptr);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    std::vector<VkSurfaceFormatKHR> device_surface_formats(
+        device_surface_format_count);
+    result = vkGetPhysicalDeviceSurfaceFormatsKHR(
+        context->vk.physical_device, context->vk.surface,
+        &device_surface_format_count, device_surface_formats.data());
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(device_surface_format_count == 0U);
+    LOG_ALWAYS_FATAL_IF(device_surface_formats[0].format ==
+                        VK_FORMAT_UNDEFINED);
+    VkSurfaceFormatKHR present_surface_format = device_surface_formats[0];
+    // Determine the swapchain present mode.
+    // TODO(cort): query device_present_modes to make sure MAILBOX is supported.
+    // But according to libvulkan, it is.
+    uint32_t device_present_mode_count = 0;
+    result = vkGetPhysicalDeviceSurfacePresentModesKHR(
+        context->vk.physical_device, context->vk.surface,
+        &device_present_mode_count, nullptr);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    std::vector<VkPresentModeKHR> device_present_modes(
+        device_present_mode_count);
+    result = vkGetPhysicalDeviceSurfacePresentModesKHR(
+        context->vk.physical_device, context->vk.surface,
+        &device_present_mode_count, device_present_modes.data());
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
+    // Extract presentation surface extents, image count, transform, usages,
+    // etc.
+    LOG_ALWAYS_FATAL_IF(
+        static_cast<int>(surface_capabilities.currentExtent.width) == -1 ||
+        static_cast<int>(surface_capabilities.currentExtent.height) == -1);
+    VkExtent2D swapchain_extent = surface_capabilities.currentExtent;
+
+    uint32_t desired_image_count = surface_capabilities.minImageCount;
+    if (surface_capabilities.maxImageCount > 0 &&
+        desired_image_count > surface_capabilities.maxImageCount) {
+      desired_image_count = surface_capabilities.maxImageCount;
+    }
+    VkSurfaceTransformFlagBitsKHR surface_transform =
+        surface_capabilities.currentTransform;
+    VkImageUsageFlags image_usage_flags =
+        surface_capabilities.supportedUsageFlags;
+    LOG_ALWAYS_FATAL_IF(surface_capabilities.supportedCompositeAlpha ==
+                        static_cast<VkFlags>(0));
+    VkCompositeAlphaFlagBitsKHR composite_alpha =
+        VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+    if (!(surface_capabilities.supportedCompositeAlpha &
+          VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) {
+      composite_alpha = VkCompositeAlphaFlagBitsKHR(
+          static_cast<int>(surface_capabilities.supportedCompositeAlpha) &
+          -static_cast<int>(surface_capabilities.supportedCompositeAlpha));
+    }
+    // Create VkSwapchainKHR
+    VkSwapchainCreateInfoKHR swapchain_ci = {};
+    swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+    swapchain_ci.pNext = nullptr;
+    swapchain_ci.surface = context->vk.surface;
+    swapchain_ci.minImageCount = desired_image_count;
+    swapchain_ci.imageFormat = present_surface_format.format;
+    swapchain_ci.imageColorSpace = present_surface_format.colorSpace;
+    swapchain_ci.imageExtent.width = swapchain_extent.width;
+    swapchain_ci.imageExtent.height = swapchain_extent.height;
+    swapchain_ci.imageUsage = image_usage_flags;
+    swapchain_ci.preTransform = surface_transform;
+    swapchain_ci.compositeAlpha = composite_alpha;
+    swapchain_ci.imageArrayLayers = 1;
+    swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    swapchain_ci.queueFamilyIndexCount = 0;
+    swapchain_ci.pQueueFamilyIndices = nullptr;
+    swapchain_ci.presentMode = present_mode;
+    swapchain_ci.clipped = VK_TRUE;
+    swapchain_ci.oldSwapchain = VK_NULL_HANDLE;
+    result = vkCreateSwapchainKHR(context->vk.device, &swapchain_ci,
+                                  context->vk.allocation_callbacks,
+                                  &context->vk.swapchain);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    // Create swapchain image views
+    uint32_t image_count = 0;
+    result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
+                                     &image_count, nullptr);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(image_count == 0U);
+    context->vk.swapchain_images.resize(image_count);
+    result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
+                                     &image_count,
+                                     context->vk.swapchain_images.data());
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    context->vk.swapchain_image_views.resize(image_count);
+    VkImageViewCreateInfo image_view_ci = {};
+    image_view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    image_view_ci.pNext = nullptr;
+    image_view_ci.flags = 0;
+    image_view_ci.format = swapchain_ci.imageFormat;
+    image_view_ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    image_view_ci.subresourceRange.baseMipLevel = 0;
+    image_view_ci.subresourceRange.levelCount = 1;
+    image_view_ci.subresourceRange.baseArrayLayer = 0;
+    image_view_ci.subresourceRange.layerCount = 1;
+    image_view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    image_view_ci.image = VK_NULL_HANDLE;  // filled in below
+    for (uint32_t i = 0; i < image_count; ++i) {
+      image_view_ci.image = context->vk.swapchain_images[i];
+      result = vkCreateImageView(context->vk.device, &image_view_ci,
+                                 context->vk.allocation_callbacks,
+                                 &context->vk.swapchain_image_views[i]);
+      LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    }
+    // Fill in any requested output parameters.
+    for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+      switch (p->key) {
+        case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_COUNT_OUT:
+          *static_cast<uint32_t*>(p->value_out) = image_count;
+          break;
+        case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_FORMAT_OUT:
+          *static_cast<VkFormat*>(p->value_out) = swapchain_ci.imageFormat;
+          break;
+      }
+    }
+  }
+
+  *return_graphics_context = context.release();
+  return 0;
+}
+
+void dvrGraphicsContextDestroy(DvrGraphicsContext* graphics_context) {
+  delete graphics_context;
+}
+
+// ANativeWindow function implementations. These should only be used
+// by the Vulkan path.
+int DvrGraphicsContext::Post(android::dvr::NativeBufferProducer* buffer,
+                             int fence_fd) {
+  LOG_ALWAYS_FATAL_IF(graphics_api != DVR_GRAPHICS_API_VULKAN);
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+  ALOGI_IF(TRACE, "DvrGraphicsContext::Post: buffer_id=%d, fence_fd=%d",
+           buffer->buffer()->id(), fence_fd);
+  ALOGW_IF(!display_surface->visible(),
+           "DvrGraphicsContext::Post: Posting buffer on invisible surface!!!");
+  // The NativeBufferProducer closes the fence fd, so dup it for tracking in the
+  // frame history.
+  frame_history.OnFrameSubmit(LocalHandle::AsDuplicate(fence_fd));
+  int result = buffer->Post(fence_fd, 0);
+  return result;
+}
+
+int DvrGraphicsContext::SetSwapInterval(ANativeWindow* window, int interval) {
+  ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval);
+  DvrGraphicsContext* self = getSelf(window);
+  (void)self;
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::DequeueBuffer(ANativeWindow* window,
+                                      ANativeWindowBuffer** buffer,
+                                      int* fence_fd) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+
+  DvrGraphicsContext* self = getSelf(window);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  if (!self->current_buffer) {
+    self->current_buffer = self->buffer_queue.get()->Dequeue();
+  }
+  ATRACE_ASYNC_BEGIN("BufferDraw", self->current_buffer->buffer()->id());
+  *fence_fd = self->current_buffer->ClaimReleaseFence().Release();
+  *buffer = self->current_buffer;
+
+  ALOGI_IF(TRACE, "DvrGraphicsContext::DequeueBuffer: fence_fd=%d", *fence_fd);
+  return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::QueueBuffer(ANativeWindow* window,
+                                    ANativeWindowBuffer* buffer, int fence_fd) {
+  ATRACE_NAME("NativeWindow::QueueBuffer");
+  ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd);
+
+  DvrGraphicsContext* self = getSelf(window);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  android::dvr::NativeBufferProducer* native_buffer =
+      static_cast<android::dvr::NativeBufferProducer*>(buffer);
+  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+  bool do_post = true;
+  if (self->buffer_already_posted) {
+    // Check that the buffer is the one we expect, but handle it if this happens
+    // in production by allowing this buffer to post on top of the previous one.
+    LOG_FATAL_IF(native_buffer != self->current_buffer);
+    if (native_buffer == self->current_buffer) {
+      do_post = false;
+      if (fence_fd >= 0)
+        close(fence_fd);
+    }
+  }
+  if (do_post) {
+    ATRACE_ASYNC_BEGIN("BufferPost", native_buffer->buffer()->id());
+    self->Post(native_buffer, fence_fd);
+  }
+  self->buffer_already_posted = false;
+  self->current_buffer = nullptr;
+
+  return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::CancelBuffer(ANativeWindow* window,
+                                     ANativeWindowBuffer* buffer,
+                                     int fence_fd) {
+  ATRACE_NAME("DvrGraphicsContext::CancelBuffer");
+  ALOGI_IF(TRACE, "DvrGraphicsContext::CancelBuffer: fence_fd: %d", fence_fd);
+
+  DvrGraphicsContext* self = getSelf(window);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  android::dvr::NativeBufferProducer* native_buffer =
+      static_cast<android::dvr::NativeBufferProducer*>(buffer);
+  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+  ATRACE_INT("CancelBuffer", native_buffer->buffer()->id());
+  bool do_enqueue = true;
+  if (self->buffer_already_posted) {
+    // Check that the buffer is the one we expect, but handle it if this happens
+    // in production by returning this buffer to the buffer queue.
+    LOG_FATAL_IF(native_buffer != self->current_buffer);
+    if (native_buffer == self->current_buffer) {
+      do_enqueue = false;
+    }
+  }
+  if (do_enqueue) {
+    self->buffer_queue.get()->Enqueue(native_buffer);
+  }
+  if (fence_fd >= 0)
+    close(fence_fd);
+  self->buffer_already_posted = false;
+  self->current_buffer = nullptr;
+
+  return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::Query(const ANativeWindow* window, int what,
+                              int* value) {
+  DvrGraphicsContext* self = getSelf(const_cast<ANativeWindow*>(window));
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  switch (what) {
+    case NATIVE_WINDOW_WIDTH:
+      *value = self->display_surface->width();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_HEIGHT:
+      *value = self->display_surface->height();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_FORMAT:
+      *value = self->display_surface->format();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+      *value = 1;
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_CONCRETE_TYPE:
+      *value = NATIVE_WINDOW_SURFACE;
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+      *value = 1;
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_DEFAULT_WIDTH:
+      *value = self->display_surface->width();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_DEFAULT_HEIGHT:
+      *value = self->display_surface->height();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_TRANSFORM_HINT:
+      *value = 0;
+      return android::NO_ERROR;
+  }
+
+  *value = 0;
+  return android::BAD_VALUE;
+}
+
+int DvrGraphicsContext::Perform(ANativeWindow* window, int operation, ...) {
+  DvrGraphicsContext* self = getSelf(window);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  va_list args;
+  va_start(args, operation);
+
+  // TODO(eieio): The following operations are not used at this time. They are
+  // included here to help document which operations may be useful and what
+  // parameters they take.
+  switch (operation) {
+    case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: {
+      int w = va_arg(args, int);
+      int h = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: w=%d h=%d", w, h);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+      int format = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_FORMAT: format=%d", format);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: {
+      int transform = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: transform=%d",
+               transform);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_SET_USAGE: {
+      int usage = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_CONNECT:
+    case NATIVE_WINDOW_DISCONNECT:
+    case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+    case NATIVE_WINDOW_API_CONNECT:
+    case NATIVE_WINDOW_API_DISCONNECT:
+      // TODO(eieio): we should implement these
+      return android::NO_ERROR;
+
+    case NATIVE_WINDOW_SET_BUFFER_COUNT: {
+      int buffer_count = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFER_COUNT: bufferCount=%d",
+               buffer_count);
+      return android::NO_ERROR;
+    }
+    case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+      android_dataspace_t data_space =
+          static_cast<android_dataspace_t>(va_arg(args, int));
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DATASPACE: dataSpace=%d",
+               data_space);
+      return android::NO_ERROR;
+    }
+    case NATIVE_WINDOW_SET_SCALING_MODE: {
+      int mode = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_SCALING_MODE: mode=%d", mode);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_LOCK:
+    case NATIVE_WINDOW_UNLOCK_AND_POST:
+    case NATIVE_WINDOW_SET_CROP:
+    case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+      return android::INVALID_OPERATION;
+  }
+
+  return android::NAME_NOT_FOUND;
+}
+
+int DvrGraphicsContext::DequeueBuffer_DEPRECATED(ANativeWindow* window,
+                                                 ANativeWindowBuffer** buffer) {
+  int fence_fd = -1;
+  int ret = DequeueBuffer(window, buffer, &fence_fd);
+
+  // wait for fence
+  if (ret == android::NO_ERROR && fence_fd != -1)
+    close(fence_fd);
+
+  return ret;
+}
+
+int DvrGraphicsContext::CancelBuffer_DEPRECATED(ANativeWindow* window,
+                                                ANativeWindowBuffer* buffer) {
+  return CancelBuffer(window, buffer, -1);
+}
+
+int DvrGraphicsContext::QueueBuffer_DEPRECATED(ANativeWindow* window,
+                                               ANativeWindowBuffer* buffer) {
+  return QueueBuffer(window, buffer, -1);
+}
+
+int DvrGraphicsContext::LockBuffer_DEPRECATED(ANativeWindow* /*window*/,
+                                              ANativeWindowBuffer* /*buffer*/) {
+  return android::NO_ERROR;
+}
+// End ANativeWindow implementation
+
+int dvrSetEdsPose(DvrGraphicsContext* graphics_context,
+                  float32x4_t render_pose_orientation,
+                  float32x4_t render_pose_translation) {
+  ATRACE_NAME("dvrSetEdsPose");
+  if (!graphics_context->current_buffer) {
+    ALOGE("dvrBeginRenderFrame must be called before dvrSetEdsPose");
+    return -EPERM;
+  }
+
+  // When late-latching is enabled, the pose buffer is written by the GPU, so
+  // we don't touch it here.
+  float32x4_t is_late_latch = DVR_POSE_LATE_LATCH;
+  if (render_pose_orientation[0] != is_late_latch[0]) {
+    volatile android::dvr::DisplaySurfaceMetadata* data =
+        graphics_context->surface_metadata;
+    uint32_t buffer_index =
+        graphics_context->current_buffer->surface_buffer_index();
+    ALOGE_IF(TRACE, "write pose index %d %f %f", buffer_index,
+             render_pose_orientation[0], render_pose_orientation[1]);
+    data->orientation[buffer_index] = render_pose_orientation;
+    data->translation[buffer_index] = render_pose_translation;
+  }
+
+  return 0;
+}
+
+int dvrBeginRenderFrameEds(DvrGraphicsContext* graphics_context,
+                           float32x4_t render_pose_orientation,
+                           float32x4_t render_pose_translation) {
+  ATRACE_NAME("dvrBeginRenderFrameEds");
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
+  CHECK_GL();
+  // Grab a buffer from the queue and set its pose.
+  if (!graphics_context->current_buffer) {
+    graphics_context->current_buffer =
+        graphics_context->buffer_queue->Dequeue();
+  }
+
+  int ret = dvrSetEdsPose(graphics_context, render_pose_orientation,
+                          render_pose_translation);
+  if (ret < 0)
+    return ret;
+
+  ATRACE_ASYNC_BEGIN("BufferDraw",
+                     graphics_context->current_buffer->buffer()->id());
+
+  {
+    ATRACE_NAME("glEGLImageTargetTexture2DOES");
+    // Bind the texture to the latest buffer in the queue.
+    for (int i = 0; i < graphics_context->gl.texture_count; ++i) {
+      glBindTexture(graphics_context->gl.texture_target_type,
+                    graphics_context->gl.texture_id[i]);
+      glEGLImageTargetTexture2DOES(
+          graphics_context->gl.texture_target_type,
+          graphics_context->current_buffer->image_khr(i));
+    }
+    glBindTexture(graphics_context->gl.texture_target_type, 0);
+  }
+  CHECK_GL();
+  return 0;
+}
+int dvrBeginRenderFrameEdsVk(DvrGraphicsContext* graphics_context,
+                             float32x4_t render_pose_orientation,
+                             float32x4_t render_pose_translation,
+                             VkSemaphore acquire_semaphore,
+                             VkFence acquire_fence,
+                             uint32_t* swapchain_image_index,
+                             VkImageView* swapchain_image_view) {
+  ATRACE_NAME("dvrBeginRenderFrameEds");
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api !=
+                      DVR_GRAPHICS_API_VULKAN);
+
+  // Acquire a swapchain image. This calls Dequeue() internally.
+  VkResult result = vkAcquireNextImageKHR(
+      graphics_context->vk.device, graphics_context->vk.swapchain, UINT64_MAX,
+      acquire_semaphore, acquire_fence, swapchain_image_index);
+  if (result != VK_SUCCESS)
+    return -EINVAL;
+
+  // Set the pose pose.
+  int ret = dvrSetEdsPose(graphics_context, render_pose_orientation,
+                          render_pose_translation);
+  if (ret < 0)
+    return ret;
+  *swapchain_image_view =
+      graphics_context->vk.swapchain_image_views[*swapchain_image_index];
+  return 0;
+}
+
+int dvrBeginRenderFrame(DvrGraphicsContext* graphics_context) {
+  return dvrBeginRenderFrameEds(graphics_context, DVR_POSE_NO_EDS,
+                                DVR_POSE_NO_EDS);
+}
+int dvrBeginRenderFrameVk(DvrGraphicsContext* graphics_context,
+                          VkSemaphore acquire_semaphore, VkFence acquire_fence,
+                          uint32_t* swapchain_image_index,
+                          VkImageView* swapchain_image_view) {
+  return dvrBeginRenderFrameEdsVk(
+      graphics_context, DVR_POSE_NO_EDS, DVR_POSE_NO_EDS, acquire_semaphore,
+      acquire_fence, swapchain_image_index, swapchain_image_view);
+}
+
+int dvrBeginRenderFrameLateLatch(DvrGraphicsContext* graphics_context,
+                                 uint32_t /*flags*/,
+                                 uint32_t target_vsync_count, int num_views,
+                                 const float** projection_matrices,
+                                 const float** eye_from_head_matrices,
+                                 const float** pose_offset_matrices,
+                                 uint32_t* out_late_latch_buffer_id) {
+  if (!graphics_context->late_latch) {
+    return -EPERM;
+  }
+  if (num_views > DVR_GRAPHICS_SURFACE_MAX_VIEWS) {
+    ALOGE("dvrBeginRenderFrameLateLatch called with too many views.");
+    return -EINVAL;
+  }
+  dvrBeginRenderFrameEds(graphics_context, DVR_POSE_LATE_LATCH,
+                         DVR_POSE_LATE_LATCH);
+  auto& ll = graphics_context->late_latch;
+  // TODO(jbates) Need to change this shader so that it dumps the single
+  // captured pose for both eyes into the display surface metadata buffer at
+  // the right index.
+  android::dvr::LateLatchInput input;
+  memset(&input, 0, sizeof(input));
+  for (int i = 0; i < num_views; ++i) {
+    memcpy(input.proj_mat + i, *(projection_matrices + i), 16 * sizeof(float));
+    memcpy(input.eye_from_head_mat + i, *(eye_from_head_matrices + i),
+           16 * sizeof(float));
+    memcpy(input.pose_offset + i, *(pose_offset_matrices + i),
+           16 * sizeof(float));
+  }
+  input.pose_index =
+      target_vsync_count & android::dvr::kPoseAsyncBufferIndexMask;
+  input.render_pose_index =
+      graphics_context->current_buffer->surface_buffer_index();
+  ll->AddLateLatch(input);
+  *out_late_latch_buffer_id = ll->output_buffer_id();
+  return 0;
+}
+
+extern "C" int dvrGraphicsWaitNextFrame(
+    DvrGraphicsContext* graphics_context, int64_t start_delay_ns,
+    DvrFrameSchedule* out_next_frame_schedule) {
+  start_delay_ns = std::max(start_delay_ns, static_cast<int64_t>(0));
+
+  // We only do one-shot timers:
+  int64_t wake_time_ns = 0;
+
+  uint32_t current_frame_vsync;
+  int64_t current_frame_scheduled_finish_ns;
+  int64_t vsync_period_ns;
+
+  int fetch_schedule_result = graphics_context->vsync_client->GetSchedInfo(
+      &vsync_period_ns, &current_frame_scheduled_finish_ns,
+      &current_frame_vsync);
+  if (fetch_schedule_result == 0) {
+    wake_time_ns = current_frame_scheduled_finish_ns + start_delay_ns;
+    // If the last wakeup time is still in the future, use it instead to avoid
+    // major schedule jumps when applications call WaitNextFrame with
+    // aggressive offsets.
+    int64_t now = android::dvr::GetSystemClockNs();
+    if (android::dvr::TimestampGT(wake_time_ns - vsync_period_ns, now)) {
+      wake_time_ns -= vsync_period_ns;
+      --current_frame_vsync;
+    }
+    // If the next wakeup time is in the past, add a vsync period to keep the
+    // application on schedule.
+    if (android::dvr::TimestampLT(wake_time_ns, now)) {
+      wake_time_ns += vsync_period_ns;
+      ++current_frame_vsync;
+    }
+  } else {
+    ALOGE("Error getting frame schedule because: %s",
+          strerror(-fetch_schedule_result));
+    // Sleep for a vsync period to avoid cascading failure.
+    wake_time_ns = android::dvr::GetSystemClockNs() +
+                   graphics_context->display_metrics.vsync_period_ns;
+  }
+
+  // Adjust nsec to [0..999,999,999].
+  struct itimerspec wake_time;
+  wake_time.it_interval.tv_sec = 0;
+  wake_time.it_interval.tv_nsec = 0;
+  wake_time.it_value = android::dvr::NsToTimespec(wake_time_ns);
+  bool sleep_result =
+      timerfd_settime(graphics_context->timerfd.Get(), TFD_TIMER_ABSTIME,
+                      &wake_time, nullptr) == 0;
+  if (sleep_result) {
+    ATRACE_NAME("sleep");
+    uint64_t expirations = 0;
+    sleep_result = read(graphics_context->timerfd.Get(), &expirations,
+                        sizeof(uint64_t)) == sizeof(uint64_t);
+    if (!sleep_result) {
+      ALOGE("Error: timerfd read failed");
+    }
+  } else {
+    ALOGE("Error: timerfd_settime failed because: %s", strerror(errno));
+  }
+
+  auto& frame_history = graphics_context->frame_history;
+  frame_history.CheckForFinishedFrames();
+  if (fetch_schedule_result == 0) {
+    uint32_t next_frame_vsync =
+        current_frame_vsync +
+        frame_history.PredictNextFrameVsyncInterval(vsync_period_ns);
+    int64_t next_frame_scheduled_finish =
+        (wake_time_ns - start_delay_ns) + vsync_period_ns;
+    frame_history.OnFrameStart(next_frame_vsync, next_frame_scheduled_finish);
+    if (out_next_frame_schedule) {
+      out_next_frame_schedule->vsync_count = next_frame_vsync;
+      out_next_frame_schedule->scheduled_frame_finish_ns =
+          next_frame_scheduled_finish;
+    }
+  } else {
+    frame_history.OnFrameStart(UINT32_MAX, -1);
+  }
+
+  return (fetch_schedule_result == 0 && sleep_result) ? 0 : -1;
+}
+
+extern "C" void dvrGraphicsPostEarly(DvrGraphicsContext* graphics_context) {
+  ATRACE_NAME("dvrGraphicsPostEarly");
+  ALOGI_IF(TRACE, "dvrGraphicsPostEarly");
+
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
+
+  // Note that this function can be called before or after
+  // dvrBeginRenderFrame.
+  if (!graphics_context->buffer_already_posted) {
+    graphics_context->buffer_already_posted = true;
+
+    if (!graphics_context->current_buffer) {
+      graphics_context->current_buffer =
+          graphics_context->buffer_queue->Dequeue();
+    }
+
+    auto buffer = graphics_context->current_buffer->buffer().get();
+    ATRACE_ASYNC_BEGIN("BufferPost", buffer->id());
+    int result = buffer->Post<uint64_t>(LocalHandle(), 0);
+    if (result < 0)
+      ALOGE("Buffer post failed: %d (%s)", result, strerror(-result));
+  }
+}
+
+int dvrPresent(DvrGraphicsContext* graphics_context) {
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
+
+  std::array<char, 128> buf;
+  snprintf(buf.data(), buf.size(), "dvrPresent|vsync=%d|",
+           graphics_context->frame_history.GetCurrentFrameVsync());
+  ATRACE_NAME(buf.data());
+
+  if (!graphics_context->current_buffer) {
+    ALOGE("Error: dvrPresent called without dvrBeginRenderFrame");
+    return -EPERM;
+  }
+
+  LocalHandle fence_fd =
+      android::dvr::CreateGLSyncAndFlush(graphics_context->gl.egl_display);
+
+  ALOGI_IF(TRACE, "PostBuffer: buffer_id=%d, fence_fd=%d",
+           graphics_context->current_buffer->buffer()->id(), fence_fd.Get());
+  ALOGW_IF(!graphics_context->display_surface->visible(),
+           "PostBuffer: Posting buffer on invisible surface!!!");
+
+  auto buffer = graphics_context->current_buffer->buffer().get();
+  ATRACE_ASYNC_END("BufferDraw", buffer->id());
+  if (!graphics_context->buffer_already_posted) {
+    ATRACE_ASYNC_BEGIN("BufferPost", buffer->id());
+    int result = buffer->Post<uint64_t>(fence_fd, 0);
+    if (result < 0)
+      ALOGE("Buffer post failed: %d (%s)", result, strerror(-result));
+  }
+
+  graphics_context->frame_history.OnFrameSubmit(std::move(fence_fd));
+  graphics_context->buffer_already_posted = false;
+  graphics_context->current_buffer = nullptr;
+  return 0;
+}
+
+int dvrPresentVk(DvrGraphicsContext* graphics_context,
+                 VkSemaphore submit_semaphore, uint32_t swapchain_image_index) {
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api !=
+                      DVR_GRAPHICS_API_VULKAN);
+
+  std::array<char, 128> buf;
+  snprintf(buf.data(), buf.size(), "dvrPresent|vsync=%d|",
+           graphics_context->frame_history.GetCurrentFrameVsync());
+  ATRACE_NAME(buf.data());
+
+  if (!graphics_context->current_buffer) {
+    ALOGE("Error: dvrPresentVk called without dvrBeginRenderFrameVk");
+    return -EPERM;
+  }
+
+  // Present the specified image. Internally, this gets a fence from the
+  // Vulkan driver and passes it to DvrGraphicsContext::Post(),
+  // which in turn passes it to buffer->Post() and adds it to frame_history.
+  VkPresentInfoKHR present_info = {};
+  present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+  present_info.swapchainCount = 1;
+  present_info.pSwapchains = &graphics_context->vk.swapchain;
+  present_info.pImageIndices = &swapchain_image_index;
+  present_info.waitSemaphoreCount =
+      (submit_semaphore != VK_NULL_HANDLE) ? 1 : 0;
+  present_info.pWaitSemaphores = &submit_semaphore;
+  VkResult result =
+      vkQueuePresentKHR(graphics_context->vk.present_queue, &present_info);
+  if (result != VK_SUCCESS) {
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+extern "C" int dvrGetFrameScheduleResults(DvrGraphicsContext* context,
+                                          DvrFrameScheduleResult* results,
+                                          int in_result_count) {
+  if (!context || !results)
+    return -EINVAL;
+
+  return context->frame_history.GetPreviousFrameResults(results,
+                                                        in_result_count);
+}
+
+extern "C" void dvrGraphicsSurfaceSetVisible(
+    DvrGraphicsContext* graphics_context, int visible) {
+  graphics_context->display_surface->SetVisible(visible);
+}
+
+extern "C" int dvrGraphicsSurfaceGetVisible(
+    DvrGraphicsContext* graphics_context) {
+  return graphics_context->display_surface->visible() ? 1 : 0;
+}
+
+extern "C" void dvrGraphicsSurfaceSetZOrder(
+    DvrGraphicsContext* graphics_context, int z_order) {
+  graphics_context->display_surface->SetZOrder(z_order);
+}
+
+extern "C" int dvrGraphicsSurfaceGetZOrder(
+    DvrGraphicsContext* graphics_context) {
+  return graphics_context->display_surface->z_order();
+}
+
+extern "C" DvrVideoMeshSurface* dvrGraphicsVideoMeshSurfaceCreate(
+    DvrGraphicsContext* graphics_context) {
+  auto display_surface = graphics_context->display_surface;
+  // A DisplaySurface must be created prior to the creation of a
+  // VideoMeshSurface.
+  LOG_ALWAYS_FATAL_IF(display_surface == nullptr);
+
+  LocalChannelHandle surface_handle = display_surface->CreateVideoMeshSurface();
+  if (!surface_handle.valid()) {
+    return nullptr;
+  }
+
+  std::unique_ptr<DvrVideoMeshSurface> surface(new DvrVideoMeshSurface);
+  surface->client =
+      android::dvr::VideoMeshSurfaceClient::Import(std::move(surface_handle));
+
+  // TODO(jwcai) The next line is not needed...
+  auto producer_queue = surface->client->GetProducerQueue();
+  return surface.release();
+}
+
+extern "C" void dvrGraphicsVideoMeshSurfaceDestroy(
+    DvrVideoMeshSurface* surface) {
+  delete surface;
+}
+
+extern "C" void dvrGraphicsVideoMeshSurfacePresent(
+    DvrGraphicsContext* graphics_context, DvrVideoMeshSurface* surface,
+    const int eye, const float* transform) {
+  volatile android::dvr::VideoMeshSurfaceMetadata* metadata =
+      surface->client->GetMetadataBufferPtr();
+
+  const uint32_t graphics_buffer_index =
+      graphics_context->current_buffer->surface_buffer_index();
+
+  for (int i = 0; i < 4; ++i) {
+    metadata->transform[graphics_buffer_index][eye].val[i] = {
+        transform[i + 0], transform[i + 4], transform[i + 8], transform[i + 12],
+    };
+  }
+}
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/graphics.h b/libs/vr/libdisplay/include/dvr/graphics.h
new file mode 100644
index 0000000..ac8b27f
--- /dev/null
+++ b/libs/vr/libdisplay/include/dvr/graphics.h
@@ -0,0 +1,447 @@
+#ifndef DVR_GRAPHICS_H_
+#define DVR_GRAPHICS_H_
+
+#include <EGL/egl.h>
+#include <sys/cdefs.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
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR 1
+#endif
+#include <vulkan/vulkan.h>
+
+__BEGIN_DECLS
+
+// Display surface parameters used to specify display surface options.
+enum {
+  DVR_SURFACE_PARAMETER_NONE = 0,
+  // WIDTH
+  DVR_SURFACE_PARAMETER_WIDTH_IN,
+  // HEIGHT
+  DVR_SURFACE_PARAMETER_HEIGHT_IN,
+  // DISABLE_DISTORTION
+  DVR_SURFACE_PARAMETER_DISABLE_DISTORTION_IN,
+  // DISABLE_STABILIZATION
+  DVR_SURFACE_PARAMETER_DISABLE_STABILIZATION_IN,
+  // Disable chromatic aberration correction
+  DVR_SURFACE_PARAMETER_DISABLE_CAC_IN,
+  // ENABLE_LATE_LATCH: Enable late latching of pose data for application
+  // GPU shaders.
+  DVR_SURFACE_PARAMETER_ENABLE_LATE_LATCH_IN,
+  // VISIBLE
+  DVR_SURFACE_PARAMETER_VISIBLE_IN,
+  // Z_ORDER
+  DVR_SURFACE_PARAMETER_Z_ORDER_IN,
+  // EXCLUDE_FROM_BLUR
+  DVR_SURFACE_PARAMETER_EXCLUDE_FROM_BLUR_IN,
+  // BLUR_BEHIND
+  DVR_SURFACE_PARAMETER_BLUR_BEHIND_IN,
+  // DISPLAY_WIDTH
+  DVR_SURFACE_PARAMETER_DISPLAY_WIDTH_OUT,
+  // DISPLAY_HEIGHT
+  DVR_SURFACE_PARAMETER_DISPLAY_HEIGHT_OUT,
+  // SURFACE_WIDTH: Returns width of allocated surface buffer.
+  DVR_SURFACE_PARAMETER_SURFACE_WIDTH_OUT,
+  // SURFACE_HEIGHT: Returns height of allocated surface buffer.
+  DVR_SURFACE_PARAMETER_SURFACE_HEIGHT_OUT,
+  // INTER_LENS_METERS: Returns float value in meters, the distance between
+  // lenses.
+  DVR_SURFACE_PARAMETER_INTER_LENS_METERS_OUT,
+  // LEFT_FOV_LRBT: Return storage must have room for array of 4 floats (in
+  // radians). The layout is left, right, bottom, top as indicated by LRBT.
+  DVR_SURFACE_PARAMETER_LEFT_FOV_LRBT_OUT,
+  // RIGHT_FOV_LRBT: Return storage must have room for array of 4 floats (in
+  // radians). The layout is left, right, bottom, top as indicated by LRBT.
+  DVR_SURFACE_PARAMETER_RIGHT_FOV_LRBT_OUT,
+  // VSYNC_PERIOD: Returns the period of the display refresh (in
+  // nanoseconds per refresh), as a 64-bit unsigned integer.
+  DVR_SURFACE_PARAMETER_VSYNC_PERIOD_OUT,
+  // SURFACE_TEXTURE_TARGET_TYPE: Returns the type of texture used as the render
+  // target.
+  DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_TYPE_OUT,
+  // SURFACE_TEXTURE_TARGET_ID: Returns the texture ID used as the render
+  // target.
+  DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_ID_OUT,
+  // Whether the surface needs to be flipped vertically before display. Default
+  // is 0.
+  DVR_SURFACE_PARAMETER_VERTICAL_FLIP_IN,
+  // A bool indicating whether or not to create a GL context for the surface.
+  // 0: don't create a context
+  // Non-zero: create a context.
+  // Default is 1.
+  // If this value is 0, there must be a GLES 3.2 or greater context bound on
+  // the current thread at the time dvrGraphicsContextCreate is called.
+  DVR_SURFACE_PARAMETER_CREATE_GL_CONTEXT_IN,
+  // Specify one of DVR_SURFACE_GEOMETRY_*.
+  DVR_SURFACE_PARAMETER_GEOMETRY_IN,
+  // FORMAT: One of DVR_SURFACE_FORMAT_RGBA_8888 or DVR_SURFACE_FORMAT_RGB_565.
+  // Default is DVR_SURFACE_FORMAT_RGBA_8888.
+  DVR_SURFACE_PARAMETER_FORMAT_IN,
+  // GRAPHICS_API: One of DVR_SURFACE_GRAPHICS_API_GLES or
+  // DVR_SURFACE_GRAPHICS_API_VULKAN. Default is GLES.
+  DVR_SURFACE_PARAMETER_GRAPHICS_API_IN,
+  // VK_INSTANCE: In Vulkan mode, the application creates a VkInstance and
+  // passes it in.
+  DVR_SURFACE_PARAMETER_VK_INSTANCE_IN,
+  // VK_PHYSICAL_DEVICE: In Vulkan mode, the application passes in the
+  // PhysicalDevice handle corresponding to the logical device passed to
+  // VK_DEVICE.
+  DVR_SURFACE_PARAMETER_VK_PHYSICAL_DEVICE_IN,
+  // VK_DEVICE: In Vulkan mode, the application creates a VkDevice and
+  // passes it in.
+  DVR_SURFACE_PARAMETER_VK_DEVICE_IN,
+  // VK_PRESENT_QUEUE: In Vulkan mode, the application selects a
+  // presentation-compatible VkQueue and passes it in.
+  DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_IN,
+  // VK_PRESENT_QUEUE_FAMILY: In Vulkan mode, the application passes in the
+  // index of the queue family containing the VkQueue passed to
+  // VK_PRESENT_QUEUE.
+  DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_FAMILY_IN,
+  // VK_SWAPCHAIN_IMAGE_COUNT: In Vulkan mode, the number of swapchain images
+  // will be returned here.
+  DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_COUNT_OUT,
+  // VK_SWAPCHAIN_IMAGE_FORMAT: In Vulkan mode, the VkFormat of the swapchain
+  // images will be returned here.
+  DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_FORMAT_OUT,
+};
+
+enum {
+  // Default surface type. One wide buffer with the left eye view in the left
+  // half and the right eye view in the right half.
+  DVR_SURFACE_GEOMETRY_SINGLE,
+  // Separate buffers, one per eye. The width parameters still refer to the
+  // total width (2 * eye view width).
+  DVR_SURFACE_GEOMETRY_SEPARATE_2,
+};
+
+// Surface format. Gvr only supports RGBA_8888 and RGB_565 for now, so those are
+// the only formats we provide here.
+enum {
+  DVR_SURFACE_FORMAT_RGBA_8888,
+  DVR_SURFACE_FORMAT_RGB_565,
+};
+
+enum {
+  // Graphics contexts are created for OpenGL ES client applications by default.
+  DVR_GRAPHICS_API_GLES,
+  // Create the graphics context for Vulkan client applications.
+  DVR_GRAPHICS_API_VULKAN,
+};
+
+#define DVR_SURFACE_PARAMETER_IN(name, value) \
+  { DVR_SURFACE_PARAMETER_##name##_IN, (value), NULL }
+#define DVR_SURFACE_PARAMETER_OUT(name, value) \
+  { DVR_SURFACE_PARAMETER_##name##_OUT, 0, (value) }
+#define DVR_SURFACE_PARAMETER_LIST_END \
+  { DVR_SURFACE_PARAMETER_NONE, 0, NULL }
+
+struct DvrSurfaceParameter {
+  int32_t key;
+  int64_t value;
+  void* value_out;
+};
+
+// This is a convenience struct to hold the relevant information of the HMD
+// lenses.
+struct DvrLensInfo {
+  float inter_lens_meters;
+  float left_fov[4];
+  float right_fov[4];
+};
+
+int dvrGetNativeDisplayDimensions(int* native_width, int* native_height);
+
+typedef struct DvrReadBuffer DvrReadBuffer;
+
+// Opaque struct that represents a graphics context, the texture swap chain,
+// and surfaces.
+typedef struct DvrGraphicsContext DvrGraphicsContext;
+
+// Create the graphics context. with the given parameters. The list of
+// parameters is terminated with an entry where key ==
+// DVR_SURFACE_PARAMETER_NONE. For example, the parameters array could be built
+// as follows:
+//   int display_width = 0, display_height = 0;
+//   int surface_width = 0, surface_height = 0;
+//   float inter_lens_meters = 0.0f;
+//   float left_fov[4] = {0.0f};
+//   float right_fov[4] = {0.0f};
+//   int disable_warp = 0;
+//   DvrSurfaceParameter surface_params[] = {
+//       DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+//       DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+//       DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+//       DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+//       DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+//       DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+//       DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+//       DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+//       DVR_SURFACE_PARAMETER_LIST_END,
+//   };
+int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters,
+                             DvrGraphicsContext** return_graphics_context);
+
+// Destroy the graphics context.
+void dvrGraphicsContextDestroy(DvrGraphicsContext* graphics_context);
+
+// For every frame a schedule is decided by the system compositor. A sample
+// schedule for two frames is shown below.
+//
+// |                        |                        |
+// |-----------------|------|-----------------|------|
+// |                        |                        |
+// V0                A1     V1                A2     V2
+//
+// V0, V1, and V2 are display vsync events. Vsync events are uniquely identified
+// throughout the DVR system by a vsync count maintained by the system
+// compositor.
+//
+// A1 and A2 indicate when the application should finish rendering its frame,
+// including all GPU work. Under normal circumstances the scheduled finish
+// finish time will be set a few milliseconds before the vsync time, to give the
+// compositor time to perform distortion and EDS on the app's buffer. For apps
+// that don't use system distortion the scheduled frame finish time will be
+// closer to the vsync time. Other factors can also effect the scheduled frame
+// finish time, e.g. whether or not the System UI is being displayed.
+typedef struct DvrFrameSchedule {
+  // vsync_count is used as a frame identifier.
+  uint32_t vsync_count;
+
+  // The time when the app should finish rendering its frame, including all GPU
+  // work.
+  int64_t scheduled_frame_finish_ns;
+} DvrFrameSchedule;
+
+// Sleep until it's time to render the next frame. This should be the first
+// function called as part of an app's render loop, which normally looks like
+// this:
+//
+// while (1) {
+//   DvrFrameSchedule schedule;
+//   dvrGraphicsWaitNextFrame(..., &schedule); // Sleep until it's time to
+//                                             // render the next frame
+//   pose = dvrPoseGet(schedule.vsync_count);
+//   dvrBeginRenderFrame(...);
+//   <render a frame using the pose>
+//   dvrPresent(...); // Post the buffer
+// }
+//
+// |start_delay_ns| adjusts how long this function blocks the app from starting
+// its next frame. If |start_delay_ns| is 0, the function waits until the
+// scheduled frame finish time for the current frame, which gives the app one
+// full vsync period to render the next frame. If the app needs less than a full
+// vysnc period to render the frame, pass in a non-zero |start_delay_ns| to
+// delay the start of frame rendering further. For example, if the vsync period
+// is 11.1ms and the app takes 6ms to render a frame, consider setting this to
+// 5ms (note that the value is in nanoseconds, so 5,000,000ns) so that the app
+// finishes the frame closer to the scheduled frame finish time. Delaying the
+// start of rendering allows the app to use a more up-to-date pose for
+// rendering.
+// |start_delay_ns| must be a positive value or 0. If you're unsure what to set
+// for |start_delay_ns|, use 0.
+//
+// |out_next_frame_schedule| is an output parameter that will contain the
+// schedule for the next frame. It can be null. This function returns a negative
+// error code on failure.
+int dvrGraphicsWaitNextFrame(DvrGraphicsContext* graphics_context,
+                             int64_t start_delay_ns,
+                             DvrFrameSchedule* out_next_frame_schedule);
+
+// Prepares the graphics context's texture for rendering.  This function should
+// be called once for each frame, ideally immediately before the first GL call
+// on the framebuffer which wraps the surface texture.
+//
+// For GL contexts, GL states are modified as follows by this function:
+// glBindTexture(GL_TEXTURE_2D, 0);
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @param[in] render_pose_orientation Head pose orientation that rendering for
+//            this frame will be based off of. This must be an unmodified value
+//            from DvrPoseAsync, returned by dvrPoseGet.
+// @param[in] render_pose_translation Head pose translation that rendering for
+//            this frame will be based off of. This must be an unmodified value
+//            from DvrPoseAsync, returned by dvrPoseGet.
+// @return 0 on success or a negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrBeginRenderFrameEds(DvrGraphicsContext* graphics_context,
+                           float32x4_t render_pose_orientation,
+                           float32x4_t render_pose_translation);
+int dvrBeginRenderFrameEdsVk(DvrGraphicsContext* graphics_context,
+                             float32x4_t render_pose_orientation,
+                             float32x4_t render_pose_translation,
+                             VkSemaphore acquire_semaphore,
+                             VkFence acquire_fence,
+                             uint32_t* swapchain_image_index,
+                             VkImageView* swapchain_image_view);
+// Same as dvrBeginRenderFrameEds, but with no EDS (asynchronous reprojection).
+//
+// For GL contexts, GL states are modified as follows by this function:
+// glBindTexture(GL_TEXTURE_2D, 0);
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @return 0 on success or a negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrBeginRenderFrame(DvrGraphicsContext* graphics_context);
+int dvrBeginRenderFrameVk(DvrGraphicsContext* graphics_context,
+                          VkSemaphore acquire_semaphore, VkFence acquire_fence,
+                          uint32_t* swapchain_image_index,
+                          VkImageView* swapchain_image_view);
+
+// Maximum number of views per surface buffer (for multiview, multi-eye, etc).
+#define DVR_GRAPHICS_SURFACE_MAX_VIEWS 4
+
+// Output data format of late latch shader. The application can bind all or part
+// of this data with the buffer ID returned by dvrBeginRenderFrameLateLatch.
+// This struct is compatible with std140 layout for use from shaders.
+struct __attribute__((__packed__)) DvrGraphicsLateLatchData {
+  // Column-major order.
+  float view_proj_matrix[DVR_GRAPHICS_SURFACE_MAX_VIEWS][16];
+  // Column-major order.
+  float view_matrix[DVR_GRAPHICS_SURFACE_MAX_VIEWS][16];
+  // Quaternion for pose orientation from start space.
+  float pose_orientation[4];
+  // Pose translation from start space.
+  float pose_translation[4];
+};
+
+// Begin render frame with late latching of pose data. This kicks off a compute
+// shader that will read the latest head pose and then compute and output
+// matrices that can be used by application shaders.
+//
+// Matrices are computed with the following pseudo code.
+//   Pose pose = getLateLatchPose();
+//   out.pose_orientation = pose.orientation;
+//   out.pose_translation = pose.translation;
+//   mat4 head_from_center = ComputeInverseMatrix(pose);
+//   for each view:
+//     out.viewMatrix[view] =
+//         eye_from_head_matrices[view] * head_from_center *
+//         pose_offset_matrices[view];
+//     out.viewProjMatrix[view] =
+//         projection_matrices[view] * out.viewMatrix[view];
+//
+// For GL contexts, GL states are modified as follows by this function:
+// glBindTexture(GL_TEXTURE_2D, 0);
+// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
+// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0);
+// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0);
+// glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, 0);
+// glUseProgram(0);
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @param[in] flags Specify 0.
+// @param[in] target_vsync_count The target vsync count that this frame will
+//            display at. This is used for pose prediction.
+// @param[in] num_views Number of matrices in each of the following matrix array
+//            parameters. Typically 2 for left and right eye views. Maximum is
+//            DVR_GRAPHICS_SURFACE_MAX_VIEWS.
+// @param[in] projection_matrices Array of pointers to |num_views| matrices with
+//            column-major layout. These are the application projection
+//            matrices.
+// @param[in] eye_from_head_matrices Array of pointers to |num_views| matrices
+//            with column-major layout. See pseudo code for how these are used.
+// @param[in] pose_offset_matrices Array of pointers to |num_views| matrices
+//            with column-major layout. See pseudo code for how these are used.
+// @param[out] out_late_latch_buffer_id The GL buffer ID of the output buffer of
+//             of type DvrGraphicsLateLatchData.
+// @return 0 on success or a negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrBeginRenderFrameLateLatch(DvrGraphicsContext* graphics_context,
+                                 uint32_t flags, uint32_t target_vsync_count,
+                                 int num_views,
+                                 const float** projection_matrices,
+                                 const float** eye_from_head_matrices,
+                                 const float** pose_offset_matrices,
+                                 uint32_t* out_late_latch_buffer_id);
+
+// Present a frame for display.
+// This call is normally non-blocking, unless the internal buffer queue is full.
+// @return 0 on success or a negative error code on failure.
+int dvrPresent(DvrGraphicsContext* graphics_context);
+int dvrPresentVk(DvrGraphicsContext* graphics_context,
+                 VkSemaphore submit_semaphore, uint32_t swapchain_image_index);
+
+// Post the next buffer early. This allows the application to race with either
+// the async EDS process or the scanline for applications that are not using
+// system distortion. When this is called, the next buffer in the queue is
+// posted for display. It is up to the application to kick its GPU rendering
+// work in time. If the rendering is incomplete there will be significant,
+// undesirable tearing artifacts.
+// It is not recommended to use this feature with system distortion.
+void dvrGraphicsPostEarly(DvrGraphicsContext* graphics_context);
+
+// Used to retrieve frame measurement timings from dvrGetFrameScheduleResults().
+typedef struct DvrFrameScheduleResult {
+  // vsync_count is used as a frame identifier.
+  uint32_t vsync_count;
+
+  // The app's scheduled frame finish time.
+  int64_t scheduled_frame_finish_ns;
+
+  // The difference (in nanoseconds) between the scheduled finish time and the
+  // actual finish time.
+  //
+  // A value of +2ms for frame_finish_offset_ns indicates the app's frame was
+  // late and may have been skipped by the compositor for that vsync. A value of
+  // -1ms indicates the app's frame finished just ahead of schedule, as
+  // desired. A value of -6ms indicates the app's frame finished well ahead of
+  // schedule for that vsync. In that case the app may have unnecessary visual
+  // latency. Consider using the start_delay_ns parameter in
+  // dvrGraphicsWaitNextFrame() to align the app's frame finish time closer to
+  // the scheduled finish time.
+  int64_t frame_finish_offset_ns;
+} DvrFrameScheduleResult;
+
+// Retrieve the latest frame schedule results for the app. To collect all the
+// results this should be called each frame. The results for each frame are
+// returned only once.
+// The number of results written to |results| is returned on success, or a
+// negative error code on failure.
+// |graphics_context| is the context to retrieve frame schedule results for.
+// |results| is an array that will contain the frame schedule results.
+// |result_count| is the size of the |results| array. It's recommended to pass
+// in an array with 2 elements to ensure results for all frames are collected.
+int dvrGetFrameScheduleResults(DvrGraphicsContext* graphics_context,
+                               DvrFrameScheduleResult* results,
+                               int result_count);
+
+// Make the surface visible or hidden based on |visible|.
+// 0: hidden, Non-zero: visible.
+void dvrGraphicsSurfaceSetVisible(DvrGraphicsContext* graphics_context,
+                                  int visible);
+
+// Returns surface visilibity last requested by the client.
+int dvrGraphicsSurfaceGetVisible(DvrGraphicsContext* graphics_context);
+
+// Returns surface z order last requested by the client.
+int dvrGraphicsSurfaceGetZOrder(DvrGraphicsContext* graphics_context);
+
+// Sets the compositor z-order of the surface. Higher values display on
+// top of lower values.
+void dvrGraphicsSurfaceSetZOrder(DvrGraphicsContext* graphics_context,
+                                 int z_order);
+
+typedef struct DvrVideoMeshSurface DvrVideoMeshSurface;
+
+DvrVideoMeshSurface* dvrGraphicsVideoMeshSurfaceCreate(
+    DvrGraphicsContext* graphics_context);
+void dvrGraphicsVideoMeshSurfaceDestroy(DvrVideoMeshSurface* surface);
+
+// Present a VideoMeshSurface with the current video mesh transfromation matrix.
+void dvrGraphicsVideoMeshSurfacePresent(DvrGraphicsContext* graphics_context,
+                                        DvrVideoMeshSurface* surface,
+                                        const int eye,
+                                        const float* transform);
+
+__END_DECLS
+
+#endif  // DVR_GRAPHICS_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..fec2ea5
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -0,0 +1,128 @@
+#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_rpc.h>
+
+namespace android {
+namespace dvr {
+
+struct LateLatchOutput;
+
+// Abstract base class for all surface types maintained in DVR's display
+// service.
+// TODO(jwcai) Explain more, surface is a channel...
+class SurfaceClient : public pdx::Client {
+ public:
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  SurfaceType type() const { return type_; }
+
+  // Get the shared memory metadata buffer fd for this display surface. If it is
+  // not yet allocated, this will allocate it.
+  int GetMetadataBufferFd(pdx::LocalHandle* out_fd);
+
+  // Allocate the single metadata buffer for providing metadata associated with
+  // posted buffers for this surface. This can be used to provide rendered poses
+  // for EDS, for example. The buffer format is defined by the struct
+  // DisplaySurfaceMetadata.
+  // The first call to this method will allocate the buffer in via IPC to the
+  // display surface.
+  std::shared_ptr<BufferProducer> GetMetadataBuffer();
+
+ protected:
+  SurfaceClient(LocalChannelHandle channel_handle, SurfaceType type);
+  SurfaceClient(const std::string& endpoint_path, SurfaceType type);
+
+ private:
+  SurfaceType type_;
+  std::shared_ptr<BufferProducer> metadata_buffer_;
+};
+
+// DisplaySurfaceClient represents the client interface to a displayd display
+// surface.
+class DisplaySurfaceClient
+    : public pdx::ClientBase<DisplaySurfaceClient, SurfaceClient> {
+ public:
+  using LocalHandle = pdx::LocalHandle;
+
+  int width() const { return width_; }
+  int height() const { return height_; }
+  int format() const { return format_; }
+  int usage() const { return usage_; }
+  int flags() const { return flags_; }
+  int z_order() const { return z_order_; }
+  bool visible() const { return visible_; }
+
+  void SetVisible(bool visible);
+  void SetZOrder(int z_order);
+  void SetExcludeFromBlur(bool exclude_from_blur);
+  void SetBlurBehind(bool blur_behind);
+  void SetAttributes(const DisplaySurfaceAttributes& attributes);
+
+  // Get the producer end of the buffer queue that transports graphics buffer
+  // from the application side to the compositor side.
+  std::shared_ptr<ProducerQueue> GetProducerQueue();
+
+  // Get the shared memory metadata buffer for this display surface. If it is
+  // not yet allocated, this will allocate it.
+  volatile DisplaySurfaceMetadata* GetMetadataBufferPtr();
+
+  // Create a VideoMeshSurface that is attached to the display sruface.
+  LocalChannelHandle CreateVideoMeshSurface();
+
+ private:
+  friend BASE;
+
+  DisplaySurfaceClient(int width, int height, int format, int usage, int flags);
+
+  int width_;
+  int height_;
+  int format_;
+  int usage_;
+  int flags_;
+  int z_order_;
+  bool visible_;
+  bool exclude_from_blur_;
+  bool blur_behind_;
+  DisplaySurfaceMetadata* mapped_metadata_buffer_;
+
+  // TODO(jwcai) Add support for multiple queues.
+  std::shared_ptr<ProducerQueue> producer_queue_;
+
+  DisplaySurfaceClient(const DisplaySurfaceClient&) = delete;
+  void operator=(const DisplaySurfaceClient&) = delete;
+};
+
+class DisplayClient : public pdx::ClientBase<DisplayClient> {
+ public:
+  int GetDisplayMetrics(SystemDisplayMetrics* metrics);
+  pdx::Status<void> SetViewerParams(const ViewerParams& viewer_params);
+
+  // Pull the latest eds pose data from the display service renderer
+  int GetLastFrameEdsTransform(LateLatchOutput* ll_out);
+
+  std::unique_ptr<DisplaySurfaceClient> CreateDisplaySurface(
+      int width, int height, int format, int usage, int flags);
+
+  std::unique_ptr<IonBuffer> GetNamedBuffer(const std::string& name);
+
+  // Temporary query for current VR status. Will be removed later.
+  bool IsVrAppRunning();
+
+ private:
+  friend BASE;
+
+  explicit DisplayClient(int* error = nullptr);
+
+  DisplayClient(const DisplayClient&) = delete;
+  void operator=(const DisplayClient&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
new file mode 100644
index 0000000..b0a7d13
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
@@ -0,0 +1,40 @@
+#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_IMPL_H_
+#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_IMPL_H_
+
+#include <vector>
+
+#include <pdx/client.h>
+#include <private/dvr/display_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class BufferProducer;
+
+class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
+ public:
+  ~DisplayManagerClient() override;
+
+  int GetSurfaceList(std::vector<DisplaySurfaceInfo>* surface_list);
+
+  std::unique_ptr<IonBuffer> SetupNamedBuffer(const std::string& name,
+                                              size_t size,
+                                              uint64_t producer_usage,
+                                              uint64_t consumer_usage);
+
+  using Client::event_fd;
+  using Client::GetChannel;
+
+ private:
+  friend BASE;
+
+  DisplayManagerClient();
+
+  DisplayManagerClient(const DisplayManagerClient&) = delete;
+  void operator=(const DisplayManagerClient&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_IMPL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_rpc.h b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
new file mode 100644
index 0000000..c12b090
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
@@ -0,0 +1,347 @@
+#ifndef ANDROID_DVR_DISPLAY_RPC_H_
+#define ANDROID_DVR_DISPLAY_RPC_H_
+
+#include <sys/types.h>
+
+#include <array>
+#include <map>
+
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <pdx/rpc/variant.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/display_types.h>
+
+namespace android {
+namespace dvr {
+
+struct SystemDisplayMetrics {
+  uint32_t display_native_width;
+  uint32_t display_native_height;
+  uint32_t display_x_dpi;
+  uint32_t display_y_dpi;
+  uint32_t distorted_width;
+  uint32_t distorted_height;
+  uint32_t vsync_period_ns;
+  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(SystemDisplayMetrics, display_native_width,
+                           display_native_height, display_x_dpi, display_y_dpi,
+                           distorted_width, distorted_height, vsync_period_ns,
+                           hmd_ipd_mm, inter_lens_distance_m, left_fov_lrbt,
+                           right_fov_lrbt);
+};
+
+using SurfaceType = uint32_t;
+struct SurfaceTypeEnum {
+  enum : SurfaceType {
+    Normal = DVR_SURFACE_TYPE_NORMAL,
+    VideoMesh = DVR_SURFACE_TYPE_VIDEO_MESH,
+    Overlay = DVR_SURFACE_TYPE_OVERLAY,
+  };
+};
+
+using DisplaySurfaceFlags = uint32_t;
+enum class DisplaySurfaceFlagsEnum : DisplaySurfaceFlags {
+  DisableSystemEds = DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS,
+  DisableSystemDistortion = DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION,
+  VerticalFlip = DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP,
+  SeparateGeometry = DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2,
+  DisableSystemCac = DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC,
+};
+
+using DisplaySurfaceInfoFlags = uint32_t;
+enum class DisplaySurfaceInfoFlagsEnum : DisplaySurfaceInfoFlags {
+  BuffersChanged = DVR_DISPLAY_SURFACE_ITEM_FLAGS_BUFFERS_CHANGED,
+};
+
+using DisplaySurfaceAttributeValue =
+    pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>,
+                      std::array<float, 3>, std::array<float, 4>,
+                      std::array<float, 16>>;
+using DisplaySurfaceAttribute = uint32_t;
+struct DisplaySurfaceAttributeEnum {
+  enum : DisplaySurfaceAttribute {
+    ZOrder = DVR_DISPLAY_SURFACE_ATTRIBUTE_Z_ORDER,
+    Visible = DVR_DISPLAY_SURFACE_ATTRIBUTE_VISIBLE,
+    // Manager only.
+    Blur = DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR,
+    // Client only.
+    ExcludeFromBlur = DVR_DISPLAY_SURFACE_ATTRIBUTE_EXCLUDE_FROM_BLUR,
+    BlurBehind = DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR_BEHIND,
+  };
+
+  static std::string ToString(DisplaySurfaceAttribute attribute) {
+    switch (attribute) {
+      case ZOrder:
+        return "z-order";
+      case Visible:
+        return "visible";
+      case Blur:
+        return "blur";
+      case ExcludeFromBlur:
+        return "exclude-from-blur";
+      case BlurBehind:
+        return "blur-behind";
+      default:
+        return "unknown";
+    }
+  }
+};
+
+using DisplaySurfaceAttributes =
+    std::map<DisplaySurfaceAttribute, DisplaySurfaceAttributeValue>;
+
+struct DisplaySurfaceInfo {
+  int surface_id;
+  int process_id;
+  SurfaceType type;
+  DisplaySurfaceFlags flags;
+  DisplaySurfaceInfoFlags info_flags;
+  DisplaySurfaceAttributes client_attributes;
+  DisplaySurfaceAttributes manager_attributes;
+
+  // Convenience accessors.
+  bool IsClientVisible() const {
+    const auto* variant =
+        FindClientAttribute(DisplaySurfaceAttributeEnum::Visible);
+    bool bool_value;
+    if (variant && pdx::rpc::IfAnyOf<int32_t, int64_t, bool, float>::Get(
+                       variant, &bool_value))
+      return bool_value;
+    else
+      return false;
+  }
+
+  int ClientZOrder() const {
+    const auto* variant =
+        FindClientAttribute(DisplaySurfaceAttributeEnum::ZOrder);
+    int int_value;
+    if (variant &&
+        pdx::rpc::IfAnyOf<int32_t, int64_t, float>::Get(variant, &int_value))
+      return int_value;
+    else
+      return 0;
+  }
+
+ private:
+  const DisplaySurfaceAttributeValue* FindClientAttribute(
+      DisplaySurfaceAttribute key) const {
+    auto search = client_attributes.find(key);
+    return (search != client_attributes.end()) ? &search->second : nullptr;
+  }
+
+  PDX_SERIALIZABLE_MEMBERS(DisplaySurfaceInfo, surface_id, process_id, type,
+                           flags, info_flags, client_attributes,
+                           manager_attributes);
+};
+
+struct VideoMeshSurfaceBufferMetadata {
+  int64_t timestamp_ns;
+};
+
+struct AlignmentMarker {
+ public:
+  float horizontal;
+  float vertical;
+
+  PDX_SERIALIZABLE_MEMBERS(AlignmentMarker, horizontal, vertical);
+};
+
+struct DaydreamInternalParams {
+ public:
+  int32_t version;
+  std::vector<AlignmentMarker> alignment_markers;
+
+  PDX_SERIALIZABLE_MEMBERS(DaydreamInternalParams, version, alignment_markers);
+};
+
+struct ViewerParams {
+ public:
+  // TODO(hendrikw): Do we need viewer_vendor_name and viewer_model_name?
+  float screen_to_lens_distance;
+  float inter_lens_distance;
+  float screen_center_to_lens_distance;
+  std::vector<float> left_eye_field_of_view_angles;
+
+  enum VerticalAlignmentType : int32_t {
+    BOTTOM = 0,  // phone rests against a fixed bottom tray
+    CENTER = 1,  // phone screen assumed to be centered w.r.t. lenses
+    TOP = 2      // phone rests against a fixed top tray
+  };
+
+  enum EyeOrientation : int32_t {
+    kCCW0Degrees = 0,
+    kCCW90Degrees = 1,
+    kCCW180Degrees = 2,
+    kCCW270Degrees = 3,
+    kCCW0DegreesMirrored = 4,
+    kCCW90DegreesMirrored = 5,
+    kCCW180DegreesMirrored = 6,
+    kCCW270DegreesMirrored = 7
+  };
+
+  VerticalAlignmentType vertical_alignment;
+  std::vector<EyeOrientation> eye_orientations;
+
+  float tray_to_lens_distance;
+
+  std::vector<float> distortion_coefficients_r;
+  std::vector<float> distortion_coefficients_g;
+  std::vector<float> distortion_coefficients_b;
+
+  DaydreamInternalParams daydream_internal;
+
+  PDX_SERIALIZABLE_MEMBERS(ViewerParams, screen_to_lens_distance,
+                           inter_lens_distance, screen_center_to_lens_distance,
+                           left_eye_field_of_view_angles, vertical_alignment,
+                           eye_orientations, tray_to_lens_distance,
+                           distortion_coefficients_r, distortion_coefficients_g,
+                           distortion_coefficients_b, daydream_internal);
+};
+
+struct DisplayRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/vr/display/client";
+
+  // Op codes.
+  enum {
+    kOpGetMetrics = 0,
+    kOpGetEdsCapture,
+    kOpCreateSurface,
+    kOpCreateBufferQueue,
+    kOpSetAttributes,
+    kOpGetMetadataBuffer,
+    kOpCreateVideoMeshSurface,
+    kOpVideoMeshSurfaceCreateProducerQueue,
+    kOpSetViewerParams,
+    kOpGetNamedBuffer,
+    kOpIsVrAppRunning,
+  };
+
+  // Aliases.
+  using ByteBuffer = pdx::rpc::BufferWrapper<std::vector<uint8_t>>;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  using Void = pdx::rpc::Void;
+
+  // Methods.
+  PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, SystemDisplayMetrics(Void));
+  PDX_REMOTE_METHOD(GetEdsCapture, kOpGetEdsCapture, ByteBuffer(Void));
+  PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
+                    int(int width, int height, int format, int usage,
+                        DisplaySurfaceFlags flags));
+  PDX_REMOTE_METHOD(CreateBufferQueue, kOpCreateBufferQueue,
+                    LocalChannelHandle(Void));
+  PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
+                    int(const DisplaySurfaceAttributes& attributes));
+  PDX_REMOTE_METHOD(GetMetadataBuffer, kOpGetMetadataBuffer,
+                    LocalChannelHandle(Void));
+  // VideoMeshSurface methods
+  PDX_REMOTE_METHOD(CreateVideoMeshSurface, kOpCreateVideoMeshSurface,
+                    LocalChannelHandle(Void));
+  PDX_REMOTE_METHOD(VideoMeshSurfaceCreateProducerQueue,
+                    kOpVideoMeshSurfaceCreateProducerQueue,
+                    LocalChannelHandle(Void));
+  PDX_REMOTE_METHOD(SetViewerParams, kOpSetViewerParams,
+                    void(const ViewerParams& viewer_params));
+  PDX_REMOTE_METHOD(GetNamedBuffer, kOpGetNamedBuffer,
+                    LocalNativeBufferHandle(const std::string& name));
+  PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, int(Void));
+};
+
+struct DisplayManagerRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/vr/display/manager";
+
+  // Op codes.
+  enum {
+    kOpGetSurfaceList = 0,
+    kOpUpdateSurfaces,
+    kOpSetupNamedBuffer,
+  };
+
+  // Aliases.
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  using Void = pdx::rpc::Void;
+
+  // Methods.
+  PDX_REMOTE_METHOD(GetSurfaceList, kOpGetSurfaceList,
+                    std::vector<DisplaySurfaceInfo>(Void));
+  PDX_REMOTE_METHOD(
+      UpdateSurfaces, kOpUpdateSurfaces,
+      int(const std::map<int, DisplaySurfaceAttributes>& updates));
+  PDX_REMOTE_METHOD(SetupNamedBuffer, kOpSetupNamedBuffer,
+                    LocalNativeBufferHandle(const std::string& name,
+                                            size_t size,
+                                            uint64_t producer_usage,
+                                            uint64_t consumer_usage));
+};
+
+struct ScreenshotData {
+  int width;
+  int height;
+  std::vector<uint8_t> buffer;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(ScreenshotData, width, height, buffer);
+};
+
+struct DisplayScreenshotRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/vr/display/screenshot";
+
+  // Op codes.
+  enum {
+    kOpGetFormat = 0,
+    kOpTakeScreenshot,
+  };
+
+  using Void = pdx::rpc::Void;
+
+  PDX_REMOTE_METHOD(GetFormat, kOpGetFormat, int(Void));
+  PDX_REMOTE_METHOD(TakeScreenshot, kOpTakeScreenshot,
+                    ScreenshotData(int layer_index));
+};
+
+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 DisplayVSyncRPC {
+  // 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, int(Void));
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_RPC_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_types.h b/libs/vr/libdisplay/include/private/dvr/display_types.h
new file mode 100644
index 0000000..2bd02bd
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_types.h
@@ -0,0 +1,83 @@
+#ifndef ANDROID_DVR_DISPLAY_TYPES_H_
+#define ANDROID_DVR_DISPLAY_TYPES_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 <cutils/native_handle.h>
+
+// DVR display-related data types.
+
+enum dvr_display_surface_type {
+  // Normal display surface meant to be used by applications' GL context to
+  // render into.
+  DVR_SURFACE_TYPE_NORMAL = 0,
+
+  // VideoMeshSurface is used to composite video frames into the 3D world.
+  DVR_SURFACE_TYPE_VIDEO_MESH,
+
+  // System overlay surface type. This is not currently in use.
+  DVR_SURFACE_TYPE_OVERLAY,
+};
+
+enum dvr_display_surface_flags {
+  DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS = (1 << 0),
+  DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION = (1 << 1),
+  DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP = (1 << 2),
+  DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2 = (1 << 3),
+  DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC = (1 << 4),
+};
+
+enum dvr_display_surface_item_flags {
+  DVR_DISPLAY_SURFACE_ITEM_FLAGS_BUFFERS_CHANGED = (1 << 0),
+};
+
+enum dvr_display_surface_attribute {
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_Z_ORDER = (1<<0),
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_VISIBLE = (1<<1),
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR = (1<<2),
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_EXCLUDE_FROM_BLUR = (1<<3),
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR_BEHIND = (1<<4),
+};
+
+// Maximum number of buffers for a surface. Each buffer represents a single
+// frame and may actually be a buffer array if multiview rendering is in use.
+// Define so that it can be used in shader code.
+#define kSurfaceBufferMaxCount 4
+
+// Maximum number of views per surface. Each eye is a view, for example.
+#define kSurfaceViewMaxCount 4
+
+namespace android {
+namespace dvr {
+
+struct __attribute__((packed, aligned(16))) DisplaySurfaceMetadata {
+  // Array of orientations and translations corresponding with surface buffers.
+  // The index is associated with each allocated buffer by DisplaySurface and
+  // communicated to clients.
+  // The maximum number of buffers is hard coded here as 4 so that we can bind
+  // this data structure in GPU shaders.
+  float32x4_t orientation[kSurfaceBufferMaxCount];
+  float32x4_t translation[kSurfaceBufferMaxCount];
+};
+
+struct __attribute__((packed, aligned(16))) VideoMeshSurfaceMetadata {
+  // Array of transform matrices corresponding with surface buffers.
+  // Note that The index is associated with each allocated buffer by
+  // DisplaySurface instead of VideoMeshSurface due to the fact that the
+  // metadata here is interpreted as video mesh's transformation in each
+  // application's rendering frame.
+  float32x4x4_t transform[4][2];
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_TYPES_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h b/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h
new file mode 100644
index 0000000..b03eeaa
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h
@@ -0,0 +1,30 @@
+#ifndef ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
+#define ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
+
+#include <android/native_window.h>
+#include <ui/ANativeObjectBase.h>
+
+namespace android {
+namespace dvr {
+
+// DummyNativeWindow is an implementation of ANativeWindow that is
+// essentially empty and is used as a surface placeholder during context
+// creation for contexts that we don't intend to call eglSwapBuffers on.
+class DummyNativeWindow
+    : public ANativeObjectBase<ANativeWindow, DummyNativeWindow,
+                               LightRefBase<DummyNativeWindow> > {
+ public:
+  DummyNativeWindow();
+
+ private:
+  static int Query(const ANativeWindow* window, int what, int* value);
+  static int Perform(ANativeWindow* window, int operation, ...);
+
+  DummyNativeWindow(const DummyNativeWindow&) = delete;
+  void operator=(DummyNativeWindow&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/frame_history.h b/libs/vr/libdisplay/include/private/dvr/frame_history.h
new file mode 100644
index 0000000..53e0717
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/frame_history.h
@@ -0,0 +1,71 @@
+#ifndef ANDROID_DVR_FRAME_HISTORY_H_
+#define ANDROID_DVR_FRAME_HISTORY_H_
+
+#include <dvr/graphics.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/ring_buffer.h>
+
+namespace android {
+namespace dvr {
+
+// FrameHistory tracks frame times from the start of rendering commands to when
+// the buffer is ready.
+class FrameHistory {
+ public:
+  FrameHistory();
+  explicit FrameHistory(int pending_frame_buffer_size);
+
+  void Reset(int pending_frame_buffer_size);
+
+  // Call when starting rendering commands (i.e. dvrBeginRenderFrame).
+  void OnFrameStart(uint32_t scheduled_vsync, int64_t scheduled_finish_ns);
+
+  // Call when rendering commands are finished (i.e. dvrPresent).
+  void OnFrameSubmit(android::pdx::LocalHandle&& fence);
+
+  // Call once per frame to see if any pending frames have finished.
+  void CheckForFinishedFrames();
+
+  // Uses the recently completed frame render times to predict how long the next
+  // frame will take, in vsync intervals. For example if the predicted frame
+  // time is 10ms and the vsync interval is 11ms, this will return 1. If the
+  // predicted frame time is 12ms and the vsync interval is 11ms, this will
+  // return 2.
+  int PredictNextFrameVsyncInterval(int64_t vsync_period_ns) const;
+
+  // Returns results for recently completed frames. Each frame's result is
+  // returned only once.
+  int GetPreviousFrameResults(DvrFrameScheduleResult* results,
+                              int result_count);
+
+  // Gets the vsync count for the most recently started frame. If there are no
+  // started frames this will return UINT32_MAX.
+  uint32_t GetCurrentFrameVsync() const;
+
+ private:
+  struct PendingFrame {
+    int64_t start_ns;
+    uint32_t scheduled_vsync;
+    int64_t scheduled_finish_ns;
+    android::pdx::LocalHandle fence;
+
+    PendingFrame();
+    PendingFrame(int64_t start_ns, uint32_t scheduled_vsync,
+                 int64_t scheduled_finish_ns,
+                 android::pdx::LocalHandle&& fence);
+
+    PendingFrame(PendingFrame&&) = default;
+    PendingFrame& operator=(PendingFrame&&) = default;
+    PendingFrame(const PendingFrame&) = delete;
+    PendingFrame& operator=(const PendingFrame&) = delete;
+  };
+
+  RingBuffer<PendingFrame> pending_frames_;
+  RingBuffer<DvrFrameScheduleResult> finished_frames_;
+  RingBuffer<int64_t> frame_duration_history_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_FRAME_HISTORY_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h b/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h
new file mode 100644
index 0000000..1d75335
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_DVR_GL_FENCED_FLUSH_H_
+#define ANDROID_DVR_GL_FENCED_FLUSH_H_
+
+#include <EGL/egl.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace dvr {
+
+// Creates a EGL_SYNC_NATIVE_FENCE_ANDROID and flushes. Returns the fence as a
+// file descriptor.
+pdx::LocalHandle CreateGLSyncAndFlush(EGLDisplay display);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GL_FENCED_FLUSH_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/graphics_private.h b/libs/vr/libdisplay/include/private/dvr/graphics_private.h
new file mode 100644
index 0000000..57c99da
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/graphics_private.h
@@ -0,0 +1,39 @@
+#ifndef ANDROID_DVR_GRAPHICS_PRIVATE_H_
+#define ANDROID_DVR_GRAPHICS_PRIVATE_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 <sys/cdefs.h>
+
+#include <dvr/graphics.h>
+
+__BEGIN_DECLS
+
+// Sets the pose used by the system for EDS. If dvrBeginRenderFrameEds() or
+// dvrBeginRenderFrameLateLatch() are called instead of dvrBeginRenderFrame()
+// it's not necessary to call this function. If this function is used, the call
+// must be made after dvrBeginRenderFrame() and before dvrPresent().
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @param[in] render_pose_orientation Head pose orientation that rendering for
+//            this frame will be based off of. This must be an unmodified value
+//            from DvrPoseAsync, returned by dvrPoseGet.
+// @param[in] render_pose_translation Head pose translation that rendering for
+//            this frame will be based off of. This must be an unmodified value
+//            from DvrPoseAsync, returned by dvrPoseGet.
+// @return 0 on success or a negative error code on failure.
+int dvrSetEdsPose(DvrGraphicsContext* graphics_context,
+                  float32x4_t render_pose_orientation,
+                  float32x4_t render_pose_translation);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_GRAPHICS_PRIVATE_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/late_latch.h b/libs/vr/libdisplay/include/private/dvr/late_latch.h
new file mode 100644
index 0000000..b7c5e4f
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/late_latch.h
@@ -0,0 +1,189 @@
+#ifndef ANDROID_DVR_LATE_LATCH_H_
+#define ANDROID_DVR_LATE_LATCH_H_
+
+#include <atomic>
+#include <thread>
+#include <vector>
+
+#include <dvr/pose_client.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+#include <private/dvr/types.h>
+
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+// Input data for late latch compute shader.
+struct LateLatchInput {
+  // For app late latch:
+  mat4 eye_from_head_mat[kSurfaceViewMaxCount];
+  mat4 proj_mat[kSurfaceViewMaxCount];
+  mat4 pose_offset[kSurfaceViewMaxCount];
+  // For EDS late latch only:
+  mat4 eds_mat1[kSurfaceViewMaxCount];
+  mat4 eds_mat2[kSurfaceViewMaxCount];
+  // For both app and EDS late latch:
+  uint32_t pose_index;
+  uint32_t render_pose_index;
+};
+
+// Output data for late latch shader. The application can use all or part of
+// this data by calling LateLatch::BindUniformBuffer.
+// This struct matches the layout of DvrGraphicsLateLatchData.
+struct LateLatchOutput {
+  mat4 view_proj_matrix[kSurfaceViewMaxCount];
+  mat4 view_matrix[kSurfaceViewMaxCount];
+  vec4 pose_quaternion;
+  vec4 pose_translation;
+};
+
+// LateLatch provides a facility for GL workloads to acquire a late-adjusted
+// model-view projection matrix, adjusted based on the position/quaternion pose
+// read from a buffer that is being written to asynchronously. The adjusted
+// MVP matrix is written to a GL buffer object via GL transform feedback.
+class LateLatch {
+ public:
+  enum BufferType {
+    kViewProjMatrix,
+    kViewMatrix,
+    kPoseQuaternion,
+    kPoseTranslation,
+    // Max transform feedback count is 4, so no more buffers can go here.
+    kNumBuffers,
+  };
+
+  static size_t GetBufferSize(BufferType type) {
+    switch (type) {
+      default:
+      case kViewProjMatrix:
+      case kViewMatrix:
+        return 4 * 4 * sizeof(float);
+      case kPoseQuaternion:
+      case kPoseTranslation:
+        return 4 * sizeof(float);
+    }
+  }
+
+  static size_t GetBufferOffset(BufferType type, int view) {
+    switch (type) {
+      default:
+      case kViewProjMatrix:
+        return offsetof(LateLatchOutput, view_proj_matrix) +
+               GetBufferSize(type) * view;
+      case kViewMatrix:
+        return offsetof(LateLatchOutput, view_matrix) +
+               GetBufferSize(type) * view;
+      case kPoseQuaternion:
+        return offsetof(LateLatchOutput, pose_quaternion);
+      case kPoseTranslation:
+        return offsetof(LateLatchOutput, pose_translation);
+    }
+  }
+
+  explicit LateLatch(bool is_app_late_latch);
+  LateLatch(bool is_app_late_latch, pdx::LocalHandle&& surface_metadata_fd);
+  ~LateLatch();
+
+  // Bind the late-latch output data as a GL_UNIFORM_BUFFER. For example,
+  // to bind just the view_matrix from the output:
+  // BindUniformBuffer(BINDING, offsetof(LateLatchOutput, view_matrix),
+  //                   sizeof(mat4));
+  // buffer_index is the index of one of the output buffers if more than 1 were
+  // requested in the constructor.
+  void BindUniformBuffer(GLuint ubo_binding, size_t offset, size_t size) const {
+    glBindBufferRange(GL_UNIFORM_BUFFER, ubo_binding, output_buffer_id_, offset,
+                      size);
+  }
+
+  void BindUniformBuffer(GLuint ubo_binding, BufferType type, int view) const {
+    glBindBufferRange(GL_UNIFORM_BUFFER, ubo_binding, output_buffer_id_,
+                      GetBufferOffset(type, view), GetBufferSize(type));
+  }
+
+  GLuint output_buffer_id() const { return output_buffer_id_; }
+
+  void UnbindUniformBuffer(GLuint ubo_binding) const {
+    glBindBufferBase(GL_UNIFORM_BUFFER, ubo_binding, 0);
+  }
+
+  void CaptureOutputData(LateLatchOutput* data) const;
+
+  // Add the late latch GL commands for this frame. This should be done just
+  // before the first application draw calls that are dependent on the head
+  // latest head pose.
+  //
+  // For efficiency, the application projection and eye_from_head matrices are
+  // passed through the late latch shader and output in various combinations to
+  // allow for both simple application vertex shaders that can take the view-
+  // projection matrix as-is and shaders that need to access the view matrix
+  // separately.
+  //
+  // GL state must be reset to default for this call.
+  void AddLateLatch(const LateLatchInput& data) const;
+
+  // After calling AddEdsLateLatch one or more times, this method must be called
+  // to add the necessary GL memory barrier to ensure late latch outputs are
+  // written before the EDS and warp shaders read them.
+  void PostEdsLateLatchBarrier() const {
+    // The transform feedback buffer is going to be read as a uniform by EDS,
+    // so we need a uniform memory barrier.
+    glMemoryBarrier(GL_UNIFORM_BARRIER_BIT);
+  }
+
+  // Typically not for use by application code. This method adds the EDS late
+  // latch that will adjust the application framebuffer with the latest head
+  // pose.
+  // buffer_index is the index of one of the output buffers if more than 1 were
+  // requested in the constructor.
+  void AddEdsLateLatch(const LateLatchInput& data,
+                       GLuint render_pose_buffer_object) const;
+
+  // For debugging purposes, capture the output during the next call to
+  // AddLateLatch. Set to NULL to reset.
+  void SetLateLatchDataCapture(LateLatchOutput* app_late_latch) {
+    app_late_latch_output_ = app_late_latch;
+  }
+
+  // For debugging purposes, capture the output during the next call to
+  // AddEdsLateLatch. Set to NULL to reset.
+  void SetEdsLateLatchDataCapture(LateLatchOutput* eds_late_latch) {
+    eds_late_latch_output_ = eds_late_latch;
+  }
+
+ private:
+  LateLatch(const LateLatch&) = delete;
+  LateLatch& operator=(const LateLatch&) = delete;
+
+  void LoadLateLatchShader();
+
+  // Late latch shader.
+  ShaderProgram late_latch_program_;
+
+  // Async pose ring buffer object.
+  GLuint pose_buffer_object_;
+
+  GLuint metadata_buffer_id_;
+
+  // Pose matrix buffers
+  GLuint input_buffer_id_;
+  GLuint output_buffer_id_;
+
+  bool is_app_late_latch_;
+  // During development, these can be used to capture the pose output data.
+  LateLatchOutput* app_late_latch_output_;
+  LateLatchOutput* eds_late_latch_output_;
+
+  DvrPose* pose_client_;
+
+  pdx::LocalHandle surface_metadata_fd_;
+  pdx::LocalHandle pose_buffer_fd_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LATE_LATCH_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/native_buffer_queue.h b/libs/vr/libdisplay/include/private/dvr/native_buffer_queue.h
new file mode 100644
index 0000000..4b1fa98
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/native_buffer_queue.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_QUEUE_H_
+#define ANDROID_DVR_NATIVE_BUFFER_QUEUE_H_
+
+#include <semaphore.h>
+
+#include <mutex>
+#include <vector>
+
+#include <private/dvr/native_buffer.h>
+#include <private/dvr/ring_buffer.h>
+
+#include "display_client.h"
+
+namespace android {
+namespace dvr {
+
+// A wrapper over dvr::ProducerQueue that caches EGLImage.
+class NativeBufferQueue {
+ public:
+  // Create a queue with the given number of free buffers.
+  NativeBufferQueue(EGLDisplay display,
+                    const std::shared_ptr<DisplaySurfaceClient>& surface,
+                    size_t capacity);
+
+  size_t GetQueueCapacity() const { return producer_queue_->capacity(); }
+
+  // Dequeue a buffer from the free queue, blocking until one is available.
+  NativeBufferProducer* Dequeue();
+
+  // An noop here to keep Vulkan path in GraphicsContext happy.
+  // TODO(jwcai, cort) Move Vulkan path into GVR/Google3.
+  void Enqueue(NativeBufferProducer* buffer) {}
+
+ private:
+  EGLDisplay display_;
+  std::shared_ptr<ProducerQueue> producer_queue_;
+  std::vector<sp<NativeBufferProducer>> buffers_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NATIVE_BUFFER_QUEUE_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/screenshot_client.h b/libs/vr/libdisplay/include/private/dvr/screenshot_client.h
new file mode 100644
index 0000000..b6fc859
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/screenshot_client.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_DVR_SCREENSHOT_CLIENT_H_
+#define ANDROID_DVR_SCREENSHOT_CLIENT_H_
+
+#include <memory>
+#include <vector>
+
+#include <pdx/client.h>
+#include <private/dvr/display_rpc.h>
+#include <system/graphics.h>
+
+namespace android {
+namespace dvr {
+
+// Represents a connection to the screenshot service, which allows capturing an
+// upcoming frame as it is being rendered to the display.
+class ScreenshotClient : public pdx::ClientBase<ScreenshotClient> {
+ public:
+  int format() const { return format_; }
+
+  // Attempts to take a screenshot. If successful, sets *data to the contents
+  // of the screenshot and returns zero. Otherwise, returns a negative error
+  // code.
+  // |index| is used to match the requested buffer with various buffer layers.
+  int Take(std::vector<uint8_t>* data, int index, int* return_width,
+           int* return_height);
+
+ private:
+  friend BASE;
+
+  ScreenshotClient();
+
+  // Layout information for screenshots.
+  int format_;
+
+  ScreenshotClient(const ScreenshotClient&) = delete;
+  void operator=(const ScreenshotClient&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SCREENSHOT_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h b/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
new file mode 100644
index 0000000..3a7f125
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
@@ -0,0 +1,41 @@
+#ifndef ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
+#define ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
+
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_client.h>
+
+namespace android {
+namespace dvr {
+
+class VideoMeshSurfaceClient
+    : pdx::ClientBase<VideoMeshSurfaceClient, SurfaceClient> {
+ public:
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+
+  // This call assumes ownership of |handle|.
+  static std::unique_ptr<VideoMeshSurfaceClient> Import(
+      LocalChannelHandle handle);
+
+  std::shared_ptr<ProducerQueue> GetProducerQueue();
+
+  // Get the shared memory metadata buffer for this video mesh surface. If it is
+  // not yet allocated, this will allocate it.
+  volatile VideoMeshSurfaceMetadata* GetMetadataBufferPtr();
+
+ private:
+  friend BASE;
+
+  std::shared_ptr<ProducerQueue> producer_queue_;
+  VideoMeshSurfaceMetadata* mapped_metadata_buffer_;
+
+  explicit VideoMeshSurfaceClient(LocalChannelHandle handle);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+struct DvrVideoMeshSurface {
+  std::shared_ptr<android::dvr::VideoMeshSurfaceClient> client;
+};
+
+#endif  // ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_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/late_latch.cpp b/libs/vr/libdisplay/late_latch.cpp
new file mode 100644
index 0000000..e67f009
--- /dev/null
+++ b/libs/vr/libdisplay/late_latch.cpp
@@ -0,0 +1,460 @@
+#include "include/private/dvr/late_latch.h"
+
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <log/log.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sensor_constants.h>
+#include <private/dvr/types.h>
+
+#define PRINT_MATRIX 0
+
+#if PRINT_MATRIX
+#ifndef LOG_TAG
+#define LOG_TAG "latelatch"
+#endif
+
+#define PE(str, ...)                                                  \
+  fprintf(stderr, "[%s:%d] " str, __FILE__, __LINE__, ##__VA_ARGS__); \
+  ALOGI("[%s:%d] " str, __FILE__, __LINE__, ##__VA_ARGS__)
+
+#define PV4(v) PE(#v "=%f,%f,%f,%f\n", v[0], v[1], v[2], v[3]);
+#define PM4(m)                                                               \
+  PE(#m ":\n %f,%f,%f,%f\n %f,%f,%f,%f\n %f,%f,%f,%f\n %f,%f,%f,%f\n",       \
+     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))
+#endif  // PRINT_MATRIX
+
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+
+// Compute shader bindings.
+// GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS must be at least 8 for GLES 3.1.
+#define POSE_BINDING 0
+#define RENDER_POSE_BINDING 1
+#define INPUT_BINDING 2
+#define OUTPUT_BINDING 3
+
+using android::pdx::LocalHandle;
+
+namespace {
+
+static const std::string kShaderLateLatch = R"(  // NOLINT
+  struct Pose {
+    vec4 quat;
+    vec3 pos;
+  };
+
+  // Must match DvrPoseAsync C struct.
+  struct DvrPoseAsync {
+    vec4 orientation;
+    vec4 translation;
+    vec4 right_orientation;
+    vec4 right_translation;
+    vec4 angular_velocity;
+    vec4 velocity;
+    vec4 reserved[2];
+  };
+
+  // Must match LateLatchInputData C struct.
+  layout(binding = INPUT_BINDING, std140)
+  buffer InputData {
+    mat4 uEyeFromHeadMat[kSurfaceViewMaxCount];
+    mat4 uProjMat[kSurfaceViewMaxCount];
+    mat4 uPoseOffset[kSurfaceViewMaxCount];
+    mat4 uEdsMat1[kSurfaceViewMaxCount];
+    mat4 uEdsMat2[kSurfaceViewMaxCount];
+    uint uPoseIndex;
+    uint uRenderPoseIndex;
+  } bIn;
+
+  // std140 is to layout the structure in a consistent, standard way so we
+  // can access it from C++.
+  // This structure exactly matches the pose ring buffer in pose_client.h.
+  layout(binding = POSE_BINDING, std140)
+  buffer PoseBuffer {
+    DvrPoseAsync data[kPoseAsyncBufferTotalCount];
+  } bPose;
+
+  // Must stay in sync with DisplaySurfaceMetadata C struct.
+  // GPU thread 0 will exclusively read in a pose and capture it
+  // into this array.
+  layout(binding = RENDER_POSE_BINDING, std140)
+  buffer DisplaySurfaceMetadata {
+    vec4 orientation[kSurfaceBufferMaxCount];
+    vec4 translation[kSurfaceBufferMaxCount];
+  } bSurfaceData;
+
+  // Must stay in sync with DisplaySurfaceMetadata C struct.
+  // Each thread writes to a vertic
+  layout(binding = OUTPUT_BINDING, std140)
+  buffer Output {
+    mat4 viewProjMatrix[kSurfaceViewMaxCount];
+    mat4 viewMatrix[kSurfaceViewMaxCount];
+    vec4 quaternion;
+    vec4 translation;
+  } bOut;
+
+  // Thread 0 will also store the single quat/pos pair in shared variables
+  // for the other threads to use (left and right eye in this array).
+  shared Pose sharedPose[2];
+
+  // Rotate v1 by the given quaternion. This is based on mathfu's
+  // Quaternion::Rotate function. It is the typical implementation of this
+  // operation. Eigen has a similar method (Quaternion::_transformVector) that
+  // supposedly requires fewer operations, but I am skeptical of optimizing
+  // shader code without proper profiling first.
+  vec3 rotate(vec4 quat, vec3 v1) {
+    float ss = 2.0 * quat.w;
+    vec3 v = quat.xyz;
+    return ss * cross(v, v1) + (ss * quat.w - 1.0) * v1 +
+           2.0 * dot(v, v1) * v;
+  }
+
+  // See Eigen Quaternion::conjugate;
+  // Note that this isn't a true multiplicative inverse unless you can guarantee
+  // quat is also normalized, but that typically isn't an issue for our
+  // purposes.
+  vec4 quatInvert(vec4 quat) {
+    return vec4(-quat.xyz, quat.w);
+  }
+
+  // This is based on mathfu's Quaternion::operator*(Quaternion)
+  // Eigen's version is mathematically equivalent, just notationally different.
+  vec4 quatMul(vec4 q1, vec4 q2) {
+    return vec4(q1.w * q2.xyz + q2.w * q1.xyz + cross(q1.xyz, q2.xyz),
+                q1.w * q2.w - dot(q1.xyz, q2.xyz));
+  }
+
+  // Equivalent to pose.h GetObjectFromReferenceMatrix.
+  mat4 getInverseMatrix(Pose pose) {
+    // Invert quaternion and store fields the way Eigen does so we can
+    // keep in sync with Eigen methods easier.
+    vec4 quatInv = quatInvert(pose.quat);
+    vec3 v = quatInv.xyz;
+    float s = quatInv.w;
+    // Convert quaternion to matrix. See Eigen Quaternion::toRotationMatrix()
+    float x2 = v.x * v.x, y2 = v.y * v.y, z2 = v.z * v.z;
+    float sx = s * v.x, sy = s * v.y, sz = s * v.z;
+    float xz = v.x * v.z, yz = v.y * v.z, xy = v.x * v.y;
+    // Inverse translation.
+    vec3 point = -pose.pos;
+
+    return
+      mat4(1.0 - 2.0 * (y2 + z2), 2.0 * (xy + sz), 2.0 * (xz - sy), 0.0,
+           2.0 * (xy - sz), 1.0 - 2.0 * (x2 + z2), 2.0 * (sx + yz), 0.0,
+           2.0 * (sy + xz), 2.0 * (yz - sx), 1.0 - 2.0 * (x2 + y2), 0.0,
+           0.0, 0.0, 0.0, 1.0)*
+      mat4(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
+           point.x, point.y, point.z, 1.0);
+  }
+
+  void appLateLatch() {
+    uint poseIndex = (gl_LocalInvocationIndex & uint(1));
+    mat4 head_from_center = getInverseMatrix(sharedPose[poseIndex]);
+    bOut.viewMatrix[gl_LocalInvocationIndex] =
+        bIn.uEyeFromHeadMat[gl_LocalInvocationIndex] *
+        head_from_center * bIn.uPoseOffset[gl_LocalInvocationIndex];
+    bOut.viewProjMatrix[gl_LocalInvocationIndex] =
+        bIn.uProjMat[gl_LocalInvocationIndex] *
+        bOut.viewMatrix[gl_LocalInvocationIndex];
+  }
+
+  // Extract the app frame's pose.
+  Pose getPoseFromApp() {
+    Pose p;
+    p.quat = bSurfaceData.orientation[bIn.uRenderPoseIndex];
+    p.pos =  bSurfaceData.translation[bIn.uRenderPoseIndex].xyz;
+    return p;
+  }
+
+  // See Posef::GetPoseOffset.
+  Pose getPoseOffset(Pose p1, Pose p2) {
+    Pose p;
+    p.quat = quatMul(quatInvert(p2.quat), p1.quat);
+    // TODO(jbates) Consider enabling positional EDS when it is better
+    //              tested.
+    // p.pos = p2.pos - p1.pos;
+    p.pos = vec3(0.0);
+    return p;
+  }
+
+  void edsLateLatch() {
+    Pose pose1 = getPoseFromApp();
+    Pose correction;
+    // Ignore the texture pose if the quat is not unit-length.
+    float tex_quat_length = length(pose1.quat);
+    uint poseIndex = (gl_LocalInvocationIndex & uint(1));
+    if (abs(tex_quat_length - 1.0) < 0.001)
+      correction = getPoseOffset(pose1, sharedPose[poseIndex]);
+    else
+      correction = Pose(vec4(0, 0, 0, 1), vec3(0, 0, 0));
+    mat4 eye_old_from_eye_new_matrix = getInverseMatrix(correction);
+    bOut.viewProjMatrix[gl_LocalInvocationIndex] =
+        bIn.uEdsMat1[gl_LocalInvocationIndex] *
+        eye_old_from_eye_new_matrix * bIn.uEdsMat2[gl_LocalInvocationIndex];
+    // Currently unused, except for debugging:
+    bOut.viewMatrix[gl_LocalInvocationIndex] = eye_old_from_eye_new_matrix;
+  }
+
+  // One thread per surface view.
+  layout (local_size_x = kSurfaceViewMaxCount, local_size_y = 1,
+          local_size_z = 1) in;
+
+  void main() {
+    // First, thread 0 late latches pose and stores it into various places.
+    if (gl_LocalInvocationIndex == uint(0)) {
+      sharedPose[0].quat = bPose.data[bIn.uPoseIndex].orientation;
+      sharedPose[0].pos =  bPose.data[bIn.uPoseIndex].translation.xyz;
+      sharedPose[1].quat = bPose.data[bIn.uPoseIndex].right_orientation;
+      sharedPose[1].pos =  bPose.data[bIn.uPoseIndex].right_translation.xyz;
+      if (IS_APP_LATE_LATCH) {
+        bSurfaceData.orientation[bIn.uRenderPoseIndex] = sharedPose[0].quat;
+        bSurfaceData.translation[bIn.uRenderPoseIndex] = vec4(sharedPose[0].pos, 0.0);
+        // TODO(jbates) implement app late-latch support for separate eye poses.
+        // App late latch currently uses the same pose for both eye views.
+        sharedPose[1] = sharedPose[0];
+      }
+      bOut.quaternion = sharedPose[0].quat;
+      bOut.translation = vec4(sharedPose[0].pos, 0.0);
+    }
+
+    // Memory barrier to make sure all threads can see prior writes.
+    memoryBarrierShared();
+
+    // Execution barrier to block all threads here until all threads have
+    // reached this point -- ensures the late latching is done.
+    barrier();
+
+    if (IS_APP_LATE_LATCH)
+      appLateLatch();
+    else
+      edsLateLatch();
+  }
+)";
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+LateLatch::LateLatch(bool is_app_late_latch)
+    : LateLatch(is_app_late_latch, LocalHandle()) {}
+
+LateLatch::LateLatch(bool is_app_late_latch,
+                     LocalHandle&& surface_metadata_fd)
+    : is_app_late_latch_(is_app_late_latch),
+      app_late_latch_output_(NULL),
+      eds_late_latch_output_(NULL),
+      surface_metadata_fd_(std::move(surface_metadata_fd)) {
+  CHECK_GL();
+  glGenBuffers(1, &input_buffer_id_);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer_id_);
+  glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(LateLatchInput), nullptr,
+               GL_DYNAMIC_DRAW);
+  glGenBuffers(1, &output_buffer_id_);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, output_buffer_id_);
+  glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(LateLatchOutput), nullptr,
+               GL_DYNAMIC_COPY);
+  CHECK_GL();
+
+  pose_client_ = dvrPoseCreate();
+  if (!pose_client_) {
+    ALOGE("LateLatch Error: failed to create pose client");
+  } else {
+    int ret = privateDvrPoseGetRingBufferFd(pose_client_, &pose_buffer_fd_);
+    if (ret < 0) {
+      ALOGE("LateLatch Error: failed to get pose ring buffer");
+    }
+  }
+
+  glGenBuffers(1, &pose_buffer_object_);
+  glGenBuffers(1, &metadata_buffer_id_);
+  if (!glBindSharedBufferQCOM) {
+    ALOGE("Error: Missing gralloc buffer extension, no pose data");
+  } else {
+    if (pose_buffer_fd_) {
+      glBindBuffer(GL_SHADER_STORAGE_BUFFER, pose_buffer_object_);
+      glBindSharedBufferQCOM(GL_SHADER_STORAGE_BUFFER,
+                             kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync),
+                             pose_buffer_fd_.Get());
+    }
+    CHECK_GL();
+  }
+
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, metadata_buffer_id_);
+  if (surface_metadata_fd_ && glBindSharedBufferQCOM) {
+    glBindSharedBufferQCOM(GL_SHADER_STORAGE_BUFFER,
+                           sizeof(DisplaySurfaceMetadata),
+                           surface_metadata_fd_.Get());
+  } else {
+    // Fall back on internal metadata buffer when none provided, for example
+    // when distortion is done in the application process.
+    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(DisplaySurfaceMetadata),
+                 nullptr, GL_DYNAMIC_COPY);
+  }
+  CHECK_GL();
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+
+  CHECK_GL();
+  LoadLateLatchShader();
+}
+
+LateLatch::~LateLatch() {
+  glDeleteBuffers(1, &metadata_buffer_id_);
+  glDeleteBuffers(1, &input_buffer_id_);
+  glDeleteBuffers(1, &output_buffer_id_);
+  glDeleteBuffers(1, &pose_buffer_object_);
+  dvrPoseDestroy(pose_client_);
+}
+
+void LateLatch::LoadLateLatchShader() {
+  std::string str;
+  str += "\n#define POSE_BINDING " STRINGIFY(POSE_BINDING);
+  str += "\n#define RENDER_POSE_BINDING " STRINGIFY(RENDER_POSE_BINDING);
+  str += "\n#define INPUT_BINDING " STRINGIFY(INPUT_BINDING);
+  str += "\n#define OUTPUT_BINDING " STRINGIFY(OUTPUT_BINDING);
+  str += "\n#define kPoseAsyncBufferTotalCount " STRINGIFY(
+      kPoseAsyncBufferTotalCount);
+  str += "\n#define kSurfaceBufferMaxCount " STRINGIFY(kSurfaceBufferMaxCount);
+  str += "\n#define kSurfaceBufferMaxCount " STRINGIFY(kSurfaceBufferMaxCount);
+  str += "\n#define kSurfaceViewMaxCount " STRINGIFY(kSurfaceViewMaxCount);
+  str += "\n#define IS_APP_LATE_LATCH ";
+  str += is_app_late_latch_ ? "true" : "false";
+  str += "\n";
+  str += kShaderLateLatch;
+  late_latch_program_.Link(str);
+  CHECK_GL();
+}
+
+void LateLatch::CaptureOutputData(LateLatchOutput* data) const {
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, output_buffer_id_);
+  LateLatchOutput* out_data = static_cast<LateLatchOutput*>(glMapBufferRange(
+      GL_SHADER_STORAGE_BUFFER, 0, sizeof(LateLatchOutput), GL_MAP_READ_BIT));
+  *data = *out_data;
+  glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+  CHECK_GL();
+}
+
+void LateLatch::AddLateLatch(const LateLatchInput& data) const {
+  LOG_ALWAYS_FATAL_IF(!is_app_late_latch_);
+  CHECK_GL();
+  late_latch_program_.Use();
+
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING,
+                   metadata_buffer_id_);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, pose_buffer_object_);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, output_buffer_id_);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer_id_);
+  LateLatchInput* adata = (LateLatchInput*)glMapBufferRange(
+      GL_SHADER_STORAGE_BUFFER, 0, sizeof(LateLatchInput),
+      GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+  if (adata)
+    *adata = data;
+  else
+    ALOGE("Error: LateLatchInput gl mapping is null");
+  glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, input_buffer_id_);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+  CHECK_GL();
+
+  // The output buffer is going to be written but it may be read by
+  // earlier shaders, so we need a shader storage memory barrier.
+  glMemoryBarrier(GL_SHADER_STORAGE_BUFFER);
+
+  glDispatchCompute(1, 1, 1);
+  CHECK_GL();
+
+  // The transform feedback buffer is going to be read as a uniform by the app,
+  // so we need a uniform memory barrier.
+  glMemoryBarrier(GL_UNIFORM_BARRIER_BIT);
+
+  if (app_late_latch_output_) {
+    // Capture the output data:
+    CaptureOutputData(app_late_latch_output_);
+  }
+#if PRINT_MATRIX
+  // Print the composed matrix to stderr:
+  LateLatchOutput out_data;
+  CaptureOutputData(&out_data);
+  CHECK_GL();
+  PE("LL APP slot:%d\n", data.render_pose_index);
+  PM4(data.proj_mat[0]);
+  PM4(out_data.view_proj_matrix[0]);
+  PM4(out_data.view_proj_matrix[1]);
+  PM4(out_data.view_proj_matrix[2]);
+  PM4(out_data.view_proj_matrix[3]);
+  PM4(out_data.view_matrix[0]);
+  PM4(out_data.view_matrix[1]);
+  PM4(out_data.view_matrix[2]);
+  PM4(out_data.view_matrix[3]);
+  PV4(out_data.pose_quaternion);
+  PV4(out_data.pose_translation);
+#endif
+
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, 0);
+  glUseProgram(0);
+}
+
+void LateLatch::AddEdsLateLatch(const LateLatchInput& data,
+                                GLuint render_pose_buffer_object) const {
+  LOG_ALWAYS_FATAL_IF(is_app_late_latch_);
+  late_latch_program_.Use();
+
+  // Fall back on internal buffer when none is provided.
+  if (!render_pose_buffer_object)
+    render_pose_buffer_object = metadata_buffer_id_;
+
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING,
+                   render_pose_buffer_object);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, pose_buffer_object_);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, output_buffer_id_);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer_id_);
+  LateLatchInput* adata = (LateLatchInput*)glMapBufferRange(
+      GL_SHADER_STORAGE_BUFFER, 0, sizeof(LateLatchInput),
+      GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+  *adata = data;
+  glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, input_buffer_id_);
+  CHECK_GL();
+
+  glDispatchCompute(1, 1, 1);
+  CHECK_GL();
+
+  if (eds_late_latch_output_) {
+    // Capture the output data:
+    CaptureOutputData(eds_late_latch_output_);
+  }
+#if PRINT_MATRIX
+  // Print the composed matrix to stderr:
+  LateLatchOutput out_data;
+  CaptureOutputData(&out_data);
+  CHECK_GL();
+  PE("LL EDS\n");
+  PM4(out_data.view_proj_matrix[0]);
+  PM4(out_data.view_matrix[0]);
+  PV4(out_data.pose_quaternion);
+  PV4(out_data.pose_translation);
+#endif
+
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, 0);
+  glUseProgram(0);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/native_buffer_queue.cpp b/libs/vr/libdisplay/native_buffer_queue.cpp
new file mode 100644
index 0000000..d516d63
--- /dev/null
+++ b/libs/vr/libdisplay/native_buffer_queue.cpp
@@ -0,0 +1,64 @@
+#include "include/private/dvr/native_buffer_queue.h"
+
+#include <log/log.h>
+#include <sys/epoll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <array>
+
+#include <private/dvr/display_types.h>
+
+namespace android {
+namespace dvr {
+
+NativeBufferQueue::NativeBufferQueue(
+    EGLDisplay display, const std::shared_ptr<DisplaySurfaceClient>& surface,
+    size_t capacity)
+    : display_(display), buffers_(capacity) {
+  std::shared_ptr<ProducerQueue> queue = surface->GetProducerQueue();
+
+  for (size_t i = 0; i < capacity; i++) {
+    size_t slot;
+    // TODO(jwcai) Should change to use BufferViewPort's spec to config.
+    int ret =
+        queue->AllocateBuffer(surface->width(), surface->height(),
+                              surface->format(), surface->usage(), 1, &slot);
+    if (ret < 0) {
+      ALOGE(
+          "NativeBufferQueue::NativeBufferQueue: Failed to allocate buffer, "
+          "error=%d",
+          ret);
+      return;
+    }
+
+    ALOGD_IF(TRACE,
+             "NativeBufferQueue::NativeBufferQueue: New buffer allocated at "
+             "slot=%zu",
+             slot);
+  }
+
+  producer_queue_ = std::move(queue);
+}
+
+NativeBufferProducer* NativeBufferQueue::Dequeue() {
+  ATRACE_NAME("NativeBufferQueue::Dequeue");
+
+  // This never times out.
+  size_t slot;
+  pdx::LocalHandle fence;
+  std::shared_ptr<BufferProducer> buffer =
+      producer_queue_->Dequeue(-1, &slot, &fence);
+
+  if (buffers_[slot] == nullptr) {
+    buffers_[slot] = new NativeBufferProducer(buffer, display_, slot);
+  }
+
+  ALOGD_IF(TRACE,
+           "NativeBufferQueue::Dequeue: dequeue buffer at slot=%zu, buffer=%p",
+           slot, buffers_[slot].get());
+  return buffers_[slot].get();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/screenshot_client.cpp b/libs/vr/libdisplay/screenshot_client.cpp
new file mode 100644
index 0000000..3ad0c68
--- /dev/null
+++ b/libs/vr/libdisplay/screenshot_client.cpp
@@ -0,0 +1,66 @@
+#include "include/private/dvr/screenshot_client.h"
+
+#include <log/log.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/display_rpc.h>
+
+using android::pdx::Transaction;
+using android::pdx::rpc::ClientPayload;
+using android::pdx::rpc::MessageBuffer;
+using android::pdx::rpc::ReplyBuffer;
+
+namespace android {
+namespace dvr {
+
+namespace {
+// Maximum supported pixels for screenshot capture. If the actual target buffer
+// is more than this, an error will be reported.
+constexpr int kMaxScreenshotPixels = 6000 * 4000;
+}  // namespace
+
+int ScreenshotClient::Take(std::vector<uint8_t>* out_image, int index,
+                           int* return_width, int* return_height) {
+  if (format_ != HAL_PIXEL_FORMAT_RGB_888) {
+    ALOGE("ScreenshotClient::Take: Unsupported layout format: format=%d",
+          format_);
+    return -ENOSYS;
+  }
+
+  // TODO(eieio): Make a cleaner way to ensure enough capacity for send or
+  // receive buffers. This method assumes TLS buffers that will maintain
+  // capacity across calls within the same thread.
+  MessageBuffer<ReplyBuffer>::Reserve(kMaxScreenshotPixels * 3);
+  auto status = InvokeRemoteMethod<DisplayScreenshotRPC::TakeScreenshot>(index);
+  if (!status) {
+    ALOGE("ScreenshotClient::Take: Failed to take screenshot: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *return_width = status.get().width;
+  *return_height = status.get().height;
+  *out_image = std::move(status.take().buffer);
+  return 0;
+}
+
+ScreenshotClient::ScreenshotClient()
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DisplayScreenshotRPC::kClientPath)) {
+  auto status = InvokeRemoteMethod<DisplayScreenshotRPC::GetFormat>();
+  if (!status) {
+    ALOGE(
+        "ScreenshotClient::ScreenshotClient: Failed to retrieve screenshot "
+        "layout: %s",
+        status.GetErrorMessage().c_str());
+
+    Close(status.error());
+  } else {
+    format_ = status.get();
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
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/tests/dummy_native_window_tests.cpp b/libs/vr/libdisplay/tests/dummy_native_window_tests.cpp
new file mode 100644
index 0000000..5f3ff53
--- /dev/null
+++ b/libs/vr/libdisplay/tests/dummy_native_window_tests.cpp
@@ -0,0 +1,64 @@
+#include <private/dvr/dummy_native_window.h>
+#include <gtest/gtest.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+
+class DummyNativeWindowTests : public ::testing::Test {
+ public:
+  EGLDisplay display_;
+  bool initialized_;
+
+  DummyNativeWindowTests()
+      : display_(nullptr)
+      , initialized_(false)
+  {
+  }
+
+  virtual void SetUp() {
+    display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    ASSERT_NE(nullptr, display_);
+    initialized_ = eglInitialize(display_, nullptr, nullptr);
+
+    ASSERT_TRUE(initialized_);
+  }
+
+  virtual void TearDown() {
+    if (display_ && initialized_) {
+      eglTerminate(display_);
+    }
+  }
+};
+
+// Test that eglCreateWindowSurface works with DummyNativeWindow
+TEST_F(DummyNativeWindowTests, TryCreateEglWindow) {
+  EGLint attribs[] = {
+      EGL_NONE,
+  };
+
+  EGLint num_configs;
+  EGLConfig config;
+  ASSERT_TRUE(eglChooseConfig(display_, attribs, &config, 1, &num_configs));
+
+  std::unique_ptr<android::dvr::DummyNativeWindow> dummy_window(
+      new android::dvr::DummyNativeWindow());
+
+  EGLint context_attribs[] = {
+    EGL_NONE,
+  };
+
+  EGLSurface surface = eglCreateWindowSurface(display_, config,
+                                              dummy_window.get(),
+                                              context_attribs);
+
+  EXPECT_NE(nullptr, surface);
+
+  bool destroyed = eglDestroySurface(display_, surface);
+
+  EXPECT_TRUE(destroyed);
+}
+
diff --git a/libs/vr/libdisplay/tests/graphics_app_tests.cpp b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
new file mode 100644
index 0000000..f51dd8a
--- /dev/null
+++ b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
@@ -0,0 +1,117 @@
+#include <dvr/graphics.h>
+#include <gtest/gtest.h>
+
+TEST(GraphicsAppTests, GetNativeDisplayDimensions) {
+  int width, height;
+  dvrGetNativeDisplayDimensions(&width, &height);
+  EXPECT_GT(width, 0);
+  EXPECT_GT(height, 0);
+}
+
+// TODO(jpoichet) How to check it worked?
+TEST(GraphicsAppTests, GraphicsSurfaceSetVisible) {
+  DvrSurfaceParameter surface_params[] = {DVR_SURFACE_PARAMETER_LIST_END};
+  DvrGraphicsContext* context = nullptr;
+  int result = dvrGraphicsContextCreate(surface_params, &context);
+  ASSERT_GE(result, 0);
+  ASSERT_NE(context, nullptr);
+  dvrGraphicsSurfaceSetVisible(context, 0);
+  dvrGraphicsSurfaceSetVisible(context, 1);
+  dvrGraphicsSurfaceSetVisible(context, 2);
+}
+
+// TODO(jpoichet) How to check it worked?
+TEST(GraphicsAppTests, GraphicsSurfaceSetZOrder) {
+  DvrSurfaceParameter surface_params[] = {DVR_SURFACE_PARAMETER_LIST_END};
+  DvrGraphicsContext* context = nullptr;
+  int result = dvrGraphicsContextCreate(surface_params, &context);
+  ASSERT_GE(result, 0);
+  ASSERT_NE(context, nullptr);
+  dvrGraphicsSurfaceSetZOrder(context, -1);
+  dvrGraphicsSurfaceSetZOrder(context, 0);
+  dvrGraphicsSurfaceSetZOrder(context, 1);
+  dvrGraphicsSurfaceSetZOrder(context, 2);
+}
+
+TEST(GraphicsAppTests, GraphicsContext) {
+  DvrGraphicsContext* context = 0;
+  int display_width = 0, display_height = 0;
+  int surface_width = 0, surface_height = 0;
+  float inter_lens_meters = 0.0f;
+  float left_fov[4] = {0.0f};
+  float right_fov[4] = {0.0f};
+  uint64_t vsync_period = 0;
+  int disable_warp = 0;
+  DvrSurfaceParameter surface_params[] = {
+      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+      DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+      DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+      DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+      DVR_SURFACE_PARAMETER_OUT(VSYNC_PERIOD, &vsync_period),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+  dvrGraphicsContextCreate(surface_params, &context);
+  EXPECT_NE(nullptr, context);
+
+  DvrFrameSchedule schedule;
+  int wait_result = dvrGraphicsWaitNextFrame(context, 0, &schedule);
+  EXPECT_EQ(wait_result, 0);
+  EXPECT_GE(schedule.vsync_count, 0u);
+
+  dvrBeginRenderFrame(context);
+
+  // Check range of vsync period from 70fps to 100fps.
+  // TODO(jbates) Once we have stable hardware, clamp this range down further.
+  EXPECT_LT(vsync_period, 1000000000ul / 70ul);
+  EXPECT_GT(vsync_period, 1000000000ul / 100ul);
+
+  dvrPresent(context);
+  dvrGraphicsContextDestroy(context);
+}
+
+TEST(GraphicsAppTests, CustomSurfaceSize) {
+  DvrGraphicsContext* context = 0;
+  int display_width = 0, display_height = 0;
+  int surface_width = 0, surface_height = 0;
+  float inter_lens_meters = 0.0f;
+  float left_fov[4] = {0.0f};
+  float right_fov[4] = {0.0f};
+  int disable_warp = 0;
+  int req_width = 256, req_height = 128;
+  DvrSurfaceParameter surface_params[] = {
+      DVR_SURFACE_PARAMETER_IN(WIDTH, req_width),
+      DVR_SURFACE_PARAMETER_IN(HEIGHT, req_height),
+      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+      DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+      DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+      DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+  dvrGraphicsContextCreate(surface_params, &context);
+  EXPECT_NE(nullptr, context);
+
+  EXPECT_EQ(req_width, surface_width);
+  EXPECT_EQ(req_height, surface_height);
+  dvrGraphicsContextDestroy(context);
+}
+
+TEST(GraphicsAppTests, CreateVideoMeshSurface) {
+  DvrSurfaceParameter surface_params[] = {DVR_SURFACE_PARAMETER_LIST_END};
+  DvrGraphicsContext* context = nullptr;
+  int result = dvrGraphicsContextCreate(surface_params, &context);
+  EXPECT_NE(nullptr, context);
+  EXPECT_EQ(result, 0);
+
+  DvrVideoMeshSurface* surface = dvrGraphicsVideoMeshSurfaceCreate(context);
+  EXPECT_NE(nullptr, surface);
+
+  dvrGraphicsVideoMeshSurfaceDestroy(surface);
+}
diff --git a/libs/vr/libdisplay/video_mesh_surface_client.cpp b/libs/vr/libdisplay/video_mesh_surface_client.cpp
new file mode 100644
index 0000000..a2307e5
--- /dev/null
+++ b/libs/vr/libdisplay/video_mesh_surface_client.cpp
@@ -0,0 +1,60 @@
+#include "include/private/dvr/video_mesh_surface_client.h"
+
+using android::pdx::LocalChannelHandle;
+
+namespace android {
+namespace dvr {
+
+/* static */
+std::unique_ptr<VideoMeshSurfaceClient> VideoMeshSurfaceClient::Import(
+    LocalChannelHandle handle) {
+  return VideoMeshSurfaceClient::Create(std::move(handle));
+}
+
+VideoMeshSurfaceClient::VideoMeshSurfaceClient(LocalChannelHandle handle)
+    : BASE(std::move(handle), SurfaceTypeEnum::VideoMesh),
+      mapped_metadata_buffer_(nullptr) {
+  // TODO(jwcai) import more data if needed.
+}
+
+std::shared_ptr<ProducerQueue> VideoMeshSurfaceClient::GetProducerQueue() {
+  if (producer_queue_ == nullptr) {
+    // Create producer queue through DisplayRPC
+    auto status =
+        InvokeRemoteMethod<DisplayRPC::VideoMeshSurfaceCreateProducerQueue>();
+    if (!status) {
+      ALOGE(
+          "VideoMeshSurfaceClient::GetProducerQueue: failed to create producer "
+          "queue: %s",
+          status.GetErrorMessage().c_str());
+      return nullptr;
+    }
+
+    producer_queue_ = ProducerQueue::Import(status.take());
+  }
+  return producer_queue_;
+}
+
+volatile VideoMeshSurfaceMetadata*
+VideoMeshSurfaceClient::GetMetadataBufferPtr() {
+  if (!mapped_metadata_buffer_) {
+    if (auto buffer_producer = GetMetadataBuffer()) {
+      void* addr = nullptr;
+      const int ret = buffer_producer->GetBlobReadWritePointer(
+          sizeof(VideoMeshSurfaceMetadata), &addr);
+      if (ret < 0) {
+        ALOGE(
+            "VideoMeshSurfaceClient::GetMetadataBufferPtr: Failed to map "
+            "surface metadata: %s",
+            strerror(-ret));
+        return nullptr;
+      }
+      mapped_metadata_buffer_ = static_cast<VideoMeshSurfaceMetadata*>(addr);
+    }
+  }
+
+  return mapped_metadata_buffer_;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp
new file mode 100644
index 0000000..c928a08
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_client.cpp
@@ -0,0 +1,75 @@
+#include "include/private/dvr/vsync_client.h"
+
+#include <log/log.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/display_rpc.h>
+
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+VSyncClient::VSyncClient(long timeout_ms)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+               DisplayVSyncRPC::kClientPath),
+           timeout_ms) {}
+
+VSyncClient::VSyncClient()
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DisplayVSyncRPC::kClientPath)) {}
+
+int VSyncClient::Wait(int64_t* timestamp_ns) {
+  auto status = InvokeRemoteMethod<DisplayVSyncRPC::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<DisplayVSyncRPC::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<DisplayVSyncRPC::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<DisplayVSyncRPC::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.mk b/libs/vr/libdvr/Android.mk
new file mode 100644
index 0000000..1050283
--- /dev/null
+++ b/libs/vr/libdvr/Android.mk
@@ -0,0 +1,57 @@
+# 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.
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdvr
+LOCAL_MODULE_OWNER := google
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+LOCAL_CFLAGS += \
+    -fvisibility=hidden \
+    -D DVR_EXPORT='__attribute__ ((visibility ("default")))'
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+    $(LOCAL_PATH)/include \
+
+LOCAL_SRC_FILES := \
+    display_manager_client.cpp \
+    dvr_api.cpp \
+    dvr_buffer.cpp \
+    dvr_buffer_queue.cpp \
+    dvr_hardware_composer_client.cpp \
+    dvr_surface.cpp \
+    vsync_client_api.cpp \
+
+LOCAL_STATIC_LIBRARIES := \
+    libbufferhub \
+    libbufferhubqueue \
+    libdisplay \
+    libvrsensor \
+    libvirtualtouchpadclient \
+    libvr_hwc-impl \
+    libvr_hwc-binder \
+
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.graphics.bufferqueue@1.0 \
+    android.hidl.token@1.0-utils \
+    libbase \
+    libnativewindow \
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/vr/libdvr/display_manager_client.cpp b/libs/vr/libdvr/display_manager_client.cpp
new file mode 100644
index 0000000..64c7f16
--- /dev/null
+++ b/libs/vr/libdvr/display_manager_client.cpp
@@ -0,0 +1,138 @@
+#include "include/dvr/display_manager_client.h"
+
+#include <dvr/dvr_buffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/display_manager_client_impl.h>
+
+using android::dvr::DisplaySurfaceAttributeEnum;
+
+extern "C" {
+
+struct DvrDisplayManagerClient {
+  DvrDisplayManagerClient()
+      : client(android::dvr::DisplayManagerClient::Create()) {}
+  ~DvrDisplayManagerClient() {}
+
+  std::unique_ptr<android::dvr::DisplayManagerClient> client;
+};
+
+struct DvrDisplayManagerClientSurfaceList {
+  DvrDisplayManagerClientSurfaceList(
+      std::vector<android::dvr::DisplaySurfaceInfo> surface_list)
+      : list(std::move(surface_list)) {}
+  ~DvrDisplayManagerClientSurfaceList() {}
+
+  std::vector<android::dvr::DisplaySurfaceInfo> list;
+};
+
+struct DvrDisplayManagerClientSurfaceBuffers {
+  DvrDisplayManagerClientSurfaceBuffers(
+      std::vector<std::unique_ptr<android::dvr::BufferConsumer>> buffer_list)
+      : list(std::move(buffer_list)) {}
+  ~DvrDisplayManagerClientSurfaceBuffers() {}
+
+  std::vector<std::unique_ptr<android::dvr::BufferConsumer>> list;
+};
+
+DvrDisplayManagerClient* dvrDisplayManagerClientCreate() {
+  return new DvrDisplayManagerClient();
+}
+
+void dvrDisplayManagerClientDestroy(DvrDisplayManagerClient* client) {
+  delete client;
+}
+
+DvrBuffer* dvrDisplayManagerSetupNamedBuffer(DvrDisplayManagerClient* client,
+                                             const char* name, size_t size,
+                                             uint64_t usage0, uint64_t usage1) {
+  uint64_t producer_usage = 0;
+  uint64_t consumer_usage = 0;
+  android::AHardwareBuffer_convertToGrallocUsageBits(
+      &producer_usage, &consumer_usage, usage0, usage1);
+  auto ion_buffer = client->client->SetupNamedBuffer(name, size, producer_usage,
+                                                     consumer_usage);
+  if (ion_buffer) {
+    return CreateDvrBufferFromIonBuffer(std::move(ion_buffer));
+  }
+  return nullptr;
+}
+
+int dvrDisplayManagerClientGetEventFd(DvrDisplayManagerClient* client) {
+  return client->client->event_fd();
+}
+
+int dvrDisplayManagerClientTranslateEpollEventMask(
+    DvrDisplayManagerClient* client, int in_events, int* out_events) {
+  auto result = client->client->GetChannel()->GetEventMask(in_events);
+
+  if (!result) {
+    return -EIO;
+  }
+
+  *out_events = result.get();
+
+  return 0;
+}
+
+int dvrDisplayManagerClientGetSurfaceList(
+    DvrDisplayManagerClient* client,
+    DvrDisplayManagerClientSurfaceList** surface_list) {
+  std::vector<android::dvr::DisplaySurfaceInfo> list;
+  int ret = client->client->GetSurfaceList(&list);
+  if (ret < 0)
+    return ret;
+
+  *surface_list = new DvrDisplayManagerClientSurfaceList(std::move(list));
+  return ret;
+}
+
+void dvrDisplayManagerClientSurfaceListDestroy(
+    DvrDisplayManagerClientSurfaceList* surface_list) {
+  delete surface_list;
+}
+
+size_t dvrDisplayManagerClientSurfaceListGetSize(
+    DvrDisplayManagerClientSurfaceList* surface_list) {
+  return surface_list->list.size();
+}
+
+int dvrDisplayManagerClientSurfaceListGetSurfaceId(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+  return surface_list->list[index].surface_id;
+}
+
+int dvrDisplayManagerClientSurfaceListGetClientZOrder(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+  return surface_list->list[index].ClientZOrder();
+}
+
+bool dvrDisplayManagerClientSurfaceListGetClientIsVisible(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+  return surface_list->list[index].IsClientVisible();
+}
+
+int dvrDisplayManagerClientGetSurfaceBuffers(
+    DvrDisplayManagerClient* /* client */, int /* surface_id */,
+    DvrDisplayManagerClientSurfaceBuffers** /* surface_buffers */) {
+  // TODO(jwcai, hendrikw) Remove this after we replacing
+  // dvrDisplayManagerClientGetSurfaceBuffers is dvr_api.
+  return -1;
+}
+
+void dvrDisplayManagerClientSurfaceBuffersDestroy(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers) {
+  delete surface_buffers;
+}
+
+size_t dvrDisplayManagerClientSurfaceBuffersGetSize(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers) {
+  return surface_buffers->list.size();
+}
+
+int dvrDisplayManagerClientSurfaceBuffersGetFd(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers, size_t index) {
+  return surface_buffers->list[index]->event_fd();
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
new file mode 100644
index 0000000..c4634b1
--- /dev/null
+++ b/libs/vr/libdvr/dvr_api.cpp
@@ -0,0 +1,152 @@
+#include "include/dvr/dvr_api.h"
+
+#include <errno.h>
+
+// Headers from libdvr
+#include <dvr/display_manager_client.h>
+#include <dvr/dvr_buffer.h>
+#include <dvr/dvr_buffer_queue.h>
+#include <dvr/dvr_surface.h>
+#include <dvr/vsync_client_api.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" {
+
+DVR_EXPORT int dvrGetApi(void* api, size_t struct_size, int version) {
+  if (version == 1) {
+    if (struct_size != sizeof(DvrApi_v1)) {
+      return -EINVAL;
+    }
+    DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api);
+
+    // display_manager_client.h
+    dvr_api->display_manager_client_create = dvrDisplayManagerClientCreate;
+    dvr_api->display_manager_client_destroy = dvrDisplayManagerClientDestroy;
+    dvr_api->display_manager_client_get_surface_list =
+        dvrDisplayManagerClientGetSurfaceList;
+    dvr_api->display_manager_client_surface_list_destroy =
+        dvrDisplayManagerClientSurfaceListDestroy;
+    dvr_api->display_manager_setup_named_buffer =
+        dvrDisplayManagerSetupNamedBuffer;
+    dvr_api->display_manager_client_surface_list_get_size =
+        dvrDisplayManagerClientSurfaceListGetSize;
+    dvr_api->display_manager_client_surface_list_get_surface_id =
+        dvrDisplayManagerClientSurfaceListGetSurfaceId;
+    dvr_api->display_manager_client_get_surface_buffer_list =
+        dvrDisplayManagerClientGetSurfaceBuffers;
+    dvr_api->display_manager_client_surface_buffer_list_destroy =
+        dvrDisplayManagerClientSurfaceBuffersDestroy;
+    dvr_api->display_manager_client_surface_buffer_list_get_size =
+        dvrDisplayManagerClientSurfaceBuffersGetSize;
+    dvr_api->display_manager_client_surface_buffer_list_get_fd =
+        dvrDisplayManagerClientSurfaceBuffersGetFd;
+
+    // dvr_buffer.h
+    dvr_api->write_buffer_destroy = dvrWriteBufferDestroy;
+    dvr_api->write_buffer_get_ahardwarebuffer =
+        dvrWriteBufferGetAHardwareBuffer;
+    dvr_api->write_buffer_post = dvrWriteBufferPost;
+    dvr_api->write_buffer_gain = dvrWriteBufferGain;
+    dvr_api->write_buffer_gain_async = dvrWriteBufferGainAsync;
+    dvr_api->write_buffer_get_native_handle = dvrWriteBufferGetNativeHandle;
+
+    dvr_api->read_buffer_destroy = dvrReadBufferDestroy;
+    dvr_api->read_buffer_get_ahardwarebuffer = dvrReadBufferGetAHardwareBuffer;
+    dvr_api->read_buffer_acquire = dvrReadBufferAcquire;
+    dvr_api->read_buffer_release = dvrReadBufferRelease;
+    dvr_api->read_buffer_release_async = dvrReadBufferReleaseAsync;
+    dvr_api->read_buffer_get_native_handle = dvrReadBufferGetNativeHandle;
+
+    dvr_api->buffer_destroy = dvrBufferDestroy;
+    dvr_api->buffer_get_ahardwarebuffer = dvrBufferGetAHardwareBuffer;
+    dvr_api->buffer_get_native_handle = dvrBufferGetNativeHandle;
+
+    // dvr_buffer_queue.h
+    dvr_api->write_buffer_queue_destroy = dvrWriteBufferQueueDestroy;
+    dvr_api->write_buffer_queue_get_capacity = dvrWriteBufferQueueGetCapacity;
+    dvr_api->write_buffer_queue_get_external_surface =
+        dvrWriteBufferQueueGetExternalSurface;
+    dvr_api->write_buffer_queue_create_read_queue =
+        dvrWriteBufferQueueCreateReadQueue;
+    dvr_api->write_buffer_queue_dequeue = dvrWriteBufferQueueDequeue;
+    dvr_api->read_buffer_queue_destroy = dvrReadBufferQueueDestroy;
+    dvr_api->read_buffer_queue_get_capacity = dvrReadBufferQueueGetCapacity;
+    dvr_api->read_buffer_queue_create_read_queue =
+        dvrReadBufferQueueCreateReadQueue;
+    dvr_api->read_buffer_queue_dequeue = dvrReadBufferQueueDequeue;
+
+    // dvr_surface.h
+    dvr_api->get_named_buffer = dvrGetNamedBuffer;
+    dvr_api->surface_create = dvrSurfaceCreate;
+    dvr_api->surface_get_write_buffer_queue = dvrSurfaceGetWriteBufferQueue;
+
+    // vsync_client_api.h
+    dvr_api->vsync_client_create = dvr_vsync_client_create;
+    dvr_api->vsync_client_destroy = dvr_vsync_client_destroy;
+    dvr_api->vsync_client_get_sched_info = dvr_vsync_client_get_sched_info;
+
+    // pose_client.h
+    dvr_api->pose_client_create = dvrPoseCreate;
+    dvr_api->pose_client_destroy = dvrPoseDestroy;
+    dvr_api->pose_get = dvrPoseGet;
+    dvr_api->pose_get_vsync_count = dvrPoseGetVsyncCount;
+    dvr_api->pose_get_controller = dvrPoseGetController;
+
+    // virtual_touchpad_client.h
+    dvr_api->virtual_touchpad_create = dvrVirtualTouchpadCreate;
+    dvr_api->virtual_touchpad_destroy = dvrVirtualTouchpadDestroy;
+    dvr_api->virtual_touchpad_attach = dvrVirtualTouchpadAttach;
+    dvr_api->virtual_touchpad_detach = dvrVirtualTouchpadDetach;
+    dvr_api->virtual_touchpad_touch = dvrVirtualTouchpadTouch;
+    dvr_api->virtual_touchpad_button_state = dvrVirtualTouchpadButtonState;
+
+    // dvr_hardware_composer_client.h
+    dvr_api->hwc_client_create = dvrHwcClientCreate;
+    dvr_api->hwc_client_destroy = dvrHwcClientDestroy;
+    dvr_api->hwc_frame_destroy = dvrHwcFrameDestroy;
+    dvr_api->hwc_frame_get_display_id = dvrHwcFrameGetDisplayId;
+    dvr_api->hwc_frame_get_display_width = dvrHwcFrameGetDisplayWidth;
+    dvr_api->hwc_frame_get_display_height = dvrHwcFrameGetDisplayHeight;
+    dvr_api->hwc_frame_get_display_removed = dvrHwcFrameGetDisplayRemoved;
+    dvr_api->hwc_frame_get_active_config = dvrHwcFrameGetActiveConfig;
+    dvr_api->hwc_frame_get_color_mode = dvrHwcFrameGetColorMode;
+    dvr_api->hwc_frame_get_color_transform = dvrHwcFrameGetColorTransform;
+    dvr_api->hwc_frame_get_power_mode = dvrHwcFrameGetPowerMode;
+    dvr_api->hwc_frame_get_vsync_enabled = dvrHwcFrameGetVsyncEnabled;
+    dvr_api->hwc_frame_get_layer_count = dvrHwcFrameGetLayerCount;
+    dvr_api->hwc_frame_get_layer_id = dvrHwcFrameGetLayerId;
+    dvr_api->hwc_frame_get_layer_buffer = dvrHwcFrameGetLayerBuffer;
+    dvr_api->hwc_frame_get_layer_fence = dvrHwcFrameGetLayerFence;
+    dvr_api->hwc_frame_get_layer_display_frame =
+        dvrHwcFrameGetLayerDisplayFrame;
+    dvr_api->hwc_frame_get_layer_crop = dvrHwcFrameGetLayerCrop;
+    dvr_api->hwc_frame_get_layer_blend_mode = dvrHwcFrameGetLayerBlendMode;
+    dvr_api->hwc_frame_get_layer_alpha = dvrHwcFrameGetLayerAlpha;
+    dvr_api->hwc_frame_get_layer_type = dvrHwcFrameGetLayerType;
+    dvr_api->hwc_frame_get_layer_application_id =
+        dvrHwcFrameGetLayerApplicationId;
+    dvr_api->hwc_frame_get_layer_z_order = dvrHwcFrameGetLayerZOrder;
+    dvr_api->hwc_frame_get_layer_cursor = dvrHwcFrameGetLayerCursor;
+    dvr_api->hwc_frame_get_layer_transform = dvrHwcFrameGetLayerTransform;
+    dvr_api->hwc_frame_get_layer_dataspace = dvrHwcFrameGetLayerDataspace;
+    dvr_api->hwc_frame_get_layer_color = dvrHwcFrameGetLayerColor;
+    dvr_api->hwc_frame_get_layer_num_visible_regions =
+        dvrHwcFrameGetLayerNumVisibleRegions;
+    dvr_api->hwc_frame_get_layer_visible_region =
+        dvrHwcFrameGetLayerVisibleRegion;
+    dvr_api->hwc_frame_get_layer_num_damaged_regions =
+        dvrHwcFrameGetLayerNumDamagedRegions;
+    dvr_api->hwc_frame_get_layer_damaged_region =
+        dvrHwcFrameGetLayerDamagedRegion;
+
+    return 0;
+  }
+  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..28820e7
--- /dev/null
+++ b/libs/vr/libdvr/dvr_buffer.cpp
@@ -0,0 +1,157 @@
+#include "include/dvr/dvr_buffer.h"
+
+#include <android/hardware_buffer.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <ui/GraphicBuffer.h>
+
+using namespace android;
+
+struct DvrWriteBuffer {
+  std::shared_ptr<dvr::BufferProducer> write_buffer;
+};
+
+struct DvrReadBuffer {
+  std::shared_ptr<dvr::BufferConsumer> read_buffer;
+};
+
+struct DvrBuffer {
+  std::shared_ptr<dvr::IonBuffer> buffer;
+};
+
+namespace android {
+namespace dvr {
+
+DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer(
+    const std::shared_ptr<dvr::BufferProducer>& buffer_producer) {
+  if (!buffer_producer)
+    return nullptr;
+  return new DvrWriteBuffer{std::move(buffer_producer)};
+}
+
+DvrReadBuffer* CreateDvrReadBufferFromBufferConsumer(
+    const std::shared_ptr<dvr::BufferConsumer>& buffer_consumer) {
+  if (!buffer_consumer)
+    return nullptr;
+  return new DvrReadBuffer{std::move(buffer_consumer)};
+}
+
+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 {
+
+void InitializeGraphicBuffer(const dvr::BufferHubBuffer* buffer,
+                             sp<GraphicBuffer>* graphic_buffer) {
+  *graphic_buffer = sp<GraphicBuffer>(new GraphicBuffer(
+      buffer->width(), buffer->height(), buffer->format(), 1, /* layer count */
+      buffer->usage(), buffer->stride(), buffer->native_handle(),
+      false /* keep ownership */));
+}
+
+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 dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) {
+  delete write_buffer;
+}
+
+int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) {
+  return write_buffer->write_buffer->id();
+}
+
+int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
+                                     AHardwareBuffer** hardware_buffer) {
+  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) {
+  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) {
+  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) {
+  return write_buffer->write_buffer->GainAsync();
+}
+
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) { delete read_buffer; }
+
+int dvrReadBufferGetId(DvrReadBuffer* read_buffer) {
+  return read_buffer->read_buffer->id();
+}
+
+int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
+                                    AHardwareBuffer** hardware_buffer) {
+  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) {
+  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) {
+  pdx::LocalHandle fence(release_fence_fd);
+  int result = read_buffer->read_buffer->Release(fence);
+  return result;
+}
+
+int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer) {
+  return read_buffer->read_buffer->ReleaseAsync();
+}
+
+void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; }
+
+int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
+                                AHardwareBuffer** hardware_buffer) {
+  return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(),
+                                  hardware_buffer);
+}
+
+const struct native_handle* dvrWriteBufferGetNativeHandle(
+    DvrWriteBuffer* write_buffer) {
+  return write_buffer->write_buffer->native_handle();
+}
+
+const struct native_handle* dvrReadBufferGetNativeHandle(
+    DvrReadBuffer* read_buffer) {
+  return read_buffer->read_buffer->native_handle();
+}
+
+const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer) {
+  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..dfde21d
--- /dev/null
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -0,0 +1,162 @@
+#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>
+
+#define CHECK_PARAM(param)                                               \
+  LOG_ALWAYS_FATAL_IF(param == nullptr, "%s: " #param "cannot be NULL.", \
+                      __FUNCTION__)
+
+using 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;
+}
+
+size_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) {
+  CHECK_PARAM(write_queue);
+  return write_queue->producer_queue_->capacity();
+}
+
+int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
+                                          ANativeWindow** out_window) {
+  CHECK_PARAM(write_queue);
+  CHECK_PARAM(out_window);
+
+  // Lazy creation of |native_window_|.
+  if (write_queue->native_window_ == nullptr) {
+    std::shared_ptr<dvr::BufferHubQueueCore> core =
+        dvr::BufferHubQueueCore::Create(write_queue->producer_queue_);
+    if (core == nullptr) {
+      ALOGE(
+          "dvrWriteBufferQueueGetExternalSurface: Failed to create native "
+          "window.");
+      return -ENOMEM;
+    }
+
+    sp<IGraphicBufferProducer> gbp = new dvr::BufferHubQueueProducer(core);
+    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) {
+  CHECK_PARAM(write_queue);
+  CHECK_PARAM(write_queue->producer_queue_);
+  CHECK_PARAM(out_read_queue);
+
+  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** out_buffer,
+                                  int* out_fence_fd) {
+  CHECK_PARAM(write_queue);
+  CHECK_PARAM(write_queue->producer_queue_);
+  CHECK_PARAM(out_buffer);
+  CHECK_PARAM(out_fence_fd);
+
+  size_t slot;
+  pdx::LocalHandle release_fence;
+  std::shared_ptr<dvr::BufferProducer> buffer =
+      write_queue->producer_queue_->Dequeue(timeout, &slot, &release_fence);
+  if (buffer == nullptr) {
+    ALOGE("dvrWriteBufferQueueDequeue: Failed to dequeue buffer.");
+    return -ENOMEM;
+  }
+
+  *out_buffer = CreateDvrWriteBufferFromBufferProducer(buffer);
+  *out_fence_fd = release_fence.Release();
+  return 0;
+}
+
+// ReadBufferQueue
+void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) {
+  delete read_queue;
+}
+
+size_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) {
+  CHECK_PARAM(read_queue);
+
+  return read_queue->consumer_queue_->capacity();
+}
+
+int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
+                                      DvrReadBufferQueue** out_read_queue) {
+  CHECK_PARAM(read_queue);
+  CHECK_PARAM(read_queue->consumer_queue_);
+  CHECK_PARAM(out_read_queue);
+
+  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** out_buffer, int* out_fence_fd,
+                              void* out_meta, size_t meta_size_bytes) {
+  CHECK_PARAM(read_queue);
+  CHECK_PARAM(read_queue->consumer_queue_);
+  CHECK_PARAM(out_buffer);
+  CHECK_PARAM(out_fence_fd);
+  CHECK_PARAM(out_meta);
+
+  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;
+  std::shared_ptr<dvr::BufferConsumer> buffer =
+      read_queue->consumer_queue_->Dequeue(timeout, &slot, out_meta,
+                                           meta_size_bytes, &acquire_fence);
+
+  if (buffer == nullptr) {
+    ALOGE("dvrReadBufferQueueGainBuffer: Failed to dequeue buffer.");
+    return -ENOMEM;
+  }
+
+  *out_buffer = CreateDvrReadBufferFromBufferConsumer(buffer);
+  *out_fence_fd = acquire_fence.Release();
+  return 0;
+}
+
+}  // 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_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
new file mode 100644
index 0000000..a04ed50
--- /dev/null
+++ b/libs/vr/libdvr/dvr_surface.cpp
@@ -0,0 +1,83 @@
+#include "include/dvr/dvr_surface.h"
+
+#include <private/dvr/display_client.h>
+
+using namespace android;
+
+struct DvrSurface {
+  std::unique_ptr<dvr::DisplaySurfaceClient> display_surface_;
+};
+
+extern "C" {
+
+int dvrSurfaceCreate(int width, int height, int format, uint64_t usage0,
+                     uint64_t usage1, int flags, DvrSurface** out_surface) {
+  if (out_surface == nullptr) {
+    ALOGE("dvrSurfaceCreate: invalid inputs: out_surface=%p.", out_surface);
+    return -EINVAL;
+  }
+
+  int error;
+  auto client = dvr::DisplayClient::Create(&error);
+  if (!client) {
+    ALOGE("Failed to create display client!");
+    return error;
+  }
+
+  // TODO(hendrikw): When we move to gralloc1, pass both usage0 and usage1 down.
+  std::unique_ptr<dvr::DisplaySurfaceClient> surface =
+      client->CreateDisplaySurface(
+          width, height, static_cast<int>(usage0 | usage1), format, flags);
+
+  DvrSurface* dvr_surface = new DvrSurface;
+  dvr_surface->display_surface_ = std::move(surface);
+  *out_surface = dvr_surface;
+  return 0;
+}
+
+int dvrSurfaceGetWriteBufferQueue(DvrSurface* surface,
+                                  DvrWriteBufferQueue** out_writer) {
+  if (surface == nullptr || out_writer == nullptr) {
+    ALOGE(
+        "dvrSurfaceGetWriteBufferQueue: Invalid inputs: surface=%p, "
+        "out_writer=%p.",
+        surface, out_writer);
+    return -EINVAL;
+  }
+  DvrWriteBufferQueue* buffer_writer = new DvrWriteBufferQueue;
+  buffer_writer->producer_queue_ =
+      surface->display_surface_->GetProducerQueue();
+  if (buffer_writer->producer_queue_ == nullptr) {
+    ALOGE(
+        "dvrSurfaceGetWriteBufferQueue: Failed to get producer queue from "
+        "display surface.");
+    return -ENOMEM;
+  }
+
+  *out_writer = buffer_writer;
+  return 0;
+}
+
+int dvrGetNamedBuffer(const char* name, DvrBuffer** out_buffer) {
+  auto client = android::dvr::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 named_buffer = client->GetNamedBuffer(name);
+  if (!named_buffer) {
+    ALOGE("dvrGetNamedBuffer: Failed to find named buffer: %s.", name);
+    return -EINVAL;
+  }
+  *out_buffer = CreateDvrBufferFromIonBuffer(std::move(named_buffer));
+  return 0;
+}
+
+}  // extern "C"
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/display_manager_client.h b/libs/vr/libdvr/include/dvr/display_manager_client.h
new file mode 100644
index 0000000..8cd948c
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/display_manager_client.h
@@ -0,0 +1,91 @@
+#ifndef DVR_DISPLAY_MANAGER_CLIENT_H_
+#define DVR_DISPLAY_MANAGER_CLIENT_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrDisplayManagerClient DvrDisplayManagerClient;
+typedef struct DvrDisplayManagerClientSurfaceList
+    DvrDisplayManagerClientSurfaceList;
+typedef struct DvrDisplayManagerClientSurfaceBuffers
+    DvrDisplayManagerClientSurfaceBuffers;
+typedef struct DvrBuffer DvrBuffer;
+
+DvrDisplayManagerClient* dvrDisplayManagerClientCreate();
+
+void dvrDisplayManagerClientDestroy(DvrDisplayManagerClient* client);
+
+DvrBuffer* dvrDisplayManagerSetupNamedBuffer(DvrDisplayManagerClient* client,
+                                             const char* name, size_t size,
+                                             uint64_t usage0, uint64_t usage1);
+
+// Return an event fd for checking if there was an event on the server
+// Note that the only event which will be flagged is POLLIN. You must use
+// dvrDisplayManagerClientTranslateEpollEventMask in order to get the real
+// event flags.
+// @return the fd
+int dvrDisplayManagerClientGetEventFd(DvrDisplayManagerClient* client);
+
+// Once you have received an epoll event, you must translate it to its true
+// flags. This is a workaround for working with UDS.
+// @param in_events pass in the epoll revents that were initially returned
+// @param on success, this value will be overwritten with the true epoll values
+// @return 0 on success, non-zero otherwise
+int dvrDisplayManagerClientTranslateEpollEventMask(
+    DvrDisplayManagerClient* client, int in_events, int* out_events);
+
+// If successful, populates |surface_list| with a list of application
+// surfaces the display is currently using.
+//
+// @return 0 on success. Otherwise it returns a negative error value.
+int dvrDisplayManagerClientGetSurfaceList(
+    DvrDisplayManagerClient* client,
+    DvrDisplayManagerClientSurfaceList** surface_list);
+
+void dvrDisplayManagerClientSurfaceListDestroy(
+    DvrDisplayManagerClientSurfaceList* surface_list);
+
+// @return Returns the number of surfaces in the list.
+size_t dvrDisplayManagerClientSurfaceListGetSize(
+    DvrDisplayManagerClientSurfaceList* surface_list);
+
+// @return Return a unique identifier for a client surface. The identifier can
+// be used to query for other surface properties.
+int dvrDisplayManagerClientSurfaceListGetSurfaceId(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// @return Returns the stacking order of the client surface at |index|.
+int dvrDisplayManagerClientSurfaceListGetClientZOrder(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// @return Returns true if the client surface is visible, false otherwise.
+bool dvrDisplayManagerClientSurfaceListGetClientIsVisible(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// TODO(jwcai, hendrikw) Remove this after we replacing
+// dvrDisplayManagerClientGetSurfaceBuffers is dvr_api.
+int dvrDisplayManagerClientGetSurfaceBuffers(
+    DvrDisplayManagerClient* client, int surface_id,
+    DvrDisplayManagerClientSurfaceBuffers** surface_buffers);
+
+void dvrDisplayManagerClientSurfaceBuffersDestroy(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers);
+
+// @return Returns the number of buffers.
+size_t dvrDisplayManagerClientSurfaceBuffersGetSize(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers);
+
+// @return Returns the file descriptor for the buffer consumer at |index|.
+int dvrDisplayManagerClientSurfaceBuffersGetFd(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers, size_t index);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // DVR_DISPLAY_MANAGER_CLIENT_H_
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..56f937b
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -0,0 +1,346 @@
+#ifndef ANDROID_DVR_API_H_
+#define ANDROID_DVR_API_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <dvr/dvr_hardware_composer_defs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ANativeWindow ANativeWindow;
+
+typedef struct DvrPoseAsync DvrPoseAsync;
+
+typedef struct DvrDisplayManagerClient DvrDisplayManagerClient;
+typedef struct DvrDisplayManagerClientSurfaceList
+    DvrDisplayManagerClientSurfaceList;
+typedef struct DvrDisplayManagerClientSurfaceBuffers
+    DvrDisplayManagerClientSurfaceBuffers;
+typedef struct DvrPose DvrPose;
+typedef struct dvr_vsync_client dvr_vsync_client;
+typedef struct DvrVirtualTouchpad DvrVirtualTouchpad;
+
+typedef DvrDisplayManagerClient* (*DvrDisplayManagerClientCreatePtr)(void);
+typedef void (*DvrDisplayManagerClientDestroyPtr)(
+    DvrDisplayManagerClient* client);
+
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrBuffer DvrBuffer;
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
+typedef struct DvrSurface DvrSurface;
+
+struct native_handle;
+
+// display_manager_client.h
+typedef int (*DvrDisplayManagerClientGetSurfaceListPtr)(
+    DvrDisplayManagerClient* client,
+    DvrDisplayManagerClientSurfaceList** surface_list);
+typedef void (*DvrDisplayManagerClientSurfaceListDestroyPtr)(
+    DvrDisplayManagerClientSurfaceList* surface_list);
+typedef DvrBuffer* (*DvrDisplayManagerSetupNamedBufferPtr)(
+    DvrDisplayManagerClient* client, const char* name, size_t size,
+    uint64_t usage0, uint64_t usage1);
+typedef size_t (*DvrDisplayManagerClientSurfaceListGetSizePtr)(
+    DvrDisplayManagerClientSurfaceList* surface_list);
+typedef int (*DvrDisplayManagerClientSurfaceListGetSurfaceIdPtr)(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+typedef int (*DvrDisplayManagerClientGetSurfaceBufferListPtr)(
+    DvrDisplayManagerClient* client, int surface_id,
+    DvrDisplayManagerClientSurfaceBuffers** surface_buffers);
+typedef void (*DvrDisplayManagerClientSurfaceBufferListDestroyPtr)(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers);
+typedef size_t (*DvrDisplayManagerClientSurfaceBufferListGetSizePtr)(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers);
+typedef int (*DvrDisplayManagerClientSurfaceBufferListGetFdPtr)(
+    DvrDisplayManagerClientSurfaceBuffers* surface_buffers, size_t index);
+
+// dvr_buffer.h
+typedef void (*DvrWriteBufferDestroyPtr)(DvrWriteBuffer* client);
+typedef int (*DvrWriteBufferGetAHardwareBufferPtr)(
+    DvrWriteBuffer* client, AHardwareBuffer** hardware_buffer);
+typedef int (*DvrWriteBufferPostPtr)(DvrWriteBuffer* client, int ready_fence_fd,
+                                     const void* meta, size_t meta_size_bytes);
+typedef int (*DvrWriteBufferGainPtr)(DvrWriteBuffer* client,
+                                     int* release_fence_fd);
+typedef int (*DvrWriteBufferGainAsyncPtr)(DvrWriteBuffer* client);
+typedef const struct native_handle* (*DvrWriteBufferGetNativeHandle)(
+    DvrWriteBuffer* write_buffer);
+
+typedef void (*DvrReadBufferDestroyPtr)(DvrReadBuffer* client);
+typedef int (*DvrReadBufferGetAHardwareBufferPtr)(
+    DvrReadBuffer* client, AHardwareBuffer** hardware_buffer);
+typedef int (*DvrReadBufferAcquirePtr)(DvrReadBuffer* client,
+                                       int* ready_fence_fd, void* meta,
+                                       size_t meta_size_bytes);
+typedef int (*DvrReadBufferReleasePtr)(DvrReadBuffer* client,
+                                       int release_fence_fd);
+typedef int (*DvrReadBufferReleaseAsyncPtr)(DvrReadBuffer* client);
+typedef const struct native_handle* (*DvrReadBufferGetNativeHandle)(
+    DvrReadBuffer* read_buffer);
+
+typedef void (*DvrBufferDestroy)(DvrBuffer* buffer);
+typedef int (*DvrBufferGetAHardwareBuffer)(DvrBuffer* buffer,
+                                           AHardwareBuffer** hardware_buffer);
+typedef const struct native_handle* (*DvrBufferGetNativeHandle)(
+    DvrBuffer* buffer);
+
+// dvr_buffer_queue.h
+typedef void (*DvrWriteBufferQueueDestroyPtr)(DvrWriteBufferQueue* write_queue);
+typedef size_t (*DvrWriteBufferQueueGetCapacityPtr)(
+    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 size_t (*DvrReadBufferQueueGetCapacityPtr)(
+    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)(int width, int height, int format,
+                                   uint64_t usage0, uint64_t usage1, int flags,
+                                   DvrSurface** out_surface);
+typedef int (*DvrSurfaceGetWriteBufferQueuePtr)(
+    DvrSurface* surface, DvrWriteBufferQueue** out_writer);
+
+// vsync_client_api.h
+typedef dvr_vsync_client* (*DvrVSyncClientCreatePtr)();
+typedef void (*DvrVSyncClientDestroyPtr)(dvr_vsync_client* client);
+typedef int (*DvrVSyncClientGetSchedInfoPtr)(dvr_vsync_client* client,
+                                             int64_t* vsync_period_ns,
+                                             int64_t* next_timestamp_ns,
+                                             uint32_t* next_vsync_count);
+
+// pose_client.h
+typedef DvrPose* (*DvrPoseClientCreatePtr)(void);
+typedef void (*DvrPoseClientDestroyPtr)(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);
+
+struct DvrApi_v1 {
+  // Display manager client
+  DvrDisplayManagerClientCreatePtr display_manager_client_create;
+  DvrDisplayManagerClientDestroyPtr display_manager_client_destroy;
+  DvrDisplayManagerClientGetSurfaceListPtr
+      display_manager_client_get_surface_list;
+  DvrDisplayManagerClientSurfaceListDestroyPtr
+      display_manager_client_surface_list_destroy;
+  DvrDisplayManagerSetupNamedBufferPtr display_manager_setup_named_buffer;
+  DvrDisplayManagerClientSurfaceListGetSizePtr
+      display_manager_client_surface_list_get_size;
+  DvrDisplayManagerClientSurfaceListGetSurfaceIdPtr
+      display_manager_client_surface_list_get_surface_id;
+  DvrDisplayManagerClientGetSurfaceBufferListPtr
+      display_manager_client_get_surface_buffer_list;
+  DvrDisplayManagerClientSurfaceBufferListDestroyPtr
+      display_manager_client_surface_buffer_list_destroy;
+  DvrDisplayManagerClientSurfaceBufferListGetSizePtr
+      display_manager_client_surface_buffer_list_get_size;
+  DvrDisplayManagerClientSurfaceBufferListGetFdPtr
+      display_manager_client_surface_buffer_list_get_fd;
+
+  // Write buffer
+  DvrWriteBufferDestroyPtr write_buffer_destroy;
+  DvrWriteBufferGetAHardwareBufferPtr write_buffer_get_ahardwarebuffer;
+  DvrWriteBufferPostPtr write_buffer_post;
+  DvrWriteBufferGainPtr write_buffer_gain;
+  DvrWriteBufferGainAsyncPtr write_buffer_gain_async;
+  DvrWriteBufferGetNativeHandle write_buffer_get_native_handle;
+
+  // Read buffer
+  DvrReadBufferDestroyPtr read_buffer_destroy;
+  DvrReadBufferGetAHardwareBufferPtr read_buffer_get_ahardwarebuffer;
+  DvrReadBufferAcquirePtr read_buffer_acquire;
+  DvrReadBufferReleasePtr read_buffer_release;
+  DvrReadBufferReleaseAsyncPtr read_buffer_release_async;
+  DvrReadBufferGetNativeHandle read_buffer_get_native_handle;
+
+  // Buffer
+  DvrBufferDestroy buffer_destroy;
+  DvrBufferGetAHardwareBuffer buffer_get_ahardwarebuffer;
+  DvrBufferGetNativeHandle buffer_get_native_handle;
+
+  // Write buffer queue
+  DvrWriteBufferQueueDestroyPtr write_buffer_queue_destroy;
+  DvrWriteBufferQueueGetCapacityPtr write_buffer_queue_get_capacity;
+  DvrWriteBufferQueueGetExternalSurfacePtr
+      write_buffer_queue_get_external_surface;
+  DvrWriteBufferQueueCreateReadQueuePtr write_buffer_queue_create_read_queue;
+  DvrWriteBufferQueueDequeuePtr write_buffer_queue_dequeue;
+
+  // Read buffer queue
+  DvrReadBufferQueueDestroyPtr read_buffer_queue_destroy;
+  DvrReadBufferQueueGetCapacityPtr read_buffer_queue_get_capacity;
+  DvrReadBufferQueueCreateReadQueuePtr read_buffer_queue_create_read_queue;
+  DvrReadBufferQueueDequeuePtr read_buffer_queue_dequeue;
+
+  // V-Sync client
+  DvrVSyncClientCreatePtr vsync_client_create;
+  DvrVSyncClientDestroyPtr vsync_client_destroy;
+  DvrVSyncClientGetSchedInfoPtr vsync_client_get_sched_info;
+
+  // Display surface
+  DvrGetNamedBufferPtr get_named_buffer;
+  DvrSurfaceCreatePtr surface_create;
+  DvrSurfaceGetWriteBufferQueuePtr surface_get_write_buffer_queue;
+
+  // Pose client
+  DvrPoseClientCreatePtr pose_client_create;
+  DvrPoseClientDestroyPtr pose_client_destroy;
+  DvrPoseGetPtr pose_get;
+  DvrPoseGetVsyncCountPtr pose_get_vsync_count;
+  DvrPoseGetControllerPtr pose_get_controller;
+
+  // Virtual touchpad client
+  DvrVirtualTouchpadCreatePtr virtual_touchpad_create;
+  DvrVirtualTouchpadDestroyPtr virtual_touchpad_destroy;
+  DvrVirtualTouchpadAttachPtr virtual_touchpad_attach;
+  DvrVirtualTouchpadDetachPtr virtual_touchpad_detach;
+  DvrVirtualTouchpadTouchPtr virtual_touchpad_touch;
+  DvrVirtualTouchpadButtonStatePtr virtual_touchpad_button_state;
+
+  // VR HWComposer client
+  DvrHwcClientCreatePtr hwc_client_create;
+  DvrHwcClientDestroyPtr hwc_client_destroy;
+  DvrHwcFrameDestroyPtr hwc_frame_destroy;
+  DvrHwcFrameGetDisplayIdPtr hwc_frame_get_display_id;
+  DvrHwcFrameGetDisplayWidthPtr hwc_frame_get_display_width;
+  DvrHwcFrameGetDisplayHeightPtr hwc_frame_get_display_height;
+  DvrHwcFrameGetDisplayRemovedPtr hwc_frame_get_display_removed;
+  DvrHwcFrameGetActiveConfigPtr hwc_frame_get_active_config;
+  DvrHwcFrameGetColorModePtr hwc_frame_get_color_mode;
+  DvrHwcFrameGetColorTransformPtr hwc_frame_get_color_transform;
+  DvrHwcFrameGetPowerModePtr hwc_frame_get_power_mode;
+  DvrHwcFrameGetVsyncEnabledPtr hwc_frame_get_vsync_enabled;
+  DvrHwcFrameGetLayerCountPtr hwc_frame_get_layer_count;
+  DvrHwcFrameGetLayerIdPtr hwc_frame_get_layer_id;
+  DvrHwcFrameGetLayerBufferPtr hwc_frame_get_layer_buffer;
+  DvrHwcFrameGetLayerFencePtr hwc_frame_get_layer_fence;
+  DvrHwcFrameGetLayerDisplayFramePtr hwc_frame_get_layer_display_frame;
+  DvrHwcFrameGetLayerCropPtr hwc_frame_get_layer_crop;
+  DvrHwcFrameGetLayerBlendModePtr hwc_frame_get_layer_blend_mode;
+  DvrHwcFrameGetLayerAlphaPtr hwc_frame_get_layer_alpha;
+  DvrHwcFrameGetLayerTypePtr hwc_frame_get_layer_type;
+  DvrHwcFrameGetLayerApplicationIdPtr hwc_frame_get_layer_application_id;
+  DvrHwcFrameGetLayerZOrderPtr hwc_frame_get_layer_z_order;
+  DvrHwcFrameGetLayerCursorPtr hwc_frame_get_layer_cursor;
+  DvrHwcFrameGetLayerTransformPtr hwc_frame_get_layer_transform;
+  DvrHwcFrameGetLayerDataspacePtr hwc_frame_get_layer_dataspace;
+  DvrHwcFrameGetLayerColorPtr hwc_frame_get_layer_color;
+  DvrHwcFrameGetLayerNumVisibleRegionsPtr
+      hwc_frame_get_layer_num_visible_regions;
+  DvrHwcFrameGetLayerVisibleRegionPtr hwc_frame_get_layer_visible_region;
+  DvrHwcFrameGetLayerNumDamagedRegionsPtr
+      hwc_frame_get_layer_num_damaged_regions;
+  DvrHwcFrameGetLayerDamagedRegionPtr hwc_frame_get_layer_damaged_region;
+};
+
+int dvrGetApi(void* api, size_t struct_size, int version);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_API_H_
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..3e8357c
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer.h
@@ -0,0 +1,72 @@
+#ifndef ANDROID_DVR_BUFFER_H_
+#define ANDROID_DVR_BUFFER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <memory>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrWriteBuffer DvrWriteBuffer;
+typedef struct DvrReadBuffer DvrReadBuffer;
+typedef struct DvrBuffer DvrBuffer;
+typedef struct AHardwareBuffer AHardwareBuffer;
+struct native_handle;
+
+// Write buffer
+void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer);
+int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer);
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
+                                     AHardwareBuffer** hardware_buffer);
+int dvrWriteBufferPost(DvrWriteBuffer* write_buffer, int ready_fence_fd,
+                       const void* meta, size_t meta_size_bytes);
+int dvrWriteBufferGain(DvrWriteBuffer* write_buffer, int* release_fence_fd);
+int dvrWriteBufferGainAsync(DvrWriteBuffer* write_buffer);
+const struct native_handle* dvrWriteBufferGetNativeHandle(
+    DvrWriteBuffer* write_buffer);
+
+// Read buffer
+void dvrReadBufferDestroy(DvrReadBuffer* read_buffer);
+int dvrReadBufferGetId(DvrReadBuffer* read_buffer);
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
+                                    AHardwareBuffer** hardware_buffer);
+int dvrReadBufferAcquire(DvrReadBuffer* read_buffer, int* ready_fence_fd,
+                         void* meta, size_t meta_size_bytes);
+int dvrReadBufferRelease(DvrReadBuffer* read_buffer, int release_fence_fd);
+int dvrReadBufferReleaseAsync(DvrReadBuffer* read_buffer);
+const struct native_handle* dvrReadBufferGetNativeHandle(
+    DvrReadBuffer* read_buffer);
+
+// Buffer
+void dvrBufferDestroy(DvrBuffer* buffer);
+// Caller must call AHardwareBuffer_release on hardware_buffer.
+int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
+                                AHardwareBuffer** hardware_buffer);
+const struct native_handle* dvrBufferGetNativeHandle(DvrBuffer* buffer);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+namespace android {
+namespace dvr {
+
+class BufferProducer;
+class BufferConsumer;
+class IonBuffer;
+
+DvrWriteBuffer* CreateDvrWriteBufferFromBufferProducer(
+    const std::shared_ptr<BufferProducer>& buffer_producer);
+DvrReadBuffer* CreateDvrReadBufferFromBufferConsumer(
+    const std::shared_ptr<BufferConsumer>& buffer_consumer);
+DvrBuffer* CreateDvrBufferFromIonBuffer(
+    const std::shared_ptr<IonBuffer>& ion_buffer);
+
+}  // namespace dvr
+}  // namespace android
+
+#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..ba39513
--- /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 <dvr/dvr_buffer.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ANativeWindow ANativeWindow;
+
+typedef struct DvrWriteBufferQueue DvrWriteBufferQueue;
+typedef struct DvrReadBufferQueue DvrReadBufferQueue;
+
+// WriteBufferQueue
+void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue);
+size_t dvrWriteBufferQueueGetCapacity(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);
+size_t dvrReadBufferQueueGetCapacity(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);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_BUFFER_QUEUE_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..e5228d6
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_surface.h
@@ -0,0 +1,28 @@
+#ifndef ANDROID_DVR_SURFACE_H_
+#define ANDROID_DVR_SURFACE_H_
+
+#include <dvr/dvr_buffer.h>
+#include <dvr/dvr_buffer_queue.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DvrSurface DvrSurface;
+typedef struct DvrSurfaceParameter DvrSurfaceParameter;
+
+// Get a pointer to the global pose buffer.
+int dvrGetNamedBuffer(const char* name, DvrBuffer** out_buffer);
+
+int dvrSurfaceCreate(int width, int height, int format, uint64_t usage0,
+                     uint64_t usage1, int flags, DvrSurface** out_surface);
+
+// TODO(eieio, jwcai) Change this once we have multiple buffer queue support.
+int dvrSurfaceGetWriteBufferQueue(DvrSurface* surface,
+                                  DvrWriteBufferQueue** out_writer);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_SURFACE_H_
diff --git a/libs/vr/libdvr/include/dvr/vsync_client_api.h b/libs/vr/libdvr/include/dvr/vsync_client_api.h
new file mode 100644
index 0000000..0ae5cd5
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/vsync_client_api.h
@@ -0,0 +1,36 @@
+#ifndef ANDROID_DVR_VSYNC_CLIENT_API_H_
+#define ANDROID_DVR_VSYNC_CLIENT_API_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// A client of the vsync service.
+//
+// The "dvr_vsync_client" structure wraps a client connection to the
+// system vsync service. It is used to synchronize application drawing
+// with the scanout of the display.
+typedef struct dvr_vsync_client dvr_vsync_client;
+
+// Creates a new client to the system vsync service.
+dvr_vsync_client* dvr_vsync_client_create();
+
+// Destroys the vsync client.
+void dvr_vsync_client_destroy(dvr_vsync_client* 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. This call has the same side effect on events as
+// Acknowledge, which saves an IPC message.
+int dvr_vsync_client_get_sched_info(dvr_vsync_client* client,
+                                    int64_t* vsync_period_ns,
+                                    int64_t* next_timestamp_ns,
+                                    uint32_t* next_vsync_count);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_VSYNC_CLIENT_API_H_
diff --git a/libs/vr/libdvr/tests/Android.mk b/libs/vr/libdvr/tests/Android.mk
new file mode 100644
index 0000000..29cdc13
--- /dev/null
+++ b/libs/vr/libdvr/tests/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH := $(call my-dir)
+
+shared_libraries := \
+    libbase \
+    libbinder \
+    libcutils \
+    libgui \
+    liblog \
+    libhardware \
+    libui \
+    libutils \
+    libnativewindow \
+
+static_libraries := \
+    libdvr \
+    libbufferhubqueue \
+    libbufferhub \
+    libchrome \
+    libdvrcommon \
+    libdisplay \
+    libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    dvr_buffer_queue-test.cpp \
+    dvr_named_buffer-test.cpp \
+
+LOCAL_STATIC_LIBRARIES := $(static_libraries)
+LOCAL_SHARED_LIBRARIES := $(shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+LOCAL_CFLAGS := -DLOG_TAG=\"dvr_api-test\" -DTRACE=0 -O0 -g
+LOCAL_MODULE := dvr_api-test
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_NATIVE_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..1c9eadd
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -0,0 +1,161 @@
+#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>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+static constexpr int kBufferWidth = 100;
+static constexpr int kBufferHeight = 1;
+static constexpr int kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+static constexpr int kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
+static constexpr int kBufferSliceCount = 1;  // number of slices in each buffer
+static constexpr size_t kQueueCapacity = 3;
+
+typedef uint64_t TestMeta;
+
+class DvrBufferQueueTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    write_queue_ = new DvrWriteBufferQueue;
+    write_queue_->producer_queue_ = ProducerQueue::Create<TestMeta>(0, 0, 0, 0);
+    ASSERT_NE(nullptr, write_queue_->producer_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 = write_queue_->producer_queue_->AllocateBuffer(
+          kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage,
+          kBufferSliceCount, &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, 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);
+
+  AllocateBuffers(kQueueCapacity);
+
+  // Gain buffer for writing.
+  ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, &wb, &fence_fd);
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, 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_NE(nullptr, 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;
+  int ret = dvrWriteBufferQueueGetExternalSurface(write_queue_, &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_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
new file mode 100644
index 0000000..2866f47
--- /dev/null
+++ b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
@@ -0,0 +1,156 @@
+#include <android/hardware_buffer.h>
+#include <dvr/display_manager_client.h>
+#include <dvr/dvr_buffer.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 {
+    client_ = dvrDisplayManagerClientCreate();
+    ASSERT_NE(nullptr, client_);
+  }
+
+  void TearDown() override {
+    if (client_ != nullptr) {
+      dvrDisplayManagerClientDestroy(client_);
+      client_ = nullptr;
+    }
+  }
+
+  DvrDisplayManagerClient* client_ = nullptr;
+};
+
+TEST_F(DvrNamedBufferTest, TestNamedBuffersSameName) {
+  const char* buffer_name = "same_name";
+  DvrBuffer* buffer1 =
+      dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, 0, 0);
+  ASSERT_NE(nullptr, buffer1);
+
+  DvrBuffer* buffer2 =
+      dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, 0, 0);
+  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.usage0, 0u);
+  ASSERT_EQ(desc1.usage1, 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.usage0, 0u);
+  ASSERT_EQ(desc2.usage1, 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(buffer2, &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.usage0, 0u);
+  ASSERT_EQ(desc3.usage1, 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 =
+      dvrDisplayManagerSetupNamedBuffer(client_, buffer_name1, 10, 0, 0);
+  ASSERT_NE(nullptr, setup_buffer1);
+  dvrBufferDestroy(setup_buffer1);
+
+  DvrBuffer* setup_buffer2 =
+      dvrDisplayManagerSetupNamedBuffer(client_, buffer_name2, 10, 0, 0);
+  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 usage0 to AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE. We use this because
+  // internally AHARDWAREBUFFER_USAGE0_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_USAGE0_VIDEO_ENCODE.
+  const int64_t usage0 = AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE;
+
+  DvrBuffer* setup_buffer =
+      dvrDisplayManagerSetupNamedBuffer(client_, buffer_name, 10, usage0, 0);
+  ASSERT_NE(nullptr, setup_buffer);
+
+  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(desc.usage0, AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE);
+
+  dvrBufferDestroy(setup_buffer);
+  AHardwareBuffer_release(hardware_buffer);
+}
+
+
+}  // namespace
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvr/vsync_client_api.cpp b/libs/vr/libdvr/vsync_client_api.cpp
new file mode 100644
index 0000000..dbddd3d
--- /dev/null
+++ b/libs/vr/libdvr/vsync_client_api.cpp
@@ -0,0 +1,24 @@
+#include "include/dvr/vsync_client_api.h"
+
+#include <private/dvr/vsync_client.h>
+
+extern "C" {
+
+dvr_vsync_client* dvr_vsync_client_create() {
+  auto client = android::dvr::VSyncClient::Create();
+  return static_cast<dvr_vsync_client*>(client.release());
+}
+
+void dvr_vsync_client_destroy(dvr_vsync_client* client) {
+  delete static_cast<android::dvr::VSyncClient*>(client);
+}
+
+int dvr_vsync_client_get_sched_info(dvr_vsync_client* client,
+                                    int64_t* vsync_period_ns,
+                                    int64_t* next_timestamp_ns,
+                                    uint32_t* next_vsync_count) {
+  return static_cast<android::dvr::VSyncClient*>(client)->GetSchedInfo(
+      vsync_period_ns, next_timestamp_ns, next_vsync_count);
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
new file mode 100644
index 0000000..eb78348
--- /dev/null
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -0,0 +1,79 @@
+// 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 = [
+    "frame_time_history.cpp",
+    "sync_util.cpp",
+]
+
+localIncludeFiles = [
+    "include",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "liblog",
+    "libutils",
+    "libEGL",
+    "libGLESv2",
+    "libui",
+    "libgui",
+    "libhardware",
+]
+
+staticLibraries = ["libpdx_default_transport"]
+
+headerLibraries = [
+    "libeigen",
+]
+
+cc_library {
+    srcs: sourceFiles,
+    local_include_dirs: localIncludeFiles,
+
+    cflags: [
+        "-DLOG_TAG=\"libdvrcommon\"",
+        "-DTRACE=0",
+    ],
+    export_include_dirs: localIncludeFiles,
+
+    shared_libs: sharedLibraries,
+    static_libs: staticLibraries,
+    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/frame_time_history.cpp b/libs/vr/libdvrcommon/frame_time_history.cpp
new file mode 100644
index 0000000..d4718a9
--- /dev/null
+++ b/libs/vr/libdvrcommon/frame_time_history.cpp
@@ -0,0 +1,37 @@
+#include <private/dvr/frame_time_history.h>
+
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+void FrameTimeHistory::AddSample(int64_t frame_time) {
+  if (size_ == frame_times_.size()) {
+    int64_t expired_frame_time = frame_times_[start_];
+    frame_times_[start_] = frame_time;
+    start_ = (start_ + 1) % frame_times_.size();
+    total_frame_time_ -= expired_frame_time;
+  } else {
+    frame_times_[(start_ + size_) % frame_times_.size()] = frame_time;
+    size_++;
+  }
+  total_frame_time_ += frame_time;
+}
+
+int FrameTimeHistory::GetSampleCount() const { return size_; }
+
+int64_t FrameTimeHistory::GetAverage() const {
+  LOG_ALWAYS_FATAL_IF(size_ == 0);
+  return total_frame_time_ / size_;
+}
+
+void FrameTimeHistory::ResetWithSeed(int64_t frame_time_seed) {
+  start_ = 0;
+  size_ = frame_times_.size();
+  for (size_t i = 0; i < size_; ++i)
+    frame_times_[i] = frame_time_seed;
+  total_frame_time_ = frame_time_seed * size_;
+}
+
+}  // namespace dvr
+}  // namespace android
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..91e12c5
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
@@ -0,0 +1,62 @@
+#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;
+  }
+
+ 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/frame_time_history.h b/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h
new file mode 100644
index 0000000..008c636
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h
@@ -0,0 +1,33 @@
+#ifndef ANDROID_DVR_FRAME_TIME_HISTORY_H_
+#define ANDROID_DVR_FRAME_TIME_HISTORY_H_
+
+#include <stdint.h>
+
+#include <array>
+
+namespace android {
+namespace dvr {
+
+// Maintains frame time history and provides averaging utility methods.
+class FrameTimeHistory {
+ public:
+  void AddSample(int64_t frame_time);
+  int GetSampleCount() const;
+  int64_t GetAverage() const;
+  float GetAverageFps() const {
+    return 1000000000.0f / static_cast<float>(GetAverage());
+  }
+  void ResetWithSeed(int64_t frame_time_seed);
+
+ private:
+  static constexpr int kFrameTimeHistoryNumSamples = 30;
+  std::array<int64_t, kFrameTimeHistoryNumSamples> frame_times_;
+  int start_ = 0;
+  size_t size_ = 0;
+  int64_t total_frame_time_ = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_FRAME_TIME_HISTORY_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/platform_defines.h b/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h
new file mode 100644
index 0000000..2176903
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_DVR_PLATFORM_DEFINES_H_
+#define ANDROID_DVR_PLATFORM_DEFINES_H_
+
+#include <hardware/gralloc1.h>
+// Platform-specific macros and defines.
+
+// QCOM's GRALLOC_USAGE_PRIVATE_ALLOC_UBWC usage bits.
+#define GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION GRALLOC_USAGE_PRIVATE_1 | GRALLOC1_PRODUCER_USAGE_PRIVATE_0
+
+// QCOM bit to use the ADSP heap. This carveout heap is accessible to Linux,
+// Hexagon DSPs, and the GPU.
+#define GRALLOC_USAGE_PRIVATE_ADSP_HEAP 0x01000000
+
+// Force a gralloc buffer to get the uncached ION option set.
+#define GRALLOC_USAGE_PRIVATE_UNCACHED 0x02000000
+
+#endif  // ANDROID_DVR_PLATFORM_DEFINES_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/sync_util.h b/libs/vr/libdvrcommon/include/private/dvr/sync_util.h
new file mode 100644
index 0000000..c6911bc
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/sync_util.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_SYNC_UTIL_H_
+#define ANDROID_DVR_SYNC_UTIL_H_
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android {
+namespace dvr {
+
+constexpr size_t kFenceInfoBufferSize = 4096;
+
+// This buffer is eventually mapped to a sync_fence_info_data struct (from
+// sync.h), whose largest member is a uint32_t. We align to 8 bytes to be extra
+// cautious.
+using FenceInfoBuffer = std::aligned_storage<kFenceInfoBufferSize, 8>::type;
+
+// Get fence info. Internally this works just like sync_fence_info(), except the
+// caller supplies a memory buffer instead of allocating memory.
+// On success, returns 0. On error, -1 is returned, and errno is set.
+int GetSyncFenceInfo(int fence_fd, FenceInfoBuffer* buffer);
+
+// Returns the timestamp when the fence was first signaled. buffer is used as
+// described in GetSyncFenceInfo().
+// On success, returns 0. On error, -1 is returned, and errno is set.
+int GetFenceSignaledTimestamp(int fence_fd, FenceInfoBuffer* buffer,
+                              int64_t* timestamp);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SYNC_UTIL_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/sync_util.cpp b/libs/vr/libdvrcommon/sync_util.cpp
new file mode 100644
index 0000000..3637936
--- /dev/null
+++ b/libs/vr/libdvrcommon/sync_util.cpp
@@ -0,0 +1,87 @@
+#include "include/private/dvr/sync_util.h"
+
+#include <errno.h>
+#include <sys/ioctl.h>
+
+// TODO: http://b/33239638 Move GetSyncFenceInfo() into upstream libsync instead
+//   of duplicating functionality and structure definitions from it.
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+#define SYNC_IOC_MAGIC '>'
+#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
+#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_fence_info_data)
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// This is copied from sync_pt_info() in libsync/sync.c. It's been cleaned up to
+// remove lint warnings.
+sync_pt_info* GetSyncPtInfo(sync_fence_info_data* info, sync_pt_info* itr) {
+  if (itr == nullptr)
+    itr = reinterpret_cast<sync_pt_info*>(info->pt_info);
+  else
+    itr = reinterpret_cast<sync_pt_info*>(reinterpret_cast<uint8_t*>(itr) +
+                                          itr->len);
+
+  if (reinterpret_cast<uint8_t*>(itr) - reinterpret_cast<uint8_t*>(info) >=
+      static_cast<int>(info->len))
+    return nullptr;
+
+  return itr;
+}
+
+}  // namespace
+
+int GetSyncFenceInfo(int fence_fd, FenceInfoBuffer* buffer) {
+  // If the implementation of sync_fence_info() in libsync/sync.c changes, this
+  // function should be changed to match.
+  if (buffer == nullptr) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  sync_fence_info_data* fence_info =
+      reinterpret_cast<sync_fence_info_data*>(buffer);
+  fence_info->len = kFenceInfoBufferSize;
+  return ioctl(fence_fd, SYNC_IOC_FENCE_INFO, fence_info);
+}
+
+int GetFenceSignaledTimestamp(int fence_fd, FenceInfoBuffer* buffer,
+                              int64_t* timestamp) {
+  int result = GetSyncFenceInfo(fence_fd, buffer);
+  if (result < 0)
+    return result;
+
+  sync_fence_info_data* fence_info =
+      reinterpret_cast<sync_fence_info_data*>(buffer);
+  struct sync_pt_info* pt_info = nullptr;
+  while ((pt_info = GetSyncPtInfo(fence_info, pt_info)) != nullptr) {
+    if (pt_info->status == 1) {
+      *timestamp = pt_info->timestamp_ns;
+      return 0;
+    }
+  }
+
+  errno = EAGAIN;
+  return -1;
+}
+
+}  // namespace dvr
+}  // namespace android
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/libdvrgraphics/Android.bp b/libs/vr/libdvrgraphics/Android.bp
new file mode 100644
index 0000000..73a8bf8
--- /dev/null
+++ b/libs/vr/libdvrgraphics/Android.bp
@@ -0,0 +1,45 @@
+
+
+sourceFiles = [
+    "blur.cpp",
+    "debug_text.cpp",
+    "egl_image.cpp",
+    "gpu_profiler.cpp",
+    "shader_program.cpp",
+    "timer_query.cpp",
+    "vr_gl_extensions.cpp",
+]
+
+localIncludeFiles = [
+    "include",
+]
+
+staticLibraries = [
+    "libbufferhub",
+    "libdvrcommon",
+    "libpdx_default_transport",
+]
+
+sharedLibraries = [
+    "libcutils",
+    "libbase",
+    "libEGL",
+    "libGLESv2",
+    "libpng",
+    "liblog",
+]
+
+cc_library_static {
+    srcs: sourceFiles,
+    cflags: [
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],
+    export_include_dirs: localIncludeFiles,
+    shared_libs: sharedLibraries,
+    static_libs: staticLibraries,
+    // Rather than add this header-file-only library to all users of libdvrgraphics,
+    // include it here.
+    whole_static_libs: ["libarect"],
+    name: "libdvrgraphics",
+}
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2.png b/libs/vr/libdvrgraphics/assets/controller_proto2.png
new file mode 100644
index 0000000..ffcb646
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2.png
Binary files differ
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_body.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_body.obj
new file mode 100644
index 0000000..4e54900
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_body.obj
@@ -0,0 +1,5018 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v 0.001777 0.012900 0.101619
+v -0.005750 0.012900 0.096150
+v -0.004652 0.012900 0.092770
+v 0.005750 0.012900 0.096150
+v 0.004652 0.012900 0.099530
+v -0.001777 0.012900 0.090681
+v 0.004652 0.012900 0.092770
+v 0.001777 0.012900 0.090681
+v -0.001777 0.012900 0.101619
+v -0.004652 0.012900 0.099530
+v -0.001777 0.012900 0.073181
+v -0.004652 0.012900 0.075270
+v 0.001777 0.012900 0.073181
+v 0.004652 0.012900 0.075270
+v 0.005750 0.012900 0.078650
+v 0.004652 0.012900 0.082030
+v -0.005750 0.012900 0.078650
+v -0.004652 0.012900 0.082030
+v -0.001777 0.012900 0.084119
+v 0.001777 0.012900 0.084119
+v -0.016000 0.012900 0.050841
+v 0.016000 0.012900 0.050835
+v 0.013856 0.012900 0.058900
+v 0.008000 0.012900 0.064756
+v -0.000000 0.012900 0.066900
+v -0.008000 0.012900 0.064756
+v -0.013856 0.012900 0.058900
+v 0.008000 0.012900 0.037044
+v -0.008000 0.012900 0.037044
+v -0.013856 0.012900 0.042900
+v 0.000000 0.012900 0.034900
+v 0.013856 0.012900 0.042900
+v -0.020000 0.006760 0.075600
+v -0.020000 0.006760 -0.012398
+v -0.018877 -0.001982 0.044797
+v -0.018480 -0.003505 0.048732
+v -0.018777 -0.001945 0.075600
+v -0.017726 -0.003833 0.163093
+v -0.013161 -0.013053 0.058159
+v -0.016670 -0.008191 0.054819
+v -0.016119 -0.008731 0.075600
+v -0.009698 -0.015662 0.059338
+v -0.012025 -0.013632 0.075600
+v -0.006504 -0.016678 0.075600
+v -0.000000 -0.017750 0.075600
+v -0.000000 -0.015479 0.162587
+v 0.006873 -0.016611 0.058552
+v 0.005238 -0.017469 0.059981
+v -0.000000 -0.018152 0.060190
+v -0.006874 -0.016610 0.058551
+v -0.005577 -0.017375 0.059951
+v -0.000001 -0.017790 0.058889
+v -0.017783 -0.005639 0.052082
+v -0.016159 -0.008440 0.053561
+v -0.018202 -0.002766 0.042130
+v -0.019040 -0.001282 0.040457
+v -0.019004 -0.001521 0.036065
+v -0.019278 -0.000710 -0.012015
+v -0.018775 -0.002635 0.031956
+v -0.018323 -0.004451 0.028346
+v -0.017636 -0.005173 0.029543
+v -0.015438 -0.011195 0.021684
+v -0.017576 -0.006793 0.025305
+v -0.017651 -0.007495 -0.011666
+v -0.016133 -0.009192 0.024525
+v -0.009254 -0.016963 0.019047
+v -0.012694 -0.014052 0.021271
+v -0.014657 -0.012757 -0.011402
+v -0.012775 -0.014437 0.019994
+v -0.004877 -0.018641 0.018555
+v 0.000000 -0.019243 0.018406
+v -0.004053 -0.017659 -0.013120
+v 0.000000 -0.020012 -0.011045
+v -0.007741 -0.016386 -0.013187
+v -0.007793 -0.018450 -0.011122
+v -0.011145 -0.016607 -0.011212
+v -0.013377 -0.011450 -0.013446
+v -0.017416 0.000890 -0.014092
+v -0.010471 -0.009590 0.164744
+v -0.020000 0.006760 0.163598
+v -0.017045 0.000607 0.165278
+v -0.019121 0.000433 0.163292
+v -0.012455 -0.011131 0.162773
+v -0.015399 -0.007954 0.162910
+v -0.011105 -0.009754 0.164735
+v -0.008928 -0.013453 0.162674
+v -0.004759 -0.014945 0.162610
+v 0.000000 -0.013582 0.164534
+v -0.016850 0.012900 -0.012204
+v -0.016850 0.011003 -0.014204
+v -0.017833 0.012745 0.163406
+v -0.016850 0.011003 0.165404
+v 0.016850 0.011003 0.165404
+v 0.016850 0.012900 0.163404
+v -0.016850 0.012900 0.163404
+v -0.018496 0.012405 0.163413
+v -0.019171 0.011802 0.163424
+v -0.017585 0.010679 0.165421
+v -0.019183 0.011788 -0.012224
+v -0.019646 0.010959 0.163436
+v -0.017717 0.010496 0.165430
+v -0.019850 0.009900 -0.012263
+v -0.017851 0.009891 0.165462
+v -0.019850 0.009900 0.163463
+v -0.017851 0.009891 -0.014262
+v -0.019650 0.010946 -0.012236
+v -0.018511 0.012395 -0.012213
+v -0.017342 0.010872 -0.014211
+v -0.017849 0.012739 -0.012207
+v 0.019850 0.009900 -0.012263
+v 0.019850 0.009900 0.163463
+v 0.019652 0.010946 0.163437
+v 0.019180 0.011789 0.163424
+v 0.019649 0.010953 -0.012236
+v 0.017351 0.010866 0.165411
+v 0.018506 0.012398 0.163413
+v 0.017845 0.012741 0.163406
+v 0.018501 0.012401 -0.012213
+v 0.016850 0.012900 -0.012204
+v 0.016850 0.011003 -0.014204
+v 0.017836 0.012745 -0.012207
+v 0.017359 0.010861 -0.014211
+v 0.019174 0.011796 -0.012224
+v 0.020000 0.006760 -0.012398
+v 0.020000 0.006760 0.075600
+v 0.020000 0.006760 0.163598
+v 0.017851 0.009891 0.165462
+v 0.004914 -0.018632 0.018558
+v 0.012694 -0.014513 0.019962
+v 0.009842 -0.017439 -0.011172
+v 0.009194 -0.016995 0.019036
+v 0.012696 -0.014051 0.021272
+v 0.016134 -0.009190 0.024527
+v 0.018283 -0.005474 -0.011769
+v 0.015491 -0.011112 0.021736
+v 0.017878 -0.005923 0.026303
+v 0.016851 -0.008573 0.023600
+v 0.018521 -0.003703 0.029628
+v 0.019042 -0.001309 0.037868
+v 0.018891 -0.002096 0.033543
+v 0.018999 -0.001456 0.042247
+v 0.018278 -0.004181 0.049954
+v 0.018757 -0.002471 0.046344
+v 0.017676 -0.004685 0.048340
+v 0.018202 -0.002767 0.042132
+v 0.017483 -0.006410 0.053015
+v 0.016158 -0.008441 0.053562
+v 0.016251 -0.008973 0.055490
+v 0.013246 -0.012968 0.058115
+v 0.012687 -0.013111 0.057055
+v 0.009848 -0.015576 0.059304
+v 0.006504 -0.016678 0.075600
+v 0.012025 -0.013632 0.075600
+v 0.012453 -0.011132 0.162773
+v 0.015398 -0.007956 0.162909
+v 0.018777 -0.001945 0.075600
+v 0.016119 -0.008731 0.075600
+v 0.017726 -0.003834 0.163093
+v 0.016090 -0.005051 -0.013781
+v 0.019494 0.000840 -0.012095
+v 0.014597 -0.009662 -0.013539
+v 0.015987 -0.010626 -0.011508
+v 0.012048 -0.013130 -0.013358
+v 0.012738 -0.015322 -0.011275
+v 0.004233 -0.017617 -0.013122
+v 0.004123 -0.019588 -0.011066
+v 0.008927 -0.013454 0.162674
+v 0.004758 -0.014945 0.162610
+v 0.007966 -0.011801 0.164627
+v 0.011095 -0.009763 0.164734
+v -0.016170 0.000544 0.165275
+v 0.019122 0.000435 0.163292
+v -0.000000 -0.018116 -0.013096
+v 0.008618 -0.015654 -0.013225
+v -0.018000 0.006760 -0.014400
+v -0.016263 -0.005366 -0.013764
+v -0.010678 -0.014560 -0.013283
+v 0.018001 0.006760 -0.014400
+v 0.017116 0.010978 -0.014205
+v -0.017101 0.010981 -0.014205
+v -0.017576 0.010688 -0.014220
+v -0.017712 0.010505 -0.014230
+v 0.017851 0.009891 -0.014262
+v 0.017713 0.010505 -0.014230
+v -0.000000 -0.016565 0.057943
+v 0.005098 -0.015909 0.057716
+v -0.012314 -0.011868 0.055681
+v 0.012233 -0.011949 0.055734
+v -0.009091 -0.014324 0.057057
+v 0.009220 -0.014251 0.057022
+v -0.014780 -0.008209 0.052349
+v 0.014512 -0.008747 0.052978
+v 0.015923 -0.005957 0.049562
+v 0.016463 -0.004315 0.046176
+v -0.016130 -0.005374 0.048538
+v 0.016787 -0.003149 0.038024
+v -0.016587 -0.003874 0.044839
+v 0.016736 -0.003311 0.042260
+v 0.016623 -0.003908 0.033843
+v -0.016780 -0.003141 0.040668
+v 0.016216 -0.005454 0.030112
+v 0.018185 -0.002935 0.035641
+v 0.015501 -0.007542 0.027010
+v 0.014368 -0.009927 0.024580
+v -0.015201 -0.008261 0.026184
+v 0.017637 -0.005172 0.029546
+v 0.011788 -0.013271 0.022276
+v 0.008677 -0.015490 0.021243
+v 0.006874 -0.017616 0.019963
+v 0.004690 -0.016976 0.020729
+v -0.008734 -0.015460 0.021254
+v -0.000000 -0.017541 0.020568
+v 0.000001 -0.018813 0.019687
+v -0.006872 -0.017617 0.019962
+v -0.004662 -0.016983 0.020727
+v -0.011831 -0.013230 0.022299
+v -0.014150 -0.010300 0.024269
+v -0.016017 -0.006100 0.029007
+v -0.018185 -0.002936 0.035639
+v -0.016751 -0.003331 0.036408
+v -0.016508 -0.004384 0.032442
+v -0.017676 -0.004684 0.048338
+v -0.012688 -0.013110 0.057054
+v -0.005290 -0.015857 0.057697
+v -0.017359 0.010861 0.165411
+v -0.017114 0.010978 0.165405
+v 0.017105 0.010980 0.165405
+v 0.017998 0.006760 0.165600
+v 0.013678 -0.007019 0.164878
+v 0.017042 0.000595 0.165277
+v -0.017998 0.006760 0.165600
+v 0.004260 -0.013110 0.164559
+v -0.004262 -0.013110 0.164559
+v -0.007973 -0.011798 0.164627
+v -0.013690 -0.007003 0.164879
+v 0.014199 0.012900 0.042702
+v 0.000000 0.012900 0.034505
+v -0.014199 0.012900 0.042702
+v -0.008198 0.012900 0.036701
+v 0.008198 0.012900 0.036701
+v -0.014199 0.012900 0.059098
+v -0.008198 0.012900 0.065099
+v -0.000000 0.012900 0.067295
+v 0.008198 0.012900 0.065099
+v 0.014199 0.012900 0.059098
+v -0.001880 0.012900 0.072863
+v -0.004923 0.012900 0.075074
+v 0.001880 0.012900 0.072863
+v 0.004923 0.012900 0.075074
+v 0.006085 0.012900 0.078650
+v 0.004923 0.012900 0.082227
+v -0.006085 0.012900 0.078650
+v -0.004923 0.012900 0.082227
+v -0.001880 0.012900 0.084437
+v 0.001880 0.012900 0.084437
+v -0.005003 0.012900 0.099785
+v -0.001911 0.012900 0.102031
+v 0.001911 0.012900 0.090269
+v 0.005003 0.012900 0.092515
+v -0.001911 0.012900 0.090269
+v 0.005003 0.012900 0.099785
+v 0.006184 0.012900 0.096150
+v -0.005003 0.012900 0.092515
+v -0.006184 0.012900 0.096150
+v 0.001911 0.012900 0.102031
+v 0.017551 0.010715 -0.014201
+v 0.017144 0.009891 -0.014262
+v 0.017010 0.010505 -0.014230
+v 0.017287 0.006760 -0.014400
+v -0.017159 0.009891 -0.014262
+v -0.017025 0.010505 -0.014230
+v -0.017302 0.006760 -0.014400
+v -0.003838 -0.017246 -0.013142
+v -0.007274 -0.016060 -0.013204
+v -0.012667 -0.011115 -0.013463
+v -0.016493 0.001142 -0.014106
+v 0.015107 -0.004951 -0.013786
+v 0.013539 -0.009795 -0.013532
+v 0.011678 -0.012609 -0.013385
+v 0.003882 -0.017207 -0.013144
+v -0.000062 -0.017671 -0.013120
+v 0.008313 -0.015223 -0.013248
+v -0.015318 -0.005229 -0.013772
+v -0.010464 -0.013804 -0.013322
+v -0.003962 -0.019557 -0.011198
+v 0.016850 0.012900 0.075600
+v 0.019850 0.009900 0.075600
+v 0.019177 0.011793 0.075600
+v 0.018504 0.012399 0.075600
+v 0.019650 0.010950 0.075600
+v -0.016850 0.012900 0.075600
+v -0.019177 0.011795 0.075600
+v -0.017841 0.012742 0.075600
+v -0.019850 0.009900 0.075600
+v -0.019648 0.010952 0.075600
+v -0.018504 0.012400 0.075600
+v 0.017708 0.010514 0.165429
+v 0.017545 0.010700 0.165401
+v 0.017078 0.010513 0.165429
+v 0.017357 0.006760 0.165600
+v 0.017216 0.009891 0.165462
+v -0.017229 0.009891 0.165462
+v -0.017370 0.006760 0.165600
+v -0.017099 0.010496 0.165430
+v -0.000000 -0.013199 0.164554
+v 0.007511 -0.011520 0.164642
+v 0.010461 -0.009598 0.164743
+v 0.012897 -0.007011 0.164878
+v 0.016165 0.000533 0.165274
+v 0.004017 -0.012754 0.164578
+v -0.004019 -0.012754 0.164578
+v -0.007518 -0.011517 0.164642
+v -0.012909 -0.006996 0.164879
+v 0.000000 0.012900 0.163404
+v 0.000000 0.011003 0.165404
+v -0.000006 0.009891 0.165462
+v -0.000006 0.006760 0.165600
+v -0.000010 0.010505 0.165429
+v -0.000003 0.000539 0.165274
+v -0.000006 -0.007003 0.164879
+v -0.000005 -0.009594 0.164743
+v -0.000003 -0.011518 0.164642
+v -0.016850 0.012900 0.031793
+v -0.020000 0.006760 0.031793
+v 0.020000 0.006760 0.031793
+v 0.016850 0.012900 0.031793
+v -0.019181 0.011791 0.031793
+v -0.017846 0.012740 0.031793
+v 0.018502 0.012400 0.031793
+v 0.019650 0.010952 0.031793
+v 0.017838 0.012744 0.031793
+v 0.019850 0.009900 0.031793
+v 0.019175 0.011795 0.031793
+v -0.019850 0.009900 0.031793
+v -0.019650 0.010948 0.031793
+v -0.018509 0.012397 0.031793
+v -0.019180 0.011791 0.037044
+v -0.020000 0.006760 0.037044
+v -0.017846 0.012741 0.037044
+v -0.016850 0.012900 0.037044
+v -0.019850 0.009900 0.037044
+v -0.019649 0.010948 0.037044
+v -0.018508 0.012397 0.037044
+v -0.014664 -0.003820 0.165046
+v 0.014655 -0.003835 0.165045
+v 0.015513 -0.003806 0.165046
+v -0.015521 -0.003790 0.165047
+v -0.000004 -0.003827 0.165045
+v 0.000251 -0.015641 -0.013226
+v -0.019850 0.009900 0.043222
+v -0.019649 0.010949 0.043222
+v -0.018507 0.012398 0.043222
+v -0.019180 0.011792 0.043222
+v -0.020000 0.006760 0.043222
+v -0.017845 0.012741 0.043222
+v -0.019850 0.009900 0.050841
+v -0.019649 0.010950 0.050841
+v -0.018506 0.012398 0.050841
+v -0.019179 0.011793 0.050841
+v -0.020000 0.006760 0.050841
+v -0.017844 0.012741 0.050841
+v -0.016850 0.012900 0.050841
+v -0.019850 0.009900 0.059353
+v -0.019649 0.010951 0.059352
+v -0.018505 0.012399 0.059353
+v -0.019178 0.011794 0.059352
+v -0.020000 0.006760 0.059352
+v -0.017843 0.012741 0.059352
+v 0.020000 0.006760 0.025653
+v -0.016850 0.012900 0.025680
+v -0.020000 0.006760 0.025653
+v 0.016850 0.012900 0.025680
+v -0.019181 0.011790 0.025677
+v -0.017847 0.012740 0.025679
+v 0.018502 0.012401 0.025678
+v 0.019650 0.010952 0.025675
+v 0.017837 0.012744 0.025679
+v 0.000251 -0.013207 -0.013354
+v 0.000000 0.012900 -0.012204
+v 0.000000 0.012900 0.025680
+v 0.000251 -0.010455 -0.013498
+v -0.000007 0.010505 -0.014230
+v -0.000008 0.009891 -0.014262
+v 0.019850 0.009900 0.025671
+v 0.019175 0.011795 0.025677
+v -0.019850 0.009900 0.025671
+v -0.019650 0.010948 0.025675
+v -0.018509 0.012396 0.025678
+v 0.017448 -0.005338 0.075600
+v 0.019388 0.002407 0.075600
+v 0.017236 0.000838 -0.014090
+v 0.016380 0.000959 -0.014096
+v 0.020000 0.006760 0.037056
+v 0.019850 0.009900 0.037056
+v 0.019175 0.011795 0.037056
+v 0.018502 0.012400 0.037056
+v 0.019650 0.010952 0.037056
+v 0.020000 0.006760 0.043243
+v 0.019850 0.009900 0.043243
+v 0.019176 0.011794 0.043243
+v 0.018502 0.012400 0.043243
+v 0.019650 0.010951 0.043243
+v 0.016395 0.012900 0.050834
+v 0.020000 0.006760 0.050835
+v 0.019850 0.009900 0.050835
+v 0.019176 0.011794 0.050835
+v 0.018503 0.012400 0.050835
+v 0.019650 0.010951 0.050836
+v 0.017838 0.012744 0.043243
+v 0.016850 0.012900 0.037044
+v 0.017838 0.012744 0.037044
+v 0.000251 0.006760 -0.014400
+v 0.000251 -0.005090 -0.013779
+v 0.016850 0.012900 0.050835
+v 0.017838 0.012744 0.050835
+v 0.016850 0.012900 0.043243
+v 0.017838 0.012744 0.075600
+v 0.000251 0.001050 -0.014101
+v -0.017448 -0.005338 0.075600
+v -0.019388 0.002407 0.075600
+v 0.020000 0.006760 0.059351
+v 0.019850 0.009900 0.059351
+v 0.019176 0.011793 0.059351
+v 0.018503 0.012400 0.059351
+v 0.019650 0.010951 0.059351
+v 0.017838 0.012744 0.059351
+v 0.016850 0.012900 0.059351
+v -0.016850 0.012900 0.043222
+v 0.000000 0.011003 -0.014204
+v 0.018424 -0.002660 0.026136
+v -0.016395 0.012900 0.050840
+v -0.016850 0.012900 0.059352
+v -0.009165 0.012900 0.107061
+v 0.018510 -0.002803 0.055317
+v 0.009165 0.012900 0.107061
+v 0.000000 0.012900 0.107061
+v -0.016000 0.009159 0.050841
+v 0.016000 0.009159 0.050835
+v 0.013856 0.009159 0.058900
+v 0.008000 0.009159 0.064756
+v -0.000000 0.009159 0.066900
+v -0.008000 0.009159 0.064756
+v -0.013856 0.009159 0.058900
+v 0.008000 0.009159 0.037044
+v -0.008000 0.009159 0.037044
+v -0.013856 0.009159 0.042900
+v 0.000000 0.009159 0.034900
+v 0.013856 0.009159 0.042900
+v 0.001777 0.009159 0.101619
+v -0.005750 0.009159 0.096150
+v -0.004652 0.009159 0.092770
+v 0.005750 0.009159 0.096150
+v 0.004652 0.009159 0.099530
+v -0.001777 0.009159 0.090681
+v 0.004652 0.009159 0.092770
+v 0.001777 0.009159 0.090681
+v -0.001777 0.009159 0.101619
+v -0.004652 0.009159 0.099530
+v -0.001777 0.009159 0.073181
+v -0.004652 0.009159 0.075270
+v 0.001777 0.009159 0.073181
+v 0.004652 0.009159 0.075270
+v 0.005750 0.009159 0.078650
+v 0.004652 0.009159 0.082030
+v -0.005750 0.009159 0.078650
+v -0.004652 0.009159 0.082030
+v -0.001777 0.009159 0.084119
+v 0.001777 0.009159 0.084119
+v 0.019986 0.007045 -0.012386
+v -0.017987 0.007044 -0.014388
+v 0.017344 0.007044 0.165587
+v 0.017987 0.007044 -0.014388
+v -0.019986 0.007045 -0.012386
+v 0.017985 0.007044 0.165587
+v 0.019986 0.007044 0.163586
+v -0.017985 0.007044 0.165587
+v -0.019986 0.007044 0.163586
+v 0.017274 0.007044 -0.014388
+v -0.017289 0.007044 -0.014388
+v 0.019986 0.007045 0.075600
+v -0.019986 0.007045 0.075600
+v -0.017357 0.007044 0.165587
+v -0.000006 0.007044 0.165587
+v -0.019986 0.007045 0.031793
+v 0.019986 0.007045 0.031793
+v -0.019986 0.007045 0.037044
+v 0.000227 0.007044 -0.014388
+v -0.019986 0.007045 0.043222
+v -0.019986 0.007045 0.050841
+v -0.019986 0.007045 0.059352
+v 0.019986 0.007045 0.025654
+v -0.019986 0.007045 0.025654
+v 0.019986 0.007045 0.037056
+v 0.019986 0.007045 0.043243
+v 0.019986 0.007045 0.050835
+v 0.019986 0.007045 0.059351
+v -0.019559 0.006736 0.075337
+v -0.019559 0.006736 -0.011771
+v -0.019559 0.006736 0.163028
+v 0.019559 0.006736 -0.011771
+v 0.019559 0.006736 0.075337
+v 0.019559 0.006736 0.163028
+v -0.017603 0.006736 -0.013672
+v 0.017604 0.006736 -0.013672
+v 0.017935 0.006736 0.165023
+v -0.017935 0.006736 0.165023
+v 0.016906 0.006736 -0.013672
+v -0.016920 0.006736 -0.013672
+v 0.017297 0.006736 0.165023
+v -0.017309 0.006736 0.165023
+v -0.000006 0.006736 0.165023
+v -0.019559 0.006736 0.031683
+v 0.019559 0.006736 0.031683
+v -0.019559 0.006736 0.036915
+v -0.019559 0.006736 0.043072
+v -0.019559 0.006736 0.050664
+v -0.019559 0.006736 0.059146
+v 0.019559 0.006736 0.025563
+v -0.019559 0.006736 0.025563
+v 0.019559 0.006736 0.036927
+v 0.019559 0.006736 0.043092
+v 0.019559 0.006736 0.050658
+v 0.000245 0.006736 -0.013672
+v 0.019559 0.006736 0.059144
+v 0.019545 0.007020 -0.011759
+v -0.017590 0.007019 -0.013660
+v 0.017284 0.007019 0.165011
+v 0.017590 0.007019 -0.013660
+v -0.019545 0.007020 -0.011759
+v 0.017922 0.007019 0.165011
+v 0.019545 0.007020 0.163016
+v -0.017922 0.007019 0.165011
+v -0.019545 0.007020 0.163016
+v 0.016893 0.007019 -0.013660
+v -0.016908 0.007019 -0.013660
+v 0.019545 0.007020 0.075337
+v -0.019545 0.007020 0.075337
+v -0.017297 0.007019 0.165011
+v -0.000006 0.007019 0.165011
+v -0.019545 0.007020 0.031683
+v 0.019545 0.007020 0.031683
+v -0.019545 0.007020 0.036915
+v 0.000222 0.007019 -0.013660
+v -0.019545 0.007020 0.043072
+v -0.019545 0.007020 0.050664
+v -0.019545 0.007020 0.059146
+v 0.019545 0.007020 0.025565
+v -0.019545 0.007020 0.025565
+v 0.019545 0.007020 0.036927
+v 0.019545 0.007020 0.043092
+v 0.019545 0.007020 0.050658
+v 0.019545 0.007020 0.059144
+v -0.017984 0.007113 -0.014384
+v 0.017341 0.007113 0.165584
+v -0.019983 0.007114 -0.012383
+v -0.019983 0.007114 0.163583
+v 0.019983 0.007114 0.075600
+v -0.000006 0.007113 0.165584
+v -0.019983 0.007114 0.031793
+v -0.019983 0.007114 0.037044
+v -0.019983 0.007114 0.043222
+v -0.019983 0.007114 0.050841
+v -0.019983 0.007114 0.059352
+v -0.019983 0.007114 0.025655
+v 0.019983 0.007114 -0.012383
+v 0.017984 0.007113 -0.014384
+v 0.017981 0.007113 0.165584
+v 0.019983 0.007114 0.163583
+v -0.017981 0.007113 0.165584
+v 0.017271 0.007113 -0.014384
+v -0.017286 0.007113 -0.014384
+v -0.019983 0.007114 0.075600
+v -0.017354 0.007113 0.165584
+v 0.019983 0.007114 0.031793
+v 0.000221 0.007113 -0.014384
+v 0.019983 0.007114 0.025655
+v 0.019983 0.007114 0.037056
+v 0.019983 0.007114 0.043243
+v 0.019983 0.007114 0.050835
+v 0.019983 0.007114 0.059351
+v -0.017987 0.006687 0.165596
+v -0.019990 0.006685 0.163594
+v 0.017987 0.006687 0.165596
+v -0.019991 0.006672 -0.012393
+v 0.017992 0.006690 -0.014396
+v -0.017994 0.006691 -0.014396
+v 0.019990 0.006685 0.163594
+v -0.019993 0.006709 0.075600
+v 0.019994 0.006690 -0.012394
+v -0.017293 0.006694 -0.014397
+v 0.017343 0.006687 0.165596
+v -0.017356 0.006687 0.165596
+v -0.000006 0.006687 0.165596
+v -0.019987 0.006657 0.050770
+v 0.000251 0.006693 -0.014396
+v -0.019989 0.006665 0.043189
+v -0.019988 0.006662 0.037032
+v -0.019986 0.006649 0.031795
+v -0.019980 0.006628 0.025684
+v 0.019993 0.006709 0.075600
+v 0.017276 0.006692 -0.014396
+v 0.019981 0.006649 0.025658
+v 0.019985 0.006651 0.050782
+v 0.019989 0.006665 0.037065
+v 0.019987 0.006656 0.031814
+v 0.019988 0.006663 0.043231
+v -0.019982 0.006639 0.059227
+v 0.019982 0.006647 0.059303
+vt 0.554694 0.657391
+vt 0.433825 0.577068
+vt 0.537413 0.732767
+vt 0.171840 0.756473
+vt 0.004212 0.517422
+vt 0.508978 0.054638
+vt 0.392232 0.366129
+vt 0.535955 0.277647
+vt 0.676569 0.426611
+vt 0.570944 0.608854
+vt 0.657163 0.340708
+vt 0.197012 0.750978
+vt 0.634797 0.224341
+vt 0.536619 0.527298
+vt 0.536314 0.531147
+vt 0.536640 0.005947
+vt 0.644694 0.154642
+vt 0.655354 0.714621
+vt 0.454133 0.386270
+vt 0.251537 0.915962
+vt 0.562843 0.123107
+vt 0.676441 0.480812
+vt 0.655355 0.642524
+vt 0.637665 0.486200
+vt 0.432241 0.529792
+vt 0.433661 0.200774
+vt 0.633750 0.219086
+vt 0.454217 0.366000
+vt 0.347872 0.351543
+vt 0.532084 0.715142
+vt 0.083498 0.916082
+vt 0.432769 0.139167
+vt 0.029777 0.173023
+vt 0.971059 0.270165
+vt 0.196511 0.924853
+vt 0.069097 0.789362
+vt 0.432118 0.226219
+vt 0.960817 0.525768
+vt 0.062888 0.898170
+vt 0.751919 0.532438
+vt 0.645243 0.032928
+vt 0.536503 0.029725
+vt 0.107693 0.763735
+vt 0.746048 0.263094
+vt 0.039255 0.237927
+vt 0.745523 0.377500
+vt 0.794566 0.698936
+vt 0.748277 0.502909
+vt 0.981678 0.688262
+vt 0.655395 0.610961
+vt 0.014520 0.474610
+vt 0.967778 0.039348
+vt 0.955233 0.260973
+vt 0.015769 0.092595
+vt 0.634942 0.252270
+vt 0.709424 0.528492
+vt 0.674041 0.251333
+vt 0.637096 0.502534
+vt 0.535204 0.530002
+vt 0.273950 0.423995
+vt 0.635823 0.535625
+vt 0.359047 0.385937
+vt 0.553218 0.164920
+vt 0.192800 0.757975
+vt 0.634735 0.222563
+vt 0.535521 0.522712
+vt 0.329998 0.345864
+vt 0.029987 0.504457
+vt 0.004768 0.505706
+vt 0.299688 0.366041
+vt 0.595338 0.596712
+vt 0.023828 0.663170
+vt 0.537897 0.678838
+vt 0.961468 0.192683
+vt 0.971275 0.266931
+vt 0.304474 0.848948
+vt 0.031633 0.458764
+vt 0.747975 0.497125
+vt 0.283964 0.890144
+vt 0.960814 0.228346
+vt 0.361245 0.386645
+vt 0.349320 0.402955
+vt 0.330282 0.408849
+vt 0.039492 0.241423
+vt 0.536760 0.713602
+vt 0.442348 0.349999
+vt 0.102746 0.756393
+vt 0.016753 0.149163
+vt 0.214218 0.745926
+vt 0.750133 0.062884
+vt 0.609524 0.161420
+vt 0.014526 0.472387
+vt 0.977669 0.236466
+vt 0.030308 0.150596
+vt 0.297338 0.894235
+vt 0.019649 0.278496
+vt 0.546671 0.126797
+vt 0.015396 0.168157
+vt 0.212882 0.760696
+vt 0.787316 0.081761
+vt 0.012582 0.624053
+vt 0.774112 0.718076
+vt 0.727050 0.629840
+vt 0.099210 0.924604
+vt 0.284711 0.787354
+vt 0.984125 0.711960
+vt 0.983477 0.045252
+vt 0.709486 0.537653
+vt 0.706809 0.620714
+vt 0.775145 0.106442
+vt 0.316737 0.380021
+vt 0.539605 0.004563
+vt 0.696671 0.159777
+vt 0.434073 0.229407
+vt 0.392184 0.386776
+vt 0.748928 0.228219
+vt 0.330347 0.406689
+vt 0.774356 0.710607
+vt 0.539622 0.459478
+vt 0.070462 0.768085
+vt 0.433350 0.596419
+vt 0.017375 0.271882
+vt 0.988091 0.736164
+vt 0.038080 0.286727
+vt 0.956653 0.498984
+vt 0.643348 0.004427
+vt 0.567158 0.121473
+vt 0.004703 0.376145
+vt 0.536588 0.737505
+vt 0.490932 0.375868
+vt 0.675853 0.533596
+vt 0.636743 0.530371
+vt 0.590770 0.475309
+vt 0.538671 0.024090
+vt 0.538521 0.033513
+vt 0.043277 0.857867
+vt 0.749450 0.032688
+vt 0.645173 0.029445
+vt 0.754393 0.023111
+vt 0.589149 0.533687
+vt 0.013383 0.242884
+vt 0.779461 0.710223
+vt 0.531271 0.710635
+vt 0.587157 0.229693
+vt 0.438687 0.371517
+vt 0.674437 0.267925
+vt 0.655378 0.678955
+vt 0.732820 0.609916
+vt 0.709914 0.619362
+vt 0.756039 0.657320
+vt 0.736972 0.091197
+vt 0.261922 0.777755
+vt 0.428189 0.680875
+vt 0.774985 0.650207
+vt 0.566885 0.642223
+vt 0.265301 0.924613
+vt 0.534033 0.735766
+vt 0.403920 0.403362
+vt 0.534025 0.222534
+vt 0.344296 0.372255
+vt 0.423430 0.342895
+vt 0.751600 0.028401
+vt 0.674169 0.257466
+vt 0.655361 0.630077
+vt 0.040907 0.832261
+vt 0.588054 0.224282
+vt 0.954687 0.488290
+vt 0.536350 0.710608
+vt 0.635114 0.258340
+vt 0.434245 0.524422
+vt 0.455933 0.365409
+vt 0.098472 0.751315
+vt 0.034060 0.471008
+vt 0.971354 0.487374
+vt 0.550671 0.093325
+vt 0.277201 0.909329
+vt 0.025321 0.643014
+vt 0.709536 0.503914
+vt 0.984312 0.378254
+vt 0.979750 0.666962
+vt 0.978251 0.601641
+vt 0.709493 0.536905
+vt 0.751446 0.535548
+vt 0.751236 0.536631
+vt 0.746915 0.245359
+vt 0.970965 0.268624
+vt 0.708384 0.222919
+vt 0.549232 0.313231
+vt 0.055990 0.784833
+vt 0.691240 0.378056
+vt 0.009608 0.248066
+vt 0.587869 0.259631
+vt 0.329955 0.343701
+vt 0.432345 0.236884
+vt 0.012306 0.642800
+vt 0.013446 0.468400
+vt 0.962921 0.644716
+vt 0.761952 0.657429
+vt 0.956562 0.255253
+vt 0.682882 0.614869
+vt 0.655394 0.612791
+vt 0.038668 0.275454
+vt 0.154653 0.930696
+vt 0.603935 0.620730
+vt 0.971265 0.271920
+vt 0.980807 0.074201
+vt 0.008589 0.510309
+vt 0.423159 0.409961
+vt 0.979928 0.526074
+vt 0.746895 0.027032
+vt 0.639296 0.291549
+vt 0.707748 0.267567
+vt 0.655352 0.710416
+vt 0.302023 0.385460
+vt 0.978558 0.100001
+vt 0.707715 0.257157
+vt 0.709559 0.481150
+vt 0.678586 0.427749
+vt 0.538563 0.295323
+vt 0.589068 0.302785
+vt 0.321827 0.363518
+vt 0.749767 0.224348
+vt 0.004374 0.516796
+vt 0.537692 0.481776
+vt 0.707680 0.218088
+vt 0.540126 0.738139
+vt 0.095330 0.906145
+vt 0.051304 0.864865
+vt 0.748400 0.008421
+vt 0.749619 0.003632
+vt 0.748082 0.003099
+vt 0.538111 0.005071
+vt 0.541146 0.004472
+vt 0.677240 0.327978
+vt 0.531948 0.713658
+vt 0.655397 0.737485
+vt 0.673187 0.226015
+vt 0.521964 0.411128
+vt 0.979908 0.525604
+vt 0.423475 0.344711
+vt 0.749228 0.023240
+vt 0.274018 0.328282
+vt 0.802967 0.001424
+vt 0.290863 0.785753
+vt 0.311067 0.849306
+vt 0.772869 0.678776
+vt 0.774077 0.718536
+vt 0.526667 0.101677
+vt 0.249993 0.755988
+vt 0.971146 0.485923
+vt 0.779256 0.718195
+vt 0.531589 0.718580
+vt 0.954594 0.266047
+vt 0.433746 0.277285
+vt 0.409325 0.371161
+vt 0.009620 0.247452
+vt 0.330149 0.391516
+vt 0.339081 0.364414
+vt 0.956471 0.519081
+vt 0.025901 0.623647
+vt 0.116296 0.915735
+vt 0.309020 0.825194
+vt 0.008185 0.682350
+vt 0.036461 0.262598
+vt 0.955339 0.493327
+vt 0.676143 0.497530
+vt 0.009030 0.235849
+vt 0.709533 0.487456
+vt 0.535066 0.255380
+vt 0.348072 0.401160
+vt 0.531348 0.710226
+vt 0.636835 0.509223
+vt 0.034995 0.511718
+vt 0.301854 0.366717
+vt 0.536597 0.028305
+vt 0.029675 0.195353
+vt 0.643361 0.009756
+vt 0.536889 0.498065
+vt 0.655354 0.713178
+vt 0.405161 0.350797
+vt 0.590859 0.472850
+vt 0.433423 0.363566
+vt 0.299959 0.386171
+vt 0.972895 0.491371
+vt 0.008259 0.560156
+vt 0.250249 0.930884
+vt 0.297009 0.848558
+vt 0.978844 0.242936
+vt 0.956744 0.521765
+vt 0.432278 0.615510
+vt 0.028368 0.091511
+vt 0.428819 0.074182
+vt 0.300447 0.816374
+vt 0.590645 0.478125
+vt 0.675014 0.537098
+vt 0.588282 0.276082
+vt 0.779220 0.718583
+vt 0.016948 0.111647
+vt 0.525790 0.653731
+vt 0.278146 0.791181
+vt 0.746247 0.257677
+vt 0.533658 0.024110
+vt 0.011107 0.662066
+vt 0.434475 0.469057
+vt 0.033360 0.231697
+vt 0.584750 0.152850
+vt 0.544124 0.678517
+vt 0.503025 0.726987
+vt 0.134691 0.743314
+vt 0.971919 0.480712
+vt 0.361166 0.366144
+vt 0.718219 0.130605
+vt 0.536565 0.504705
+vt 0.633687 0.219826
+vt 0.588135 0.269890
+vt 0.641617 0.463663
+vt 0.640483 0.461614
+vt 0.629973 0.443284
+vt 0.423192 0.408130
+vt 0.645562 0.061918
+vt 0.540987 0.029754
+vt 0.047483 0.832403
+vt 0.645257 0.034371
+vt 0.056776 0.856726
+vt 0.749529 0.032224
+vt 0.754691 0.032153
+vt 0.538302 0.738043
+vt 0.744670 0.062316
+vt 0.533634 0.024500
+vt 0.012978 0.193447
+vt 0.174444 0.741716
+vt 0.544872 0.094017
+vt 0.644305 0.152511
+vt 0.676009 0.503630
+vt 0.248607 0.770773
+vt 0.684540 0.613164
+vt 0.538354 0.735708
+vt 0.803117 0.754596
+vt 0.216163 0.923612
+vt 0.509042 0.698525
+vt 0.534821 0.009833
+vt 0.589833 0.530107
+vt 0.655353 0.732167
+vt 0.977857 0.519571
+vt 0.579456 0.137911
+vt 0.231654 0.764947
+vt 0.034543 0.248596
+vt 0.533591 0.223650
+vt 0.432271 0.234785
+vt 0.434126 0.231462
+vt 0.688923 0.378114
+vt 0.708399 0.221031
+vt 0.034526 0.247939
+vt 0.292615 0.871304
+vt 0.971832 0.273596
+vt 0.799650 0.030402
+vt 0.425214 0.041700
+vt 0.670522 0.152200
+vt 0.695412 0.140669
+vt 0.707712 0.251038
+vt 0.707717 0.244226
+vt 0.676339 0.487134
+vt 0.432252 0.527666
+vt 0.650571 0.588307
+vt 0.311618 0.402432
+vt 0.422919 0.392337
+vt 0.979884 0.228177
+vt 0.535566 0.521602
+vt 0.588120 0.226046
+vt 0.674953 0.536432
+vt 0.628248 0.312708
+vt 0.745517 0.158086
+vt 0.637511 0.490851
+vt 0.954561 0.483546
+vt 0.977404 0.126169
+vt 0.766630 0.678460
+vt 0.020850 0.685237
+vt 0.954700 0.274866
+vt 0.116373 0.930626
+vt 0.778725 0.715144
+vt 0.434018 0.475875
+vt 0.969029 0.717057
+vt 0.504650 0.422273
+vt 0.414114 0.388961
+vt 0.536884 0.715094
+vt 0.658207 0.416637
+vt 0.749371 0.218152
+vt 0.961663 0.611119
+vt 0.707784 0.273832
+vt 0.591159 0.451477
+vt 0.589762 0.500619
+vt 0.033419 0.483409
+vt 0.302237 0.803799
+vt 0.349132 0.349732
+vt 0.502727 0.423468
+vt 0.404913 0.402032
+vt 0.393972 0.366687
+vt 0.359063 0.366870
+vt 0.580721 0.134282
+vt 0.432424 0.518965
+vt 0.029970 0.505122
+vt 0.666956 0.165275
+vt 0.130987 0.758053
+vt 0.033282 0.230988
+vt 0.165394 0.748950
+vt 0.959856 0.239506
+vt 0.673878 0.244528
+vt 0.978602 0.155059
+vt 0.194514 0.939639
+vt 0.432109 0.224126
+vt 0.248619 0.924770
+vt 0.214068 0.938346
+vt 0.743041 0.091680
+vt 0.707623 0.226473
+vt 0.672964 0.217825
+vt 0.408922 0.380917
+vt 0.013225 0.240276
+vt 0.637100 0.165993
+vt 0.019745 0.376348
+vt 0.537181 0.492060
+vt 0.501237 0.328565
+vt 0.971142 0.484002
+vt 0.025254 0.580845
+vt 0.046237 0.804550
+vt 0.799776 0.726015
+vt 0.019320 0.282406
+vt 0.747751 0.491741
+vt 0.747616 0.487298
+vt 0.751529 0.622943
+vt 0.965817 0.694833
+vt 0.775565 0.736773
+vt 0.977723 0.517740
+vt 0.535534 0.266906
+vt 0.536475 0.710112
+vt 0.708447 0.598419
+vt 0.511184 0.375053
+vt 0.588948 0.525537
+vt 0.134639 0.935257
+vt 0.232458 0.750383
+vt 0.977806 0.234635
+vt 0.587127 0.228828
+vt 0.774231 0.710110
+vt 0.033491 0.465502
+vt 0.564276 0.141784
+vt 0.012884 0.072516
+vt 0.961373 0.155913
+vt 0.433206 0.265388
+vt 0.587707 0.253630
+vt 0.667836 0.379009
+vt 0.673044 0.218479
+vt 0.393842 0.386229
+vt 0.673938 0.221318
+vt 0.978894 0.511274
+vt 0.134723 0.920201
+vt 0.533909 0.231992
+vt 0.587527 0.246988
+vt 0.424401 0.360512
+vt 0.779539 0.710633
+vt 0.773947 0.713601
+vt 0.431307 0.106060
+vt 0.019614 0.280387
+vt 0.548780 0.657506
+vt 0.288979 0.807196
+vt 0.031882 0.490231
+vt 0.962067 0.124384
+vt 0.234728 0.920862
+vt 0.627895 0.614871
+vt 0.443320 0.348593
+vt 0.537398 0.293340
+vt 0.432080 0.389619
+vt 0.424600 0.713567
+vt 0.534800 0.248802
+vt 0.723508 0.150293
+vt 0.535050 0.228106
+vt 0.707636 0.225824
+vt 0.772427 0.738055
+vt 0.312856 0.400772
+vt 0.675128 0.528262
+vt 0.029839 0.111471
+vt 0.028708 0.522273
+vt 0.433635 0.553350
+vt 0.014258 0.476311
+vt 0.954473 0.270805
+vt 0.283724 0.911294
+vt 0.273718 0.376163
+vt 0.668375 0.150233
+vt 0.715959 0.128100
+vt 0.680454 0.591088
+vt 0.055062 0.799398
+vt 0.050889 0.880505
+vt 0.754359 0.022717
+vt 0.754641 0.032550
+vt 0.533543 0.033869
+vt 0.538652 0.024553
+vt 0.541071 0.028294
+vt 0.330471 0.361139
+vt 0.747009 0.028476
+vt 0.432501 0.516834
+vt 0.116076 0.745878
+vt 0.588015 0.265250
+vt 0.586855 0.221581
+vt 0.778862 0.713659
+vt 0.956688 0.232390
+vt 0.488712 0.375927
+vt 0.503327 0.028036
+vt 0.321776 0.388098
+vt 0.636669 0.532149
+vt 0.773290 0.732766
+vt 0.748710 0.509329
+vt 0.709556 0.510690
+vt 0.034108 0.477084
+vt 0.634733 0.245542
+vt 0.025427 0.069662
+vt 0.978682 0.641721
+vt 0.731380 0.629037
+vt 0.749174 0.642084
+vt 0.709440 0.529141
+vt 0.674539 0.274234
+vt 0.621388 0.590325
+vt 0.019365 0.276651
+vt 0.776699 0.735852
+vt 0.637867 0.479935
+vt 0.520882 0.339321
+vt 0.750975 0.527204
+vt 0.645827 0.091805
+vt 0.646015 0.117769
+vt 0.971376 0.482292
+vt 0.434189 0.283992
+vt 0.590404 0.484365
+vt 0.959832 0.514154
+vt 0.675237 0.329210
+vt 0.979708 0.191026
+vt 0.437733 0.381368
+vt 0.675843 0.510408
+vt 0.503178 0.329675
+vt 0.025798 0.603493
+vt 0.794708 0.056991
+vt 0.012105 0.604851
+vt 0.017221 0.130184
+vt 0.747482 0.481345
+vt 0.955680 0.473929
+vt 0.745821 0.273495
+vt 0.536629 0.718536
+vt 0.232684 0.935844
+vt 0.185956 0.932429
+vt 0.134021 0.750309
+vt 0.014205 0.470309
+vt 0.770665 0.738140
+vt 0.589917 0.528339
+vt 0.710195 0.532052
+vt 0.434300 0.522334
+vt 0.532968 0.732810
+vt 0.217665 0.930945
+vt 0.579339 0.629079
+vt 0.037980 0.269265
+vt 0.588604 0.532806
+vt 0.030407 0.130646
+vt 0.967376 0.378295
+vt 0.635602 0.534880
+vt 0.433742 0.481481
+vt 0.432791 0.251722
+vt 0.551499 0.623682
+vt 0.675115 0.528897
+vt 0.415208 0.363401
+vt 0.655354 0.708970
+vt 0.673197 0.226652
+vt 0.534984 0.226345
+vt 0.533870 0.230887
+vt 0.774148 0.737529
+vt 0.537461 0.486426
+vt 0.655372 0.620846
+vt 0.710214 0.533946
+vt 0.075712 0.892473
+vt 0.455878 0.386811
+vt 0.635263 0.264024
+vt 0.709525 0.492136
+vt 0.535309 0.261328
+vt 0.540765 0.457546
+vt 0.638200 0.293545
+vt 0.004754 0.505085
+vt 0.536594 0.718076
+vt 0.066970 0.894065
+vt 0.539219 0.009858
+vt 0.064045 0.876558
+vt 0.535776 0.006830
+vt 0.540046 0.006873
+vt 0.752926 0.008342
+vt 0.751905 0.005321
+vt 0.747505 0.005422
+vt 0.643330 0.006812
+vt 0.750869 0.004295
+vt 0.978763 0.243402
+vt 0.748979 0.227388
+vt 0.551316 0.438743
+vt 0.655353 0.717380
+vt 0.516716 0.079815
+vt 0.637316 0.496500
+vt 0.515915 0.675884
+vt 0.750919 0.526369
+vt 0.746520 0.251844
+vt 0.154248 0.923133
+vt 0.194581 0.743421
+vt 0.962073 0.571203
+vt 0.038629 0.281388
+vt 0.295445 0.826673
+vt 0.534980 0.736642
+vt 0.434221 0.177364
+vt 0.537481 0.063633
+vt 0.979562 0.563043
+vt 0.959789 0.240026
+vt 0.074590 0.772179
+vt 0.018629 0.284195
+vt 0.962374 0.092665
+vt 0.433210 0.494645
+vt 0.258438 0.767007
+vt 0.727350 0.114562
+vt 0.697277 0.142739
+vt 0.597429 0.146563
+vt 0.433479 0.271736
+vt 0.635940 0.526832
+vt 0.344015 0.381758
+vt 0.590218 0.488996
+vt 0.589506 0.507278
+vt 0.442222 0.402540
+vt 0.538004 0.475527
+vt 0.588362 0.278896
+vt 0.316426 0.370673
+vt 0.746434 0.003017
+vt 0.275577 0.770394
+vt 0.646095 0.131027
+vt 0.676255 0.491827
+vt 0.773822 0.715094
+vt 0.296673 0.882054
+vt 0.777827 0.732811
+vt 0.786819 0.673874
+vt 0.433003 0.501783
+vt 0.763117 0.122406
+vt 0.123327 0.925350
+vt 0.743824 0.642148
+vt 0.600829 0.619378
+vt 0.655353 0.718823
+vt 0.588988 0.524669
+vt 0.009192 0.236470
+vt 0.154071 0.741716
+vt 0.772291 0.735789
+vt 0.587238 0.220708
+vt 0.978813 0.510808
+vt 0.561531 0.642164
+vt 0.093101 0.914476
+vt 0.271514 0.904463
+vt 0.175243 0.924813
+vt 0.988091 0.019862
+vt 0.745931 0.267570
+vt 0.600260 0.144983
+vt 0.619960 0.152558
+vt 0.621256 0.150570
+vt 0.749384 0.219039
+vt 0.311290 0.349875
+vt 0.536696 0.525523
+vt 0.589997 0.494618
+vt 0.635522 0.274981
+vt 0.533504 0.033479
+vt 0.538597 0.033956
+vt 0.543337 0.062375
+vt 0.749193 0.022772
+vt 0.733832 0.113928
+vt 0.751479 0.026980
+vt 0.644841 0.023120
+vt 0.645011 0.024559
+vt 0.645013 0.028037
+vt 0.535718 0.271520
+vt 0.956413 0.235079
+vt 0.433777 0.158150
+vt 0.054858 0.832802
+vt 0.150736 0.756501
+vt 0.655325 0.735104
+vt 0.745790 0.137738
+vt 0.633948 0.227137
+vt 0.008436 0.512945
+vt 0.633970 0.227876
+vt 0.312513 0.351535
+vt 0.955625 0.280389
+vt 0.751786 0.530237
+vt 0.960831 0.526445
+vt 0.588393 0.281351
+vt 0.034756 0.515254
+vt 0.979851 0.228650
+vt 0.174079 0.939853
+vt 0.583671 0.629880
+vt 0.954771 0.479510
+vt 0.010724 0.585680
+vt 0.430716 0.648801
+vt 0.971613 0.739967
+vt 0.306608 0.873583
+vt 0.012346 0.481019
+vt 0.626238 0.613166
+vt 0.709525 0.497824
+vt 0.404167 0.349459
+vt 0.960824 0.227823
+vt 0.673972 0.223152
+vt 0.655367 0.657822
+vt 0.432992 0.258751
+vt 0.971547 0.015732
+vt 0.228014 0.756387
+vt 0.083275 0.774579
+vt 0.060100 0.807273
+vt 0.025131 0.558293
+vt 0.963614 0.073193
+vt 0.154068 0.937899
+vt 0.972835 0.262921
+vt 0.674278 0.263197
+vt 0.707728 0.262864
+vt 0.749885 0.222152
+vt 0.635912 0.527572
+vt 0.707545 0.217380
+vt 0.646161 0.142743
+vt 0.338718 0.389165
+vt 0.675893 0.531763
+vt 0.959900 0.514674
+vt 0.531552 0.718192
+vt 0.635378 0.268715
+vt 0.443290 0.404005
+vt 0.036256 0.293301
+vt 0.028786 0.521557
+vt 0.963982 0.673742
+vt 0.433455 0.487915
+vn -0.521090 0.772906 0.362053
+vn -0.285351 0.888921 0.358321
+vn -0.567682 0.823248 0.000000
+vn -0.311600 0.950213 0.000000
+vn 0.372764 -0.088140 -0.923731
+vn 0.321855 0.605804 -0.727606
+vn 0.913199 -0.133217 -0.385123
+vn 0.747977 0.586436 -0.310843
+vn -0.992997 0.118139 0.000000
+vn -0.992990 0.118199 0.000000
+vn -0.939966 0.341268 0.000000
+vn -0.939891 0.341476 0.000000
+vn -0.782058 0.623205 0.000000
+vn -0.569305 0.822126 0.000000
+vn -0.782234 0.622985 0.000000
+vn -0.569521 0.821977 0.000000
+vn -0.870160 0.331315 0.364764
+vn -0.720412 0.591619 0.361930
+vn -0.939332 0.343010 0.000000
+vn -0.780839 0.624732 0.000000
+vn -0.984672 -0.174417 0.000336
+vn -0.952300 -0.305104 0.005982
+vn -0.902001 -0.203565 0.380730
+vn -0.857846 -0.346612 0.379420
+vn -0.250746 -0.957540 -0.142281
+vn -0.297408 -0.954362 0.027223
+vn -0.513463 -0.830318 -0.216629
+vn -0.581935 -0.812903 0.023255
+vn -0.778583 -0.505372 0.372032
+vn -0.857846 -0.346612 0.379420
+vn -0.886617 -0.462368 0.011231
+vn -0.952300 -0.305104 0.005982
+vn -0.660766 -0.650298 0.374834
+vn -0.778583 -0.505372 0.372032
+vn -0.788745 -0.614540 0.014893
+vn -0.886617 -0.462368 0.011231
+vn 0.000000 -0.925801 0.378011
+vn -0.000855 -0.999608 0.027986
+vn 0.238233 -0.894792 0.377614
+vn 0.296405 -0.954678 0.027101
+vn 0.296405 -0.954678 0.027101
+vn -0.000855 -0.999608 0.027986
+vn 0.231212 -0.957992 -0.169686
+vn 0.004517 -0.992742 -0.120182
+vn -0.238259 -0.894775 0.377637
+vn -0.471889 -0.796857 0.377279
+vn -0.297408 -0.954362 0.027223
+vn -0.581935 -0.812903 0.023255
+vn 0.581567 -0.813174 0.022981
+vn 0.296405 -0.954678 0.027101
+vn 0.511229 -0.844469 -0.159738
+vn 0.231212 -0.957992 -0.169686
+vn 0.238233 -0.894792 0.377614
+vn 0.296405 -0.954678 0.027101
+vn 0.471859 -0.796889 0.377249
+vn 0.581567 -0.813174 0.022981
+vn 0.007019 -0.844861 -0.534939
+vn 0.004517 -0.992742 -0.120182
+vn -0.250746 -0.957540 -0.142281
+vn 0.241984 -0.835699 -0.493002
+vn 0.231212 -0.957992 -0.169686
+vn 0.004517 -0.992742 -0.120182
+vn 0.007019 -0.844861 -0.534939
+vn 0.241984 -0.835699 -0.493002
+vn 0.004517 -0.992742 -0.120182
+vn -0.243419 -0.829015 -0.503470
+vn -0.250746 -0.957540 -0.142281
+vn -0.513463 -0.830318 -0.216629
+vn -0.243419 -0.829015 -0.503470
+vn 0.007019 -0.844861 -0.534939
+vn -0.250746 -0.957540 -0.142281
+vn -0.243419 -0.829015 -0.503470
+vn -0.513463 -0.830318 -0.216629
+vn -0.710895 -0.695605 -0.103736
+vn -0.460262 -0.773604 -0.435541
+vn -0.710895 -0.695605 -0.103736
+vn -0.849339 -0.521933 -0.078800
+vn -0.243419 -0.829015 -0.503470
+vn -0.710895 -0.695605 -0.103736
+vn -0.460262 -0.773604 -0.435541
+vn -0.604308 -0.711613 -0.358355
+vn -0.849339 -0.521933 -0.078800
+vn -0.913458 -0.399399 -0.077945
+vn -0.460262 -0.773604 -0.435541
+vn -0.849339 -0.521933 -0.078800
+vn -0.604308 -0.711613 -0.358355
+vn -0.639161 -0.723760 -0.260084
+vn -0.913458 -0.399399 -0.077945
+vn -0.939019 -0.338459 -0.060733
+vn -0.604308 -0.711613 -0.358355
+vn -0.913458 -0.399399 -0.077945
+vn -0.639161 -0.723760 -0.260084
+vn -0.639161 -0.723760 -0.260084
+vn -0.939019 -0.338459 -0.060733
+vn -0.949451 -0.312272 -0.032076
+vn -0.648624 -0.756632 -0.082432
+vn -0.949451 -0.312272 -0.032076
+vn -0.954279 -0.298907 0.002472
+vn -0.639161 -0.723760 -0.260084
+vn -0.949451 -0.312272 -0.032076
+vn -0.648624 -0.756632 -0.082432
+vn 0.028932 0.428152 -0.903243
+vn 0.000000 0.418594 -0.908174
+vn 0.068943 0.929284 -0.362875
+vn 0.000000 0.928819 -0.370533
+vn -0.648624 -0.756632 -0.082432
+vn -0.954279 -0.298907 0.002472
+vn -0.951202 -0.308125 0.016572
+vn -0.647672 -0.752810 0.117467
+vn -0.951202 -0.308125 0.016572
+vn -0.949874 -0.309738 0.042452
+vn -0.859060 -0.359823 -0.364066
+vn -0.899920 -0.425957 0.093297
+vn -0.909937 -0.184063 -0.371666
+vn -0.939317 -0.333574 0.080082
+vn -0.648624 -0.756632 -0.082432
+vn -0.951202 -0.308125 0.016572
+vn -0.647672 -0.752810 0.117467
+vn -0.638581 -0.710667 0.295241
+vn -0.949874 -0.309738 0.042452
+vn -0.939317 -0.333574 0.080082
+vn -0.647672 -0.752810 0.117467
+vn -0.949874 -0.309738 0.042452
+vn -0.638581 -0.710667 0.295241
+vn -0.758811 -0.548713 -0.350885
+vn -0.782121 -0.606665 0.142282
+vn -0.859060 -0.359823 -0.364066
+vn -0.899920 -0.425957 0.093297
+vn -0.638581 -0.710667 0.295241
+vn -0.939317 -0.333574 0.080082
+vn -0.899920 -0.425957 0.093297
+vn -0.609223 -0.680913 0.406454
+vn -0.899920 -0.425957 0.093297
+vn -0.782121 -0.606665 0.142282
+vn -0.638581 -0.710667 0.295241
+vn -0.899920 -0.425957 0.093297
+vn -0.609223 -0.680913 0.406454
+vn -0.467923 -0.734358 0.491698
+vn -0.782121 -0.606665 0.142282
+vn -0.633088 -0.762062 0.135871
+vn -0.609223 -0.680913 0.406454
+vn -0.782121 -0.606665 0.142282
+vn -0.467923 -0.734358 0.491698
+vn -0.584690 -0.736127 -0.340962
+vn -0.633088 -0.762062 0.135871
+vn -0.758811 -0.548713 -0.350885
+vn -0.782121 -0.606665 0.142282
+vn -0.467923 -0.734358 0.491698
+vn -0.633088 -0.762062 0.135871
+vn -0.429001 -0.879885 0.204353
+vn -0.429001 -0.879885 0.204353
+vn -0.382251 -0.857281 -0.344896
+vn -0.200814 -0.956675 0.210824
+vn -0.191601 -0.913507 -0.358878
+vn -0.237198 -0.806417 0.541691
+vn -0.429001 -0.879885 0.204353
+vn -0.200814 -0.956675 0.210824
+vn -0.467923 -0.734358 0.491698
+vn -0.429001 -0.879885 0.204353
+vn -0.237198 -0.806417 0.541691
+vn 0.000702 -0.938897 -0.344197
+vn -0.001221 -0.985450 0.169959
+vn -0.191601 -0.913507 -0.358878
+vn -0.200814 -0.956675 0.210824
+vn -0.001221 -0.985450 0.169959
+vn 0.000275 -0.811299 0.584631
+vn -0.200814 -0.956675 0.210824
+vn 0.000702 -0.938897 -0.344197
+vn -0.191601 -0.913507 -0.358878
+vn 0.002747 -0.441857 -0.897081
+vn -0.087345 -0.430315 -0.898443
+vn 0.212324 -0.913234 -0.347739
+vn 0.439571 -0.822101 -0.361838
+vn 0.209422 -0.955035 0.209880
+vn 0.453122 -0.859397 0.236892
+vn 0.000275 -0.811299 0.584631
+vn -0.001221 -0.985450 0.169959
+vn 0.209422 -0.955035 0.209880
+vn -0.277025 -0.360404 -0.890711
+vn -0.171580 -0.405969 -0.897636
+vn -0.584690 -0.736127 -0.340962
+vn -0.382251 -0.857281 -0.344896
+vn 0.106113 -0.434098 -0.894594
+vn 0.212324 -0.913234 -0.347739
+vn 0.002747 -0.441857 -0.897081
+vn 0.000702 -0.938897 -0.344197
+vn -0.342519 -0.277268 -0.897666
+vn -0.277025 -0.360404 -0.890711
+vn -0.758811 -0.548713 -0.350885
+vn -0.584690 -0.736127 -0.340962
+vn -0.386469 -0.166759 -0.907102
+vn -0.342519 -0.277268 -0.897666
+vn -0.859060 -0.359823 -0.364066
+vn -0.758811 -0.548713 -0.350885
+vn -0.991137 -0.132636 -0.007416
+vn -0.734553 0.678551 -0.000092
+vn -0.991018 -0.133704 -0.002472
+vn -0.734685 0.678408 0.000000
+vn 0.319082 -0.282245 0.904723
+vn 0.000000 -0.052339 0.998629
+vn 0.253949 -0.348223 0.902358
+vn 0.000000 -0.052402 0.998626
+vn 0.311907 -0.598482 -0.737925
+vn 0.739459 -0.596169 -0.312703
+vn 0.233778 -0.773910 -0.588566
+vn 0.590148 -0.771523 -0.237652
+vn -0.087286 -0.425991 0.900507
+vn 0.000000 -0.434743 0.900555
+vn 0.000000 -0.052096 0.998642
+vn 0.000000 -0.052157 0.998639
+vn 0.377487 -0.139135 0.915503
+vn 0.000000 -0.052309 0.998631
+vn 0.352987 -0.211408 0.911431
+vn 0.000000 -0.052309 0.998631
+vn -0.254165 -0.348043 0.902367
+vn -0.174140 -0.397386 0.900977
+vn 0.000031 -0.052432 0.998624
+vn -0.000031 -0.052432 0.998624
+vn -0.319323 -0.281907 0.904744
+vn -0.353195 -0.211129 0.911415
+vn -0.778583 -0.505372 0.372032
+vn -0.857846 -0.346612 0.379420
+vn 0.253949 -0.348223 0.902358
+vn 0.660743 -0.650336 0.374808
+vn 0.319082 -0.282245 0.904723
+vn 0.778579 -0.505401 0.372000
+vn -0.353195 -0.211129 0.911415
+vn -0.377613 -0.138923 0.915483
+vn -0.857846 -0.346612 0.379420
+vn -0.902001 -0.203565 0.380730
+vn 0.000000 -0.434743 0.900555
+vn -0.087286 -0.425991 0.900507
+vn 0.000000 -0.925801 0.378011
+vn -0.238259 -0.894775 0.377637
+vn -0.174140 -0.397386 0.900977
+vn -0.254165 -0.348043 0.902367
+vn -0.471889 -0.796857 0.377279
+vn -0.660766 -0.650298 0.374834
+vn 0.238233 -0.894792 0.377614
+vn 0.087254 -0.426017 0.900498
+vn 0.000000 -0.925801 0.378011
+vn 0.000000 -0.434743 0.900555
+vn -0.254165 -0.348043 0.902367
+vn -0.319323 -0.281907 0.904744
+vn -0.660766 -0.650298 0.374834
+vn -0.778583 -0.505372 0.372032
+vn -0.919299 0.124793 0.373250
+vn -0.395796 0.091006 0.913818
+vn -0.870160 0.331315 0.364764
+vn -0.392471 0.192390 0.899418
+vn 0.396049 0.205456 0.894948
+vn 0.396174 0.091223 0.913633
+vn 0.867846 0.335834 0.366140
+vn 0.919256 0.124211 0.373550
+vn -0.312362 0.298720 0.901774
+vn -0.720412 0.591619 0.361930
+vn -0.392471 0.192390 0.899418
+vn -0.870160 0.331315 0.364764
+vn -0.992942 0.118598 0.000000
+vn -0.919299 0.124793 0.373250
+vn -0.939332 0.343010 0.000000
+vn -0.870160 0.331315 0.364764
+vn -0.285351 0.888921 0.358321
+vn -0.122289 0.433856 0.892645
+vn -0.069248 0.929275 0.362841
+vn -0.029177 0.427853 0.903377
+vn -0.782587 0.622541 0.000000
+vn -0.569973 0.821663 0.000000
+vn -0.722183 0.589362 -0.362083
+vn -0.523403 0.771371 -0.361988
+vn -0.313581 0.949562 0.000000
+vn -0.079471 0.996837 0.000000
+vn -0.287517 0.888248 -0.358259
+vn -0.070103 0.929281 -0.362660
+vn -0.122289 0.433856 0.892645
+vn -0.285351 0.888921 0.358321
+vn -0.224988 0.373677 0.899859
+vn -0.521090 0.772906 0.362053
+vn -0.569973 0.821663 0.000000
+vn -0.313581 0.949562 0.000000
+vn -0.523403 0.771371 -0.361988
+vn -0.287517 0.888248 -0.358259
+vn -0.939966 0.341268 0.000000
+vn -0.782587 0.622541 0.000000
+vn -0.870643 0.329573 -0.365189
+vn -0.722183 0.589362 -0.362083
+vn -0.285351 0.888921 0.358321
+vn -0.069248 0.929275 0.362841
+vn -0.311600 0.950213 0.000000
+vn -0.078738 0.996895 0.000000
+vn -0.224988 0.373677 0.899859
+vn -0.521090 0.772906 0.362053
+vn -0.312362 0.298720 0.901774
+vn -0.720412 0.591619 0.361930
+vn -0.312152 0.299273 -0.901663
+vn -0.393298 0.193094 -0.898906
+vn -0.722183 0.589362 -0.362083
+vn -0.870643 0.329573 -0.365189
+vn -0.720412 0.591619 0.361930
+vn -0.521090 0.772906 0.362053
+vn -0.780839 0.624732 0.000000
+vn -0.567682 0.823248 0.000000
+vn -0.224897 0.374747 -0.899436
+vn -0.312152 0.299273 -0.901663
+vn -0.523403 0.771371 -0.361988
+vn -0.722183 0.589362 -0.362083
+vn -0.121224 0.433042 -0.893185
+vn -0.224897 0.374747 -0.899436
+vn -0.287517 0.888248 -0.358259
+vn -0.523403 0.771371 -0.361988
+vn -0.029329 0.427604 -0.903490
+vn -0.121224 0.433042 -0.893185
+vn -0.070103 0.929281 -0.362660
+vn -0.287517 0.888248 -0.358259
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992997 0.118139 0.000000
+vn -0.992990 0.118199 0.000000
+vn 0.000000 0.052309 -0.998631
+vn 0.000000 0.048190 -0.998838
+vn -0.393298 0.193094 -0.898906
+vn -0.396046 0.091008 -0.913710
+vn -0.734964 0.678106 0.000061
+vn -0.670866 0.741578 0.000000
+vn -0.730909 0.682475 0.000000
+vn -0.670866 0.741578 0.000000
+vn 0.029085 0.427176 0.903701
+vn 0.121893 0.433583 0.892832
+vn 0.069399 0.929325 0.362684
+vn 0.286943 0.888480 0.358145
+vn 0.720131 0.591401 0.362843
+vn 0.527037 0.768230 0.363393
+vn 0.320333 0.310109 0.895108
+vn 0.235030 0.375908 0.896356
+vn 0.121893 0.433583 0.892832
+vn 0.235030 0.375908 0.896356
+vn 0.286943 0.888480 0.358145
+vn 0.527037 0.768230 0.363393
+vn 0.939143 0.343526 0.000000
+vn 0.993040 0.117774 0.000000
+vn 0.868458 0.334950 -0.365499
+vn 0.919351 0.124242 -0.373307
+vn 0.310045 -0.595674 0.740976
+vn 0.000000 -0.675817 0.737069
+vn 0.263930 -0.785352 0.559967
+vn 0.000000 -0.675849 0.737040
+vn 0.568602 0.822613 0.000000
+vn 0.780351 0.625342 0.000000
+vn 0.524598 0.770401 -0.362326
+vn 0.719155 0.592775 -0.362538
+vn 0.780351 0.625342 0.000000
+vn 0.939143 0.343526 0.000000
+vn 0.719155 0.592775 -0.362538
+vn 0.868458 0.334950 -0.365499
+vn 0.867846 0.335834 0.366140
+vn 0.720131 0.591401 0.362843
+vn 0.396049 0.205456 0.894948
+vn 0.320333 0.310109 0.895108
+vn 0.311536 0.950234 0.000000
+vn 0.568602 0.822613 0.000000
+vn 0.286295 0.888581 -0.358411
+vn 0.524598 0.770401 -0.362326
+vn 0.078190 0.996939 0.000000
+vn 0.311536 0.950234 0.000000
+vn 0.068943 0.929284 -0.362875
+vn 0.286295 0.888581 -0.358411
+vn 0.325795 0.316060 -0.891046
+vn 0.719155 0.592775 -0.362538
+vn 0.392297 0.200055 -0.897820
+vn 0.868458 0.334950 -0.365499
+vn -0.381763 0.058475 0.922409
+vn 0.000000 0.044008 0.999031
+vn -0.395796 0.091006 0.913818
+vn 0.000000 0.048434 0.998826
+vn 0.939246 0.343245 0.000000
+vn 0.993048 0.117711 0.000000
+vn 0.939213 0.343335 0.000000
+vn 0.993044 0.117741 0.000000
+vn -0.353195 -0.211129 0.911415
+vn -0.319323 -0.281907 0.904744
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052279 0.998633
+vn 0.239788 -0.804483 0.543423
+vn 0.209422 -0.955035 0.209880
+vn 0.453122 -0.859397 0.236892
+vn 0.239788 -0.804483 0.543423
+vn 0.000275 -0.811299 0.584631
+vn 0.209422 -0.955035 0.209880
+vn 0.640223 -0.679501 -0.358321
+vn 0.802405 -0.482688 -0.350938
+vn 0.640007 -0.753384 0.151006
+vn 0.797224 -0.586336 0.143685
+vn 0.239788 -0.804483 0.543423
+vn 0.453122 -0.859397 0.236892
+vn 0.640007 -0.753384 0.151006
+vn 0.466575 -0.738743 0.486381
+vn 0.640007 -0.753384 0.151006
+vn 0.797224 -0.586336 0.143685
+vn 0.466575 -0.738743 0.486381
+vn 0.239788 -0.804483 0.543423
+vn 0.640007 -0.753384 0.151006
+vn 0.596342 -0.683993 0.420155
+vn 0.797224 -0.586336 0.143685
+vn 0.897571 -0.428521 0.103613
+vn 0.596342 -0.683993 0.420155
+vn 0.466575 -0.738743 0.486381
+vn 0.797224 -0.586336 0.143685
+vn 0.366104 -0.154792 -0.917610
+vn 0.877705 -0.298509 -0.374869
+vn 0.374497 -0.244701 -0.894356
+vn 0.802405 -0.482688 -0.350938
+vn 0.596342 -0.683993 0.420155
+vn 0.897571 -0.428521 0.103613
+vn 0.934625 -0.355612 0.004151
+vn 0.897571 -0.428521 0.103613
+vn 0.797224 -0.586336 0.143685
+vn 0.877705 -0.298509 -0.374869
+vn 0.802405 -0.482688 -0.350938
+vn 0.638345 -0.713270 0.289416
+vn 0.934625 -0.355612 0.004151
+vn 0.949811 -0.310561 0.037569
+vn 0.638345 -0.713270 0.289416
+vn 0.596342 -0.683993 0.420155
+vn 0.934625 -0.355612 0.004151
+vn 0.638345 -0.713270 0.289416
+vn 0.949811 -0.310561 0.037569
+vn 0.950440 -0.308980 0.034579
+vn 0.985937 -0.163336 -0.035341
+vn 0.897571 -0.428521 0.103613
+vn 0.904830 -0.170541 -0.390126
+vn 0.877705 -0.298509 -0.374869
+vn 0.650787 -0.750218 0.116827
+vn 0.950440 -0.308980 0.034579
+vn 0.957065 -0.289866 0.002228
+vn 0.650787 -0.750218 0.116827
+vn 0.638345 -0.713270 0.289416
+vn 0.950440 -0.308980 0.034579
+vn 0.650787 -0.750218 0.116827
+vn 0.957065 -0.289866 0.002228
+vn 0.949985 -0.312053 -0.012299
+vn 0.649294 -0.756172 -0.081364
+vn 0.949985 -0.312053 -0.012299
+vn 0.946362 -0.321059 -0.036318
+vn 0.649294 -0.756172 -0.081364
+vn 0.650787 -0.750218 0.116827
+vn 0.949985 -0.312053 -0.012299
+vn 0.912059 -0.149299 0.381914
+vn 0.989641 -0.143562 -0.000641
+vn 0.750260 0.586248 0.305652
+vn 0.738513 0.674239 0.000000
+vn 0.637546 -0.726174 -0.257307
+vn 0.946362 -0.321059 -0.036318
+vn 0.922100 -0.382311 -0.059756
+vn 0.637546 -0.726174 -0.257307
+vn 0.649294 -0.756172 -0.081364
+vn 0.946362 -0.321059 -0.036318
+vn 0.946362 -0.321059 -0.036318
+vn 0.981702 -0.190316 0.006439
+vn 0.922100 -0.382311 -0.059756
+vn 0.637546 -0.726174 -0.257307
+vn 0.922100 -0.382311 -0.059756
+vn 0.898822 -0.432030 -0.073948
+vn 0.898822 -0.432030 -0.073948
+vn 0.951306 -0.308211 0.004853
+vn 0.834906 -0.542718 -0.091587
+vn 0.884960 -0.465507 0.012208
+vn 0.602210 -0.707044 -0.370720
+vn 0.898822 -0.432030 -0.073948
+vn 0.834906 -0.542718 -0.091587
+vn 0.602210 -0.707044 -0.370720
+vn 0.637546 -0.726174 -0.257307
+vn 0.898822 -0.432030 -0.073948
+vn 0.834906 -0.542718 -0.091587
+vn 0.884960 -0.465507 0.012208
+vn 0.726236 -0.680854 -0.094976
+vn 0.789045 -0.614139 0.015504
+vn 0.602210 -0.707044 -0.370720
+vn 0.834906 -0.542718 -0.091587
+vn 0.726236 -0.680854 -0.094976
+vn 0.789045 -0.614139 0.015504
+vn 0.581567 -0.813174 0.022981
+vn 0.726236 -0.680854 -0.094976
+vn 0.511229 -0.844469 -0.159738
+vn 0.461423 -0.761703 -0.454861
+vn 0.726236 -0.680854 -0.094976
+vn 0.511229 -0.844469 -0.159738
+vn 0.461423 -0.761703 -0.454861
+vn 0.602210 -0.707044 -0.370720
+vn 0.726236 -0.680854 -0.094976
+vn -0.513463 -0.830318 -0.216629
+vn -0.581935 -0.812903 0.023255
+vn -0.710895 -0.695605 -0.103736
+vn -0.788745 -0.614540 0.014893
+vn 0.241984 -0.835699 -0.493002
+vn 0.511229 -0.844469 -0.159738
+vn 0.231212 -0.957992 -0.169686
+vn 0.241984 -0.835699 -0.493002
+vn 0.461423 -0.761703 -0.454861
+vn 0.511229 -0.844469 -0.159738
+vn 0.471859 -0.796889 0.377249
+vn 0.581567 -0.813174 0.022981
+vn 0.660743 -0.650336 0.374808
+vn 0.789045 -0.614139 0.015504
+vn -0.471889 -0.796857 0.377279
+vn -0.660766 -0.650298 0.374834
+vn -0.581935 -0.812903 0.023255
+vn -0.788745 -0.614540 0.014893
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.171580 -0.405969 -0.897636
+vn -0.087345 -0.430315 -0.898443
+vn -0.382251 -0.857281 -0.344896
+vn -0.191601 -0.913507 -0.358878
+vn 0.087254 -0.426017 0.900498
+vn 0.238233 -0.894792 0.377614
+vn 0.173990 -0.397360 0.901018
+vn 0.471859 -0.796889 0.377249
+vn 0.173990 -0.397360 0.901018
+vn 0.471859 -0.796889 0.377249
+vn 0.253949 -0.348223 0.902358
+vn 0.660743 -0.650336 0.374808
+vn 0.319082 -0.282245 0.904723
+vn 0.778579 -0.505401 0.372000
+vn 0.352987 -0.211408 0.911431
+vn 0.857781 -0.346641 0.379541
+vn 0.901970 -0.203534 0.380821
+vn 0.377487 -0.139135 0.915503
+vn 0.857781 -0.346641 0.379541
+vn 0.352987 -0.211408 0.911431
+vn 0.375234 -0.102697 0.921223
+vn 0.912059 -0.149299 0.381914
+vn 0.329149 0.602020 0.727484
+vn 0.750260 0.586248 0.305652
+vn 0.122871 0.434167 -0.892414
+vn 0.286295 0.888581 -0.358411
+vn 0.228679 0.372332 -0.899486
+vn 0.524598 0.770401 -0.362326
+vn 0.028932 0.428152 -0.903243
+vn 0.068943 0.929284 -0.362875
+vn 0.122871 0.434167 -0.892414
+vn 0.286295 0.888581 -0.358411
+vn -0.048831 -0.536314 -0.842605
+vn 0.048037 -0.537777 -0.841717
+vn -0.000397 -0.487642 -0.873044
+vn 0.241984 -0.835699 -0.493002
+vn 0.048037 -0.537777 -0.841717
+vn 0.120276 -0.592529 -0.796519
+vn 0.007019 -0.844861 -0.534939
+vn 0.048037 -0.537777 -0.841717
+vn 0.241984 -0.835699 -0.493002
+vn -0.127296 -0.607792 0.783827
+vn -0.114081 -0.538145 0.835096
+vn 0.133795 -0.607355 0.783083
+vn 0.113316 -0.537375 0.835696
+vn 0.461423 -0.761703 -0.454861
+vn 0.120276 -0.592529 -0.796519
+vn 0.138436 -0.667855 -0.731304
+vn 0.241984 -0.835699 -0.493002
+vn 0.120276 -0.592529 -0.796519
+vn 0.461423 -0.761703 -0.454861
+vn -0.171456 -0.865459 -0.470726
+vn 0.147194 -0.854141 -0.498775
+vn -0.176492 -0.781012 -0.599058
+vn 0.183848 -0.767314 -0.614352
+vn 0.602210 -0.707044 -0.370720
+vn 0.138436 -0.667855 -0.731304
+vn 0.183848 -0.767314 -0.614352
+vn 0.461423 -0.761703 -0.454861
+vn 0.138436 -0.667855 -0.731304
+vn 0.602210 -0.707044 -0.370720
+vn -0.154304 -0.850013 0.503654
+vn 0.165595 -0.881167 0.442857
+vn -0.123083 -0.939557 0.319503
+vn 0.105839 -0.942204 0.317883
+vn 0.602210 -0.707044 -0.370720
+vn 0.183848 -0.767314 -0.614352
+vn 0.147194 -0.854141 -0.498775
+vn -0.123083 -0.939557 0.319503
+vn 0.105839 -0.942204 0.317883
+vn -0.165385 -0.979921 0.111366
+vn 0.128944 -0.988856 0.074406
+vn 0.637546 -0.726174 -0.257307
+vn 0.147194 -0.854141 -0.498775
+vn 0.095371 -0.953529 -0.285809
+vn 0.602210 -0.707044 -0.370720
+vn 0.147194 -0.854141 -0.498775
+vn 0.637546 -0.726174 -0.257307
+vn -0.121832 -0.784555 0.607972
+vn 0.125158 -0.786652 0.604577
+vn -0.154304 -0.850013 0.503654
+vn 0.165595 -0.881167 0.442857
+vn 0.637546 -0.726174 -0.257307
+vn 0.095371 -0.953529 -0.285809
+vn 0.166633 -0.977095 -0.132361
+vn -0.127296 -0.607792 0.783827
+vn 0.133795 -0.607355 0.783083
+vn -0.157268 -0.676224 0.719714
+vn 0.172404 -0.693613 0.699412
+vn 0.649294 -0.756172 -0.081364
+vn 0.166633 -0.977095 -0.132361
+vn 0.128944 -0.988856 0.074406
+vn 0.637546 -0.726174 -0.257307
+vn 0.166633 -0.977095 -0.132361
+vn 0.649294 -0.756172 -0.081364
+vn -0.140114 -0.669504 -0.729474
+vn 0.138436 -0.667855 -0.731304
+vn -0.119057 -0.591407 -0.797536
+vn 0.120276 -0.592529 -0.796519
+vn 0.650787 -0.750218 0.116827
+vn 0.128944 -0.988856 0.074406
+vn 0.105839 -0.942204 0.317883
+vn 0.649294 -0.756172 -0.081364
+vn 0.128944 -0.988856 0.074406
+vn 0.650787 -0.750218 0.116827
+vn -0.165385 -0.979921 0.111366
+vn 0.128944 -0.988856 0.074406
+vn -0.145208 -0.987646 -0.058901
+vn 0.166633 -0.977095 -0.132361
+vn 0.650787 -0.750218 0.116827
+vn 0.105839 -0.942204 0.317883
+vn 0.165595 -0.881167 0.442857
+vn -0.123999 -0.951033 -0.283125
+vn 0.095371 -0.953529 -0.285809
+vn -0.171456 -0.865459 -0.470726
+vn 0.147194 -0.854141 -0.498775
+vn 0.638345 -0.713270 0.289416
+vn 0.165595 -0.881167 0.442857
+vn 0.125158 -0.786652 0.604577
+vn 0.650787 -0.750218 0.116827
+vn 0.165595 -0.881167 0.442857
+vn 0.638345 -0.713270 0.289416
+vn 0.596342 -0.683993 0.420155
+vn 0.125158 -0.786652 0.604577
+vn 0.172404 -0.693613 0.699412
+vn 0.638345 -0.713270 0.289416
+vn 0.125158 -0.786652 0.604577
+vn 0.596342 -0.683993 0.420155
+vn -0.145208 -0.987646 -0.058901
+vn 0.166633 -0.977095 -0.132361
+vn -0.123999 -0.951033 -0.283125
+vn 0.095371 -0.953529 -0.285809
+vn 0.596342 -0.683993 0.420155
+vn 0.172404 -0.693613 0.699412
+vn 0.133795 -0.607355 0.783083
+vn -0.114081 -0.538145 0.835096
+vn -0.046175 -0.491786 0.869491
+vn 0.113316 -0.537375 0.835696
+vn 0.046267 -0.491606 0.869588
+vn 0.466575 -0.738743 0.486381
+vn 0.133795 -0.607355 0.783083
+vn 0.113316 -0.537375 0.835696
+vn 0.596342 -0.683993 0.420155
+vn 0.133795 -0.607355 0.783083
+vn 0.466575 -0.738743 0.486381
+vn 0.239788 -0.804483 0.543423
+vn 0.113316 -0.537375 0.835696
+vn 0.046267 -0.491606 0.869588
+vn 0.466575 -0.738743 0.486381
+vn 0.113316 -0.537375 0.835696
+vn 0.239788 -0.804483 0.543423
+vn -0.046175 -0.491786 0.869491
+vn 0.000031 -0.438709 0.898629
+vn 0.046267 -0.491606 0.869588
+vn 0.239788 -0.804483 0.543423
+vn 0.046267 -0.491606 0.869588
+vn 0.000275 -0.811299 0.584631
+vn -0.237198 -0.806417 0.541691
+vn -0.046175 -0.491786 0.869491
+vn -0.114081 -0.538145 0.835096
+vn 0.000275 -0.811299 0.584631
+vn -0.237198 -0.806417 0.541691
+vn -0.200814 -0.956675 0.210824
+vn -0.237198 -0.806417 0.541691
+vn 0.000275 -0.811299 0.584631
+vn -0.046175 -0.491786 0.869491
+vn -0.467923 -0.734358 0.491698
+vn -0.114081 -0.538145 0.835096
+vn -0.127296 -0.607792 0.783827
+vn -0.467923 -0.734358 0.491698
+vn -0.237198 -0.806417 0.541691
+vn -0.114081 -0.538145 0.835096
+vn -0.467923 -0.734358 0.491698
+vn -0.127296 -0.607792 0.783827
+vn -0.157268 -0.676224 0.719714
+vn -0.609223 -0.680913 0.406454
+vn -0.157268 -0.676224 0.719714
+vn -0.121832 -0.784555 0.607972
+vn -0.609223 -0.680913 0.406454
+vn -0.467923 -0.734358 0.491698
+vn -0.157268 -0.676224 0.719714
+vn -0.609223 -0.680913 0.406454
+vn -0.121832 -0.784555 0.607972
+vn -0.154304 -0.850013 0.503654
+vn -0.638581 -0.710667 0.295241
+vn -0.154304 -0.850013 0.503654
+vn -0.123083 -0.939557 0.319503
+vn -0.638581 -0.710667 0.295241
+vn -0.609223 -0.680913 0.406454
+vn -0.154304 -0.850013 0.503654
+vn -0.647672 -0.752810 0.117467
+vn -0.123083 -0.939557 0.319503
+vn -0.165385 -0.979921 0.111366
+vn -0.647672 -0.752810 0.117467
+vn -0.638581 -0.710667 0.295241
+vn -0.123083 -0.939557 0.319503
+vn -0.647672 -0.752810 0.117467
+vn -0.165385 -0.979921 0.111366
+vn -0.145208 -0.987646 -0.058901
+vn -0.648624 -0.756632 -0.082432
+vn -0.145208 -0.987646 -0.058901
+vn -0.123999 -0.951033 -0.283125
+vn -0.648624 -0.756632 -0.082432
+vn -0.647672 -0.752810 0.117467
+vn -0.145208 -0.987646 -0.058901
+vn -0.639161 -0.723760 -0.260084
+vn -0.123999 -0.951033 -0.283125
+vn -0.171456 -0.865459 -0.470726
+vn -0.639161 -0.723760 -0.260084
+vn -0.648624 -0.756632 -0.082432
+vn -0.123999 -0.951033 -0.283125
+vn -0.639161 -0.723760 -0.260084
+vn -0.171456 -0.865459 -0.470726
+vn -0.176492 -0.781012 -0.599058
+vn -0.604308 -0.711613 -0.358355
+vn -0.639161 -0.723760 -0.260084
+vn -0.176492 -0.781012 -0.599058
+vn -0.460262 -0.773604 -0.435541
+vn -0.140114 -0.669504 -0.729474
+vn -0.119057 -0.591407 -0.797536
+vn -0.460262 -0.773604 -0.435541
+vn -0.604308 -0.711613 -0.358355
+vn -0.140114 -0.669504 -0.729474
+vn -0.243419 -0.829015 -0.503470
+vn -0.119057 -0.591407 -0.797536
+vn -0.048831 -0.536314 -0.842605
+vn -0.243419 -0.829015 -0.503470
+vn -0.460262 -0.773604 -0.435541
+vn -0.119057 -0.591407 -0.797536
+vn -0.243419 -0.829015 -0.503470
+vn -0.048831 -0.536314 -0.842605
+vn 0.007019 -0.844861 -0.534939
+vn -0.745103 0.591285 -0.308552
+vn -0.734685 0.678408 0.000000
+vn -0.500057 0.836990 -0.222241
+vn -0.670866 0.741578 0.000000
+vn 0.253949 -0.348223 0.902358
+vn 0.000000 -0.052402 0.998626
+vn 0.173990 -0.397360 0.901018
+vn 0.000031 -0.052339 0.998629
+vn 0.381762 0.058535 0.922405
+vn 0.396174 0.091223 0.913633
+vn 0.000000 0.044038 0.999030
+vn 0.000000 0.048464 0.998825
+vn 0.000000 0.052126 0.998641
+vn -0.392471 0.192390 0.899418
+vn 0.000000 0.048434 0.998826
+vn -0.395796 0.091006 0.913818
+vn -0.381975 0.058596 -0.922313
+vn -0.396046 0.091008 -0.913710
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.048190 -0.998838
+vn -0.316758 0.606690 -0.729103
+vn -0.214888 0.828363 -0.517338
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.732851 -0.680389
+vn -0.393298 0.193094 -0.898906
+vn -0.396046 0.091008 -0.913710
+vn -0.870643 0.329573 -0.365189
+vn -0.919325 0.124518 -0.373278
+vn 0.173990 -0.397360 0.901018
+vn 0.000031 -0.052339 0.998629
+vn 0.087254 -0.426017 0.900498
+vn 0.000000 -0.052126 0.998641
+vn -0.912094 -0.149238 0.381855
+vn -0.902001 -0.203565 0.380730
+vn -0.375319 -0.102543 0.921206
+vn -0.377613 -0.138923 0.915483
+vn 0.000000 0.044008 0.999031
+vn 0.000000 0.044069 0.999029
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.048160 -0.998840
+vn 0.395989 0.090856 -0.913750
+vn 0.000000 0.043978 -0.999033
+vn 0.382011 0.058628 -0.922296
+vn -0.319323 -0.281907 0.904744
+vn -0.254165 -0.348043 0.902367
+vn 0.000000 -0.052279 0.998633
+vn 0.000031 -0.052432 0.998624
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078433 0.996919 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.719155 0.592775 -0.362538
+vn 0.325795 0.316060 -0.891046
+vn 0.524598 0.770401 -0.362326
+vn 0.228679 0.372332 -0.899486
+vn 0.919351 0.124242 -0.373307
+vn 0.395989 0.090856 -0.913750
+vn 0.868458 0.334950 -0.365499
+vn 0.392297 0.200055 -0.897820
+vn 0.998866 0.047610 0.000000
+vn 0.993055 0.117650 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993066 0.117560 0.000000
+vn 0.395989 0.090856 -0.913750
+vn 0.000000 0.048160 -0.998840
+vn 0.392297 0.200055 -0.897820
+vn 0.002930 0.055209 -0.998470
+vn -0.316758 0.606690 -0.729103
+vn -0.745103 0.591285 -0.308552
+vn -0.214888 0.828363 -0.517338
+vn -0.500057 0.836990 -0.222241
+vn 0.000000 0.052279 -0.998633
+vn 0.000000 0.048190 -0.998838
+vn 0.000000 0.052309 -0.998631
+vn 0.000000 0.048190 -0.998838
+vn 0.392297 0.200055 -0.897820
+vn 0.002930 0.055209 -0.998470
+vn 0.325795 0.316060 -0.891046
+vn 0.228679 0.372332 -0.899486
+vn 0.228679 0.372332 -0.899486
+vn 0.002930 0.055209 -0.998470
+vn 0.122871 0.434167 -0.892414
+vn 0.028932 0.428152 -0.903243
+vn -0.393298 0.193094 -0.898906
+vn -0.312152 0.299273 -0.901663
+vn 0.000000 0.052309 -0.998631
+vn -0.224897 0.374747 -0.899436
+vn -0.029329 0.427604 -0.903490
+vn 0.000000 0.052309 -0.998631
+vn -0.121224 0.433042 -0.893185
+vn -0.224897 0.374747 -0.899436
+vn 0.000000 0.418594 -0.908174
+vn 0.028932 0.428152 -0.903243
+vn 0.000000 0.052279 -0.998633
+vn 0.002930 0.055209 -0.998470
+vn 0.184245 -0.380941 -0.906056
+vn 0.106113 -0.434098 -0.894594
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn -0.171580 -0.405969 -0.897636
+vn -0.277025 -0.360404 -0.890711
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.366104 -0.154792 -0.917610
+vn 0.374497 -0.244701 -0.894356
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn -0.386469 -0.166759 -0.907102
+vn 0.000000 -0.052309 -0.998631
+vn -0.342519 -0.277268 -0.897666
+vn 0.000000 -0.052309 -0.998631
+vn 0.106113 -0.434098 -0.894594
+vn 0.002747 -0.441857 -0.897081
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn -0.277025 -0.360404 -0.890711
+vn -0.342519 -0.277268 -0.897666
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.386469 -0.166759 -0.907102
+vn -0.385668 -0.101262 -0.917064
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.171580 -0.405969 -0.897636
+vn 0.000000 -0.052309 -0.998631
+vn -0.087345 -0.430315 -0.898443
+vn 0.000000 -0.052339 -0.998629
+vn 0.184245 -0.380941 -0.906056
+vn 0.000000 -0.052309 -0.998631
+vn 0.304368 -0.320665 -0.896958
+vn 0.000000 -0.052339 -0.998629
+vn -0.087345 -0.430315 -0.898443
+vn 0.000000 -0.052339 -0.998629
+vn 0.002747 -0.441857 -0.897081
+vn 0.000000 -0.052339 -0.998629
+vn 0.374497 -0.244701 -0.894356
+vn 0.304368 -0.320665 -0.896958
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 0.699991 -0.714152
+vn 0.372764 -0.088140 -0.923731
+vn 0.321855 0.605804 -0.727606
+vn -0.316758 0.606690 -0.729103
+vn 0.000000 0.699991 -0.714152
+vn -0.379990 -0.085209 -0.921058
+vn 0.000000 -0.052309 -0.998631
+vn 0.209422 -0.955035 0.209880
+vn -0.001221 -0.985450 0.169959
+vn 0.212324 -0.913234 -0.347739
+vn 0.000702 -0.938897 -0.344197
+vn 0.184245 -0.380941 -0.906056
+vn 0.439571 -0.822101 -0.361838
+vn 0.106113 -0.434098 -0.894594
+vn 0.212324 -0.913234 -0.347739
+vn 0.720131 0.591401 0.362843
+vn 0.867846 0.335834 0.366140
+vn 0.781137 0.624360 0.000000
+vn 0.939468 0.342637 0.000000
+vn 0.527037 0.768230 0.363393
+vn 0.720131 0.591401 0.362843
+vn 0.569396 0.822064 0.000000
+vn 0.781137 0.624360 0.000000
+vn 0.867846 0.335834 0.366140
+vn 0.919256 0.124211 0.373550
+vn 0.939468 0.342637 0.000000
+vn 0.993066 0.117560 0.000000
+vn 0.993066 0.117560 0.000000
+vn 0.919256 0.124211 0.373550
+vn 0.998866 0.047610 0.000000
+vn 0.922245 0.060458 0.381850
+vn -0.992964 0.118414 0.000000
+vn -0.992942 0.118598 0.000000
+vn -0.939600 0.342275 0.000000
+vn -0.939332 0.343010 0.000000
+vn 0.780580 0.625056 0.000000
+vn 0.939246 0.343245 0.000000
+vn 0.780499 0.625156 0.000000
+vn 0.939213 0.343335 0.000000
+vn 0.000000 0.928845 0.370470
+vn 0.000000 0.418172 0.908368
+vn 0.069399 0.929325 0.362684
+vn 0.029085 0.427176 0.903701
+vn 0.396174 0.091223 0.913633
+vn 0.396049 0.205456 0.894948
+vn 0.000000 0.048464 0.998825
+vn 0.003998 0.056093 0.998418
+vn 0.000000 0.052005 0.998647
+vn 0.000000 0.052126 0.998641
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.048434 0.998826
+vn -0.392471 0.192390 0.899418
+vn 0.000000 0.052126 0.998641
+vn -0.312362 0.298720 0.901774
+vn -0.224988 0.373677 0.899859
+vn -0.029177 0.427853 0.903377
+vn -0.122289 0.433856 0.892645
+vn 0.000000 0.052126 0.998641
+vn -0.224988 0.373677 0.899859
+vn 0.396049 0.205456 0.894948
+vn 0.320333 0.310109 0.895108
+vn 0.003998 0.056093 0.998418
+vn 0.235030 0.375908 0.896356
+vn 0.235030 0.375908 0.896356
+vn 0.121893 0.433583 0.892832
+vn 0.003998 0.056093 0.998418
+vn 0.029085 0.427176 0.903701
+vn 0.000000 0.418172 0.908368
+vn 0.000000 0.052005 0.998647
+vn 0.029085 0.427176 0.903701
+vn 0.003998 0.056093 0.998418
+vn -0.174140 -0.397386 0.900977
+vn -0.087286 -0.425991 0.900507
+vn -0.000031 -0.052432 0.998624
+vn 0.000000 -0.052096 0.998642
+vn 0.087254 -0.426017 0.900498
+vn 0.000000 -0.052126 0.998641
+vn 0.000000 -0.434743 0.900555
+vn 0.000000 -0.052157 0.998639
+vn 0.000000 -0.052309 0.998631
+vn 0.377487 -0.139135 0.915503
+vn 0.000000 -0.052370 0.998628
+vn 0.375234 -0.102697 0.921223
+vn 0.000000 -0.052279 0.998633
+vn 0.000000 0.703021 0.711169
+vn -0.375319 -0.102543 0.921206
+vn -0.329091 0.602117 0.727430
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052370 0.998628
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052463 0.998623
+vn 0.000000 -0.052402 0.998626
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052248 0.998634
+vn 0.000031 -0.052339 0.998629
+vn 0.000000 -0.052463 0.998623
+vn 0.000000 -0.052402 0.998626
+vn 0.000031 -0.052432 0.998624
+vn -0.000031 -0.052432 0.998624
+vn 0.000000 -0.052463 0.998623
+vn 0.000000 -0.052248 0.998634
+vn 0.000000 -0.052279 0.998633
+vn 0.000031 -0.052432 0.998624
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052463 0.998623
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052279 0.998633
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052279 0.998633
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 0.052126 0.998641
+vn 0.000000 0.052005 0.998647
+vn -0.029177 0.427853 0.903377
+vn 0.000000 0.418172 0.908368
+vn 0.000000 0.048464 0.998825
+vn 0.003998 0.056093 0.998418
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.052005 0.998647
+vn -0.069248 0.929275 0.362841
+vn -0.029177 0.427853 0.903377
+vn 0.000000 0.928845 0.370470
+vn 0.000000 0.418172 0.908368
+vn 0.000031 -0.052339 0.998629
+vn 0.000000 -0.052248 0.998634
+vn 0.000000 -0.052126 0.998641
+vn 0.000000 -0.052157 0.998639
+vn -0.000031 -0.052432 0.998624
+vn 0.000000 -0.052096 0.998642
+vn 0.000000 -0.052248 0.998634
+vn 0.000000 -0.052157 0.998639
+vn 0.660743 -0.650336 0.374808
+vn 0.789045 -0.614139 0.015504
+vn 0.778579 -0.505401 0.372000
+vn 0.884960 -0.465507 0.012208
+vn 0.310045 -0.595674 0.740976
+vn 0.263930 -0.785352 0.559967
+vn 0.741037 -0.593385 0.314257
+vn 0.621805 -0.753893 0.212141
+vn -0.157268 -0.676224 0.719714
+vn 0.172404 -0.693613 0.699412
+vn -0.121832 -0.784555 0.607972
+vn 0.125158 -0.786652 0.604577
+vn -0.048831 -0.536314 -0.842605
+vn -0.119057 -0.591407 -0.797536
+vn 0.048037 -0.537777 -0.841717
+vn 0.120276 -0.592529 -0.796519
+vn 0.007019 -0.844861 -0.534939
+vn -0.000397 -0.487642 -0.873044
+vn 0.048037 -0.537777 -0.841717
+vn 0.007019 -0.844861 -0.534939
+vn -0.048831 -0.536314 -0.842605
+vn -0.000397 -0.487642 -0.873044
+vn 0.000275 -0.811299 0.584631
+vn 0.046267 -0.491606 0.869588
+vn 0.000031 -0.438709 0.898629
+vn 0.000275 -0.811299 0.584631
+vn 0.000031 -0.438709 0.898629
+vn -0.046175 -0.491786 0.869491
+vn 0.439571 -0.822101 -0.361838
+vn 0.640223 -0.679501 -0.358321
+vn 0.453122 -0.859397 0.236892
+vn 0.640007 -0.753384 0.151006
+vn 0.304368 -0.320665 -0.896958
+vn 0.640223 -0.679501 -0.358321
+vn 0.184245 -0.380941 -0.906056
+vn 0.439571 -0.822101 -0.361838
+vn 0.374497 -0.244701 -0.894356
+vn 0.802405 -0.482688 -0.350938
+vn 0.304368 -0.320665 -0.896958
+vn 0.640223 -0.679501 -0.358321
+vn -0.382251 -0.857281 -0.344896
+vn -0.429001 -0.879885 0.204353
+vn -0.584690 -0.736127 -0.340962
+vn -0.633088 -0.762062 0.135871
+vn -0.176492 -0.781012 -0.599058
+vn 0.183848 -0.767314 -0.614352
+vn -0.140114 -0.669504 -0.729474
+vn 0.138436 -0.667855 -0.731304
+vn -0.604308 -0.711613 -0.358355
+vn -0.176492 -0.781012 -0.599058
+vn -0.140114 -0.669504 -0.729474
+vn 0.004517 -0.992742 -0.120182
+vn -0.000855 -0.999608 0.027986
+vn -0.250746 -0.957540 -0.142281
+vn -0.297408 -0.954362 0.027223
+vn 0.568818 0.822464 0.000000
+vn 0.780580 0.625056 0.000000
+vn 0.568762 0.822502 0.000000
+vn 0.780499 0.625156 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992964 0.118414 0.000000
+vn -0.992942 0.118598 0.000000
+vn 0.780499 0.625156 0.000000
+vn 0.939213 0.343335 0.000000
+vn 0.780446 0.625223 0.000000
+vn 0.939190 0.343399 0.000000
+vn 0.568762 0.822502 0.000000
+vn 0.780499 0.625156 0.000000
+vn 0.568692 0.822550 0.000000
+vn 0.780446 0.625223 0.000000
+vn 0.939213 0.343335 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.939190 0.343399 0.000000
+vn 0.993044 0.117741 0.000000
+vn -0.939844 0.341603 0.000000
+vn -0.782234 0.622985 0.000000
+vn -0.939891 0.341476 0.000000
+vn -0.782382 0.622799 0.000000
+vn -0.569521 0.821977 0.000000
+vn -0.313186 0.949692 0.000000
+vn -0.569702 0.821851 0.000000
+vn -0.313370 0.949631 0.000000
+vn -0.313186 0.949692 0.000000
+vn -0.079319 0.996849 0.000000
+vn -0.313370 0.949631 0.000000
+vn -0.079380 0.996844 0.000000
+vn -0.782234 0.622985 0.000000
+vn -0.569521 0.821977 0.000000
+vn -0.782382 0.622799 0.000000
+vn -0.569702 0.821851 0.000000
+vn -0.569305 0.822126 0.000000
+vn -0.313003 0.949752 0.000000
+vn -0.569521 0.821977 0.000000
+vn -0.313186 0.949692 0.000000
+vn -0.939778 0.341785 0.000000
+vn -0.782058 0.623205 0.000000
+vn -0.939844 0.341603 0.000000
+vn -0.782234 0.622985 0.000000
+vn -0.992990 0.118199 0.000000
+vn -0.992986 0.118229 0.000000
+vn -0.939891 0.341476 0.000000
+vn -0.939844 0.341603 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993040 0.117774 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993044 0.117741 0.000000
+vn -0.912094 -0.149238 0.381855
+vn -0.750227 0.586307 0.305621
+vn -0.990001 -0.141058 -0.000305
+vn -0.737682 0.675148 0.000000
+vn -0.991137 -0.132636 -0.007416
+vn -0.992570 -0.121649 -0.002442
+vn -0.734553 0.678551 -0.000092
+vn -0.730939 0.682443 -0.000031
+vn -0.992527 -0.122013 0.001740
+vn -0.992570 -0.121649 -0.002442
+vn -0.954279 -0.298907 0.002472
+vn -0.951202 -0.308125 0.016572
+vn -0.087286 -0.425991 0.900507
+vn -0.174140 -0.397386 0.900977
+vn -0.238259 -0.894775 0.377637
+vn -0.471889 -0.796857 0.377279
+vn 0.000000 -0.925801 0.378011
+vn -0.238259 -0.894775 0.377637
+vn -0.000855 -0.999608 0.027986
+vn -0.297408 -0.954362 0.027223
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn -0.377613 -0.138923 0.915483
+vn -0.353195 -0.211129 0.911415
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052309 0.998631
+vn 0.352987 -0.211408 0.911431
+vn 0.000000 -0.052309 0.998631
+vn 0.319082 -0.282245 0.904723
+vn 0.000000 -0.052339 0.998629
+vn -0.849339 -0.521933 -0.078800
+vn -0.710895 -0.695605 -0.103736
+vn -0.886617 -0.462368 0.011231
+vn -0.788745 -0.614540 0.014893
+vn 0.000000 0.048160 -0.998840
+vn 0.000000 0.048190 -0.998838
+vn 0.002930 0.055209 -0.998470
+vn 0.000000 0.052279 -0.998633
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn -0.916742 -0.125557 -0.379234
+vn -0.909937 -0.184063 -0.371666
+vn -0.991018 -0.133704 -0.002472
+vn -0.939317 -0.333574 0.080082
+vn -0.939703 0.341993 0.000000
+vn -0.781842 0.623477 0.000000
+vn -0.939778 0.341785 0.000000
+vn -0.782058 0.623205 0.000000
+vn -0.569034 0.822314 0.000000
+vn -0.312756 0.949834 0.000000
+vn -0.569305 0.822126 0.000000
+vn -0.313003 0.949752 0.000000
+vn -0.992986 0.118229 0.000000
+vn -0.992979 0.118293 0.000000
+vn -0.939844 0.341603 0.000000
+vn -0.939778 0.341785 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992990 0.118199 0.000000
+vn -0.992986 0.118229 0.000000
+vn -0.781842 0.623477 0.000000
+vn -0.569034 0.822314 0.000000
+vn -0.782058 0.623205 0.000000
+vn -0.569305 0.822126 0.000000
+vn -0.781571 0.623816 0.000000
+vn -0.568658 0.822574 0.000000
+vn -0.781842 0.623477 0.000000
+vn -0.569034 0.822314 0.000000
+vn -0.079471 0.996837 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.070103 0.929281 -0.362660
+vn 0.000000 0.928819 -0.370533
+vn -0.568658 0.822574 0.000000
+vn -0.312453 0.949933 0.000000
+vn -0.569034 0.822314 0.000000
+vn -0.312756 0.949834 0.000000
+vn -0.939600 0.342275 0.000000
+vn -0.781571 0.623816 0.000000
+vn -0.939703 0.341993 0.000000
+vn -0.781842 0.623477 0.000000
+vn -0.992979 0.118293 0.000000
+vn -0.992975 0.118323 0.000000
+vn -0.939778 0.341785 0.000000
+vn -0.939703 0.341993 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992986 0.118229 0.000000
+vn -0.992979 0.118293 0.000000
+vn -0.939332 0.343010 0.000000
+vn -0.780839 0.624732 0.000000
+vn -0.939600 0.342275 0.000000
+vn -0.781571 0.623816 0.000000
+vn -0.567682 0.823248 0.000000
+vn -0.311600 0.950213 0.000000
+vn -0.568658 0.822574 0.000000
+vn -0.312453 0.949933 0.000000
+vn -0.780839 0.624732 0.000000
+vn -0.567682 0.823248 0.000000
+vn -0.781571 0.623816 0.000000
+vn -0.568658 0.822574 0.000000
+vn -0.992975 0.118323 0.000000
+vn -0.992964 0.118414 0.000000
+vn -0.939703 0.341993 0.000000
+vn -0.939600 0.342275 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992979 0.118293 0.000000
+vn -0.992975 0.118323 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.992975 0.118323 0.000000
+vn -0.992964 0.118414 0.000000
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.079471 0.996837 0.000000
+vn -0.079380 0.996844 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 0.699991 -0.714152
+vn 0.078190 0.996939 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311664 0.950192 0.000000
+vn 0.078190 0.996939 0.000000
+vn 0.311536 0.950234 0.000000
+vn 0.311664 0.950192 0.000000
+vn 0.568692 0.822550 0.000000
+vn 0.311536 0.950234 0.000000
+vn 0.568602 0.822613 0.000000
+vn 0.780446 0.625223 0.000000
+vn 0.939190 0.343399 0.000000
+vn 0.780351 0.625342 0.000000
+vn 0.939143 0.343526 0.000000
+vn 0.568692 0.822550 0.000000
+vn 0.780446 0.625223 0.000000
+vn 0.568602 0.822613 0.000000
+vn 0.780351 0.625342 0.000000
+vn 0.939190 0.343399 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.939143 0.343526 0.000000
+vn 0.993040 0.117774 0.000000
+vn -0.939891 0.341476 0.000000
+vn -0.782382 0.622799 0.000000
+vn -0.939966 0.341268 0.000000
+vn -0.782587 0.622541 0.000000
+vn -0.569702 0.821851 0.000000
+vn -0.313370 0.949631 0.000000
+vn -0.569973 0.821663 0.000000
+vn -0.313581 0.949562 0.000000
+vn -0.313370 0.949631 0.000000
+vn -0.079380 0.996844 0.000000
+vn -0.313581 0.949562 0.000000
+vn -0.079471 0.996837 0.000000
+vn -0.782382 0.622799 0.000000
+vn -0.569702 0.821851 0.000000
+vn -0.782587 0.622541 0.000000
+vn -0.569973 0.821663 0.000000
+vn -0.919325 0.124518 -0.373278
+vn -0.992997 0.118139 0.000000
+vn -0.870643 0.329573 -0.365189
+vn -0.939966 0.341268 0.000000
+vn 0.922348 0.060580 -0.381580
+vn 0.919351 0.124242 -0.373307
+vn 0.998866 0.047610 0.000000
+vn 0.993040 0.117774 0.000000
+vn -0.734964 0.678106 0.000061
+vn -0.730909 0.682475 0.000000
+vn -0.990831 -0.134986 0.005646
+vn -0.992527 -0.122013 0.001740
+vn -0.379990 -0.085209 -0.921058
+vn -0.916742 -0.125557 -0.379234
+vn -0.316758 0.606690 -0.729103
+vn -0.745103 0.591285 -0.308552
+vn 0.990046 -0.140663 -0.004853
+vn 0.913199 -0.133217 -0.385123
+vn 0.737157 0.675722 -0.000092
+vn 0.747977 0.586436 -0.310843
+vn 0.778579 -0.505401 0.372000
+vn 0.884960 -0.465507 0.012208
+vn 0.857781 -0.346641 0.379541
+vn 0.951306 -0.308211 0.004853
+vn 0.984351 -0.176215 -0.001099
+vn 0.901970 -0.203534 0.380821
+vn 0.951306 -0.308211 0.004853
+vn 0.857781 -0.346641 0.379541
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.366104 -0.154792 -0.917610
+vn 0.362879 -0.103340 -0.926089
+vn 0.904830 -0.170541 -0.390126
+vn 0.877705 -0.298509 -0.374869
+vn 0.362879 -0.103340 -0.926089
+vn 0.366104 -0.154792 -0.917610
+vn -0.385668 -0.101262 -0.917064
+vn -0.386469 -0.166759 -0.907102
+vn -0.909937 -0.184063 -0.371666
+vn -0.859060 -0.359823 -0.364066
+vn 0.897571 -0.428521 0.103613
+vn 0.985937 -0.163336 -0.035341
+vn 0.934625 -0.355612 0.004151
+vn -0.922339 0.060549 -0.381607
+vn -0.998866 0.047610 0.000000
+vn -0.919325 0.124518 -0.373278
+vn -0.992997 0.118139 0.000000
+vn 0.568888 0.822415 0.000000
+vn 0.780675 0.624937 0.000000
+vn 0.568818 0.822464 0.000000
+vn 0.780580 0.625056 0.000000
+vn 0.780675 0.624937 0.000000
+vn 0.939279 0.343154 0.000000
+vn 0.780580 0.625056 0.000000
+vn 0.939246 0.343245 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.939279 0.343154 0.000000
+vn 0.993052 0.117681 0.000000
+vn 0.939246 0.343245 0.000000
+vn 0.993048 0.117711 0.000000
+vn 0.939335 0.343000 0.000000
+vn 0.993055 0.117650 0.000000
+vn 0.939279 0.343154 0.000000
+vn 0.993052 0.117681 0.000000
+vn 0.780797 0.624784 0.000000
+vn 0.939335 0.343000 0.000000
+vn 0.780675 0.624937 0.000000
+vn 0.939279 0.343154 0.000000
+vn 0.568999 0.822338 0.000000
+vn 0.780797 0.624784 0.000000
+vn 0.568888 0.822415 0.000000
+vn 0.780675 0.624937 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993044 0.117741 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993048 0.117711 0.000000
+vn -0.311879 -0.598488 -0.737932
+vn 0.000000 -0.679106 -0.734040
+vn -0.233778 -0.773910 -0.588566
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 0.418594 -0.908174
+vn 0.000000 0.052279 -0.998633
+vn -0.029329 0.427604 -0.903490
+vn 0.000000 0.052309 -0.998631
+vn 0.311664 0.950192 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.069399 0.929325 0.362684
+vn 0.286943 0.888480 0.358145
+vn 0.078433 0.996919 0.000000
+vn 0.312178 0.950024 0.000000
+vn 0.286943 0.888480 0.358145
+vn 0.527037 0.768230 0.363393
+vn 0.312178 0.950024 0.000000
+vn 0.569396 0.822064 0.000000
+vn 0.992243 -0.124275 0.003082
+vn 0.992707 -0.120552 -0.000946
+vn 0.731704 0.681622 0.000031
+vn 0.730525 0.682886 0.000000
+vn 0.568762 0.822502 0.000000
+vn 0.568692 0.822550 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.311664 0.950192 0.000000
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.992707 -0.120552 -0.000946
+vn 0.990875 -0.134469 -0.009247
+vn 0.730525 0.682886 0.000000
+vn 0.735447 0.677583 -0.000153
+vn 0.311756 0.950162 0.000000
+vn 0.568999 0.822338 0.000000
+vn 0.311720 0.950174 0.000000
+vn 0.568888 0.822415 0.000000
+vn 0.311756 0.950162 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.312178 0.950024 0.000000
+vn 0.078433 0.996919 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311720 0.950174 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.568818 0.822464 0.000000
+vn 0.568762 0.822502 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.311720 0.950174 0.000000
+vn 0.568888 0.822415 0.000000
+vn 0.311692 0.950183 0.000000
+vn 0.568818 0.822464 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078433 0.996919 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079319 0.996849 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079380 0.996844 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.886617 -0.462368 0.011231
+vn -0.952300 -0.305104 0.005982
+vn -0.849339 -0.521933 -0.078800
+vn -0.913458 -0.399399 -0.077945
+vn -0.952300 -0.305104 0.005982
+vn -0.984672 -0.174417 0.000336
+vn -0.913458 -0.399399 -0.077945
+vn -0.939019 -0.338459 -0.060733
+vn -0.990831 -0.134986 0.005646
+vn -0.989839 -0.142190 0.001251
+vn -0.734964 0.678106 0.000061
+vn -0.738163 0.674622 0.000000
+vn -0.990001 -0.141058 -0.000305
+vn -0.989839 -0.142190 0.001251
+vn -0.984672 -0.174417 0.000336
+vn -0.939019 -0.338459 -0.060733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 0.418594 -0.908174
+vn -0.029329 0.427604 -0.903490
+vn 0.000000 0.928819 -0.370533
+vn -0.070103 0.929281 -0.362660
+vn 0.078220 0.996936 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.311720 0.950174 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.311756 0.950162 0.000000
+vn 0.078220 0.996936 0.000000
+vn 0.312178 0.950024 0.000000
+vn 0.569396 0.822064 0.000000
+vn 0.311756 0.950162 0.000000
+vn 0.568999 0.822338 0.000000
+vn 0.569396 0.822064 0.000000
+vn 0.781137 0.624360 0.000000
+vn 0.568999 0.822338 0.000000
+vn 0.780797 0.624784 0.000000
+vn 0.781137 0.624360 0.000000
+vn 0.939468 0.342637 0.000000
+vn 0.780797 0.624784 0.000000
+vn 0.939335 0.343000 0.000000
+vn 0.939468 0.342637 0.000000
+vn 0.993066 0.117560 0.000000
+vn 0.939335 0.343000 0.000000
+vn 0.993055 0.117650 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993048 0.117711 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993052 0.117681 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.078738 0.996895 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 1.000000 0.000000
+vn 0.078190 0.996939 0.000000
+vn 0.000000 0.928819 -0.370533
+vn 0.068943 0.929284 -0.362875
+vn -0.079165 0.996861 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079259 0.996854 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079044 0.996871 0.000000
+vn -0.079165 0.996861 0.000000
+vn -0.313003 0.949752 0.000000
+vn -0.079259 0.996854 0.000000
+vn -0.313186 0.949692 0.000000
+vn -0.079319 0.996849 0.000000
+vn -0.312756 0.949834 0.000000
+vn -0.079165 0.996861 0.000000
+vn -0.313003 0.949752 0.000000
+vn -0.079259 0.996854 0.000000
+vn -0.312453 0.949933 0.000000
+vn -0.079044 0.996871 0.000000
+vn -0.312756 0.949834 0.000000
+vn -0.079165 0.996861 0.000000
+vn -0.311600 0.950213 0.000000
+vn -0.078738 0.996895 0.000000
+vn -0.312453 0.949933 0.000000
+vn -0.079044 0.996871 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079319 0.996849 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.079259 0.996854 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.078738 0.996895 0.000000
+vn -0.079044 0.996871 0.000000
+vn 0.949811 -0.310561 0.037569
+vn 0.934625 -0.355612 0.004151
+vn 0.985937 -0.163336 -0.035341
+vn 0.950440 -0.308980 0.034579
+vn 0.949811 -0.310561 0.037569
+vn 0.985937 -0.163336 -0.035341
+vn 0.990046 -0.140663 -0.004853
+vn 0.990875 -0.134469 -0.009247
+vn 0.985937 -0.163336 -0.035341
+vn 0.950440 -0.308980 0.034579
+vn 0.992243 -0.124275 0.003082
+vn 0.731704 0.681622 0.000031
+vn 0.990438 -0.137855 0.005310
+vn 0.736588 0.676342 0.000092
+vn 0.990438 -0.137855 0.005310
+vn 0.736588 0.676342 0.000092
+vn 0.989089 -0.147315 0.001312
+vn 0.739720 0.672914 0.000000
+vn 0.989641 -0.143562 -0.000641
+vn 0.984351 -0.176215 -0.001099
+vn 0.989089 -0.147315 0.001312
+vn 0.981702 -0.190316 0.006439
+vn 0.898822 -0.432030 -0.073948
+vn 0.981702 -0.190316 0.006439
+vn 0.951306 -0.308211 0.004853
+vn 0.984351 -0.176215 -0.001099
+vn 0.898822 -0.432030 -0.073948
+vn 0.922100 -0.382311 -0.059756
+vn 0.981702 -0.190316 0.006439
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 -0.052339 -0.998629
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.078738 0.996895 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.078433 0.996919 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.069399 0.929325 0.362684
+vn 0.078433 0.996919 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.078738 0.996895 0.000000
+vn -0.069248 0.929275 0.362841
+vn 0.000000 1.000000 0.000000
+vn -0.069248 0.929275 0.362841
+vn 0.000000 0.928845 0.370470
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.069399 0.929325 0.362684
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.928845 0.370470
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.552338 0.770490 -0.318225
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 -0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 -0.637723
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn -0.552336 0.770518 -0.318162
+vn 0.000000 1.000000 0.000000
+vn -0.552336 0.770518 -0.318162
+vn 0.000000 1.000000 0.000000
+vn -0.637722 0.770266 0.001282
+vn 0.000000 1.000000 0.000000
+vn -0.637722 0.770266 0.001282
+vn 0.000000 1.000000 0.000000
+vn -0.552246 0.770001 0.319567
+vn 0.000000 1.000000 0.000000
+vn -0.552246 0.770001 0.319567
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.552244 0.770028 0.319505
+vn 0.000000 1.000000 0.000000
+vn 0.552244 0.770028 0.319505
+vn 0.000000 1.000000 0.000000
+vn 0.637722 0.770266 0.001160
+vn 0.000000 1.000000 0.000000
+vn 0.637722 0.770266 0.001160
+vn 0.000000 1.000000 0.000000
+vn 0.552338 0.770490 -0.318225
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 0.552274
+vn 0.552244 0.770028 0.319505
+vn 0.499989 -0.000000 0.866032
+vn 0.865566 -0.000000 0.500796
+vn -0.552246 0.770001 0.319567
+vn -0.318864 0.770272 0.552274
+vn -0.865518 -0.000000 0.500877
+vn -0.499989 -0.000000 0.866032
+vn 0.000000 0.770266 0.637723
+vn 0.318864 0.770272 0.552274
+vn 0.000000 -0.000000 1.000000
+vn 0.499989 -0.000000 0.866032
+vn -0.318864 0.770272 -0.552274
+vn -0.552336 0.770518 -0.318162
+vn -0.499989 0.000000 -0.866032
+vn -0.866523 0.000000 -0.499137
+vn 0.318864 0.770272 -0.552274
+vn 0.000000 0.770266 -0.637723
+vn 0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn 0.637722 0.770266 0.001160
+vn 0.552338 0.770490 -0.318225
+vn 0.999998 -0.000000 0.001831
+vn 0.866482 0.000000 -0.499209
+vn -0.552336 0.770518 -0.318162
+vn -0.637722 0.770266 0.001282
+vn -0.866523 0.000000 -0.499137
+vn -0.999998 -0.000000 0.002014
+vn -0.637722 0.770266 0.001282
+vn -0.552246 0.770001 0.319567
+vn -0.999998 -0.000000 0.002014
+vn -0.865518 -0.000000 0.500877
+vn -0.318864 0.770272 0.552274
+vn 0.000000 0.770266 0.637723
+vn -0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 0.552338 0.770490 -0.318225
+vn 0.318864 0.770272 -0.552274
+vn 0.866482 0.000000 -0.499209
+vn 0.499989 0.000000 -0.866032
+vn 0.552244 0.770028 0.319505
+vn 0.637722 0.770266 0.001160
+vn 0.865566 -0.000000 0.500796
+vn 0.999998 -0.000000 0.001831
+vn 0.000000 0.770266 -0.637723
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 0.000000 -1.000000
+vn -0.499989 0.000000 -0.866032
+vn 0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.191964 0.783728 -0.590695
+vn 0.000000 1.000000 0.000000
+vn -0.502529 0.783702 -0.365070
+vn 0.000000 1.000000 0.000000
+vn -0.502529 0.783702 -0.365070
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502529 0.783702 -0.365070
+vn 0.000000 1.000000 0.000000
+vn 0.502529 0.783702 -0.365070
+vn 0.000000 1.000000 0.000000
+vn 0.191964 0.783728 -0.590695
+vn 0.000000 1.000000 0.000000
+vn 0.191964 0.783728 -0.590695
+vn 0.000000 1.000000 0.000000
+vn -0.191964 0.783728 -0.590695
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 0.365076
+vn -1.000000 0.000000 0.000000
+vn -0.809030 -0.000000 0.587767
+vn -0.502506 0.783714 0.365076
+vn -0.191936 0.783706 0.590733
+vn -0.809030 -0.000000 0.587767
+vn -0.309004 -0.000000 0.951061
+vn 0.502529 0.783702 -0.365070
+vn 0.191964 0.783728 -0.590695
+vn 0.809045 0.000000 -0.587747
+vn 0.309068 0.000000 -0.951040
+vn -0.502529 0.783702 -0.365070
+vn -0.621130 0.783707 0.000000
+vn -0.809045 0.000000 -0.587747
+vn -1.000000 0.000000 0.000000
+vn 0.191964 0.783728 -0.590695
+vn -0.191964 0.783728 -0.590695
+vn 0.309068 0.000000 -0.951040
+vn -0.309068 0.000000 -0.951040
+vn -0.191936 0.783706 0.590733
+vn 0.191936 0.783706 0.590733
+vn -0.309004 -0.000000 0.951061
+vn 0.309004 -0.000000 0.951061
+vn 0.502506 0.783714 0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.809030 -0.000000 0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn 0.502506 0.783714 0.365076
+vn 0.309004 -0.000000 0.951061
+vn 0.809030 -0.000000 0.587767
+vn 0.621130 0.783707 0.000000
+vn 0.502529 0.783702 -0.365070
+vn 1.000000 0.000000 0.000000
+vn 0.809045 0.000000 -0.587747
+vn -0.191964 0.783728 -0.590695
+vn -0.502529 0.783702 -0.365070
+vn -0.309068 0.000000 -0.951040
+vn -0.809045 0.000000 -0.587747
+vn -0.502506 0.783714 0.365076
+vn -0.191936 0.783706 0.590733
+vn -0.809030 -0.000000 0.587767
+vn -0.309004 -0.000000 0.951061
+vn -0.191936 0.783706 0.590733
+vn 0.191936 0.783706 0.590733
+vn -0.309004 -0.000000 0.951061
+vn 0.309004 -0.000000 0.951061
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 0.365076
+vn -1.000000 0.000000 0.000000
+vn -0.809030 -0.000000 0.587767
+vn 0.621130 0.783707 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 1.000000 0.000000 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 0.191936 0.783706 0.590733
+vn 0.502506 0.783714 0.365076
+vn 0.309004 -0.000000 0.951061
+vn 0.809030 -0.000000 0.587767
+vn 0.502506 0.783714 0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.809030 -0.000000 0.587767
+vn 1.000000 0.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn -0.502506 0.783714 -0.365076
+vn -0.309004 0.000000 -0.951061
+vn -0.809030 0.000000 -0.587767
+vn 0.191936 0.783706 -0.590733
+vn -0.191936 0.783706 -0.590733
+vn 0.309004 0.000000 -0.951061
+vn -0.309004 0.000000 -0.951061
+vn 0.502506 0.783714 -0.365076
+vn 0.191936 0.783706 -0.590733
+vn 0.809030 0.000000 -0.587767
+vn 0.309004 0.000000 -0.951061
+vn -0.502506 0.783714 -0.365076
+vn -0.621130 0.783707 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.730909 0.682475 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.730939 0.682443 -0.000031
+vn -0.670866 0.741578 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993052 0.117681 0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.993055 0.117650 0.000000
+vn -0.737682 0.675148 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.738163 0.674622 0.000000
+vn -0.670866 0.741578 0.000000
+vn 0.735447 0.677583 -0.000153
+vn 0.737157 0.675722 -0.000092
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.732851 -0.680389
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.732866 -0.680373
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 -0.675866 0.737025
+vn 0.321855 0.605804 -0.727606
+vn 0.000000 0.699991 -0.714152
+vn 0.214916 0.828380 -0.517299
+vn 0.000000 0.732866 -0.680373
+vn -0.329091 0.602117 0.727430
+vn 0.000000 0.703021 0.711169
+vn -0.230361 0.846581 0.479828
+vn 0.000000 0.736456 0.676486
+vn 0.329149 0.602020 0.727484
+vn 0.230361 0.846581 0.479828
+vn 0.000000 0.702975 0.711215
+vn 0.000000 0.736456 0.676486
+vn -0.311879 -0.598488 -0.737932
+vn -0.233778 -0.773910 -0.588566
+vn -0.739452 -0.596163 -0.312731
+vn -0.590148 -0.771523 -0.237652
+vn 0.311907 -0.598482 -0.737925
+vn 0.233778 -0.773910 -0.588566
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.699991 -0.714152
+vn 0.000000 0.732866 -0.680373
+vn 0.000000 0.732866 -0.680373
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679106 -0.734040
+vn 0.738513 0.674239 0.000000
+vn 0.739720 0.672914 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.922245 0.060458 0.381850
+vn -0.741037 -0.593385 0.314257
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.730939 0.682443 -0.000031
+vn -0.670866 0.741578 0.000000
+vn -0.734553 0.678551 -0.000092
+vn -0.670866 0.741578 0.000000
+vn 0.741037 -0.593385 0.314257
+vn 0.621805 -0.753893 0.212141
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.922245 0.060458 0.381850
+vn -0.741037 -0.593385 0.314257
+vn -0.381763 0.058475 0.922409
+vn -0.310013 -0.595732 0.740942
+vn 0.750260 0.586248 0.305652
+vn 0.738513 0.674239 0.000000
+vn 0.531041 0.824090 0.197157
+vn 0.670866 0.741578 0.000000
+vn -0.310013 -0.595732 0.740942
+vn -0.741037 -0.593385 0.314257
+vn -0.263930 -0.785352 0.559967
+vn -0.621790 -0.753906 0.212136
+vn -0.381975 0.058596 -0.922313
+vn -0.311879 -0.598488 -0.737932
+vn -0.922339 0.060549 -0.381607
+vn -0.739452 -0.596163 -0.312731
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.922245 0.060458 0.381850
+vn 0.381762 0.058535 0.922405
+vn 0.741037 -0.593385 0.314257
+vn 0.310045 -0.595674 0.740976
+vn 0.922348 0.060580 -0.381580
+vn 0.739459 -0.596169 -0.312703
+vn 0.382011 0.058628 -0.922296
+vn 0.311907 -0.598482 -0.737925
+vn 0.736588 0.676342 0.000092
+vn 0.731704 0.681622 0.000031
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679106 -0.734040
+vn -0.741037 -0.593385 0.314257
+vn -0.742650 -0.669679 -0.000000
+vn -0.621790 -0.753906 0.212136
+vn -0.742346 -0.670016 -0.000000
+vn 0.000000 -0.675866 0.737025
+vn 0.000000 0.736456 0.676486
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 0.736456 0.676486
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 0.732866 -0.680373
+vn 0.000000 0.732866 -0.680373
+vn 0.500093 0.836968 -0.222243
+vn 0.214916 0.828380 -0.517299
+vn 0.590148 -0.771523 -0.237652
+vn 0.233778 -0.773910 -0.588566
+vn 0.621805 -0.753893 0.212141
+vn 0.263930 -0.785352 0.559967
+vn 0.531041 0.824090 0.197157
+vn 0.230361 0.846581 0.479828
+vn -0.233778 -0.773910 -0.588566
+vn -0.214888 0.828363 -0.517338
+vn -0.590148 -0.771523 -0.237652
+vn -0.500057 0.836990 -0.222241
+vn -0.621790 -0.753906 0.212136
+vn -0.531041 0.824090 0.197157
+vn -0.263930 -0.785352 0.559967
+vn -0.230361 0.846581 0.479828
+vn -0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.621790 -0.753906 0.212136
+vn -0.531041 0.824090 0.197157
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 0.732851 -0.680389
+vn 0.000000 0.732866 -0.680373
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.500057 0.836990 -0.222241
+vn -0.670866 0.741578 0.000000
+vn -0.590148 -0.771523 -0.237652
+vn -0.742346 -0.670016 -0.000000
+vn 0.500093 0.836968 -0.222243
+vn 0.590148 -0.771523 -0.237652
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.531041 0.824090 0.197157
+vn 0.621805 -0.753893 0.212141
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.000000 -0.679867 -0.733336
+vn 0.233778 -0.773910 -0.588566
+vn 0.000000 0.732866 -0.680373
+vn 0.214916 0.828380 -0.517299
+vn 0.000000 0.736456 0.676486
+vn 0.000000 0.736456 0.676486
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675866 0.737025
+vn -0.214888 0.828363 -0.517338
+vn -0.233778 -0.773910 -0.588566
+vn 0.000000 0.732851 -0.680389
+vn 0.000000 -0.679867 -0.733336
+vn 0.230361 0.846581 0.479828
+vn 0.263930 -0.785352 0.559967
+vn 0.000000 0.736456 0.676486
+vn 0.000000 -0.675849 0.737040
+vn -0.230361 0.846581 0.479828
+vn 0.000000 0.736456 0.676486
+vn -0.263930 -0.785352 0.559967
+vn 0.000000 -0.675849 0.737040
+vn -0.670866 0.741578 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.730525 0.682886 0.000000
+vn 0.735447 0.677583 -0.000153
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.329149 0.602020 0.727484
+vn 0.750260 0.586248 0.305652
+vn 0.230361 0.846581 0.479828
+vn 0.531041 0.824090 0.197157
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679867 -0.733336
+vn -0.329091 0.602117 0.727430
+vn -0.230361 0.846581 0.479828
+vn -0.750227 0.586307 0.305621
+vn -0.531041 0.824090 0.197157
+vn -0.310013 -0.595732 0.740942
+vn -0.263930 -0.785352 0.559967
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675866 0.737025
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 -0.679867 -0.733336
+vn 0.000000 0.703021 0.711169
+vn 0.000000 0.702990 0.711200
+vn 0.000000 0.736456 0.676486
+vn 0.000000 0.736456 0.676486
+vn 0.747977 0.586436 -0.310843
+vn 0.500093 0.836968 -0.222243
+vn 0.737157 0.675722 -0.000092
+vn 0.670866 0.741578 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.000000 0.702975 0.711215
+vn 0.000000 0.736456 0.676486
+vn 0.000000 0.702990 0.711200
+vn 0.000000 0.736456 0.676486
+vn -0.734553 0.678551 -0.000092
+vn -0.670866 0.741578 0.000000
+vn -0.734685 0.678408 0.000000
+vn -0.670866 0.741578 0.000000
+vn 0.321855 0.605804 -0.727606
+vn 0.214916 0.828380 -0.517299
+vn 0.747977 0.586436 -0.310843
+vn 0.500093 0.836968 -0.222243
+vn 0.731704 0.681622 0.000031
+vn 0.730525 0.682886 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn -0.739452 -0.596163 -0.312731
+vn -0.590148 -0.771523 -0.237652
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.739459 -0.596169 -0.312703
+vn 0.742650 -0.669679 -0.000000
+vn 0.590148 -0.771523 -0.237652
+vn 0.742346 -0.670016 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.738163 0.674622 0.000000
+vn -0.670866 0.741578 0.000000
+vn -0.734964 0.678106 0.000061
+vn -0.670866 0.741578 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn 0.739720 0.672914 0.000000
+vn 0.736588 0.676342 0.000092
+vn 0.670866 0.741578 0.000000
+vn 0.670866 0.741578 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.742346 -0.670016 -0.000000
+vn -0.750227 0.586307 0.305621
+vn -0.531041 0.824090 0.197157
+vn -0.737682 0.675148 0.000000
+vn -0.670866 0.741578 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.742346 -0.670016 -0.000000
+vn 0.000000 0.044038 0.999030
+vn 0.000000 0.044069 0.999029
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 0.048434 0.998826
+vn 0.000000 0.044069 0.999029
+vn 0.000000 0.048464 0.998825
+vn 0.000000 0.044038 0.999030
+vn 0.000000 0.048190 -0.998838
+vn 0.000000 0.048160 -0.998840
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.043978 -0.999033
+vn 0.922348 0.060580 -0.381580
+vn 0.382011 0.058628 -0.922296
+vn 0.919351 0.124242 -0.373307
+vn 0.395989 0.090856 -0.913750
+vn 0.396174 0.091223 0.913633
+vn 0.381762 0.058535 0.922405
+vn 0.919256 0.124211 0.373550
+vn 0.922245 0.060458 0.381850
+vn -0.396046 0.091008 -0.913710
+vn -0.381975 0.058596 -0.922313
+vn -0.919325 0.124518 -0.373278
+vn -0.922339 0.060549 -0.381607
+vn -0.922245 0.060458 0.381850
+vn -0.381763 0.058475 0.922409
+vn -0.919299 0.124793 0.373250
+vn -0.395796 0.091006 0.913818
+vn -0.992942 0.118598 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.919299 0.124793 0.373250
+vn -0.922245 0.060458 0.381850
+vn 0.000000 0.048190 -0.998838
+vn 0.000000 0.048190 -0.998838
+vn 0.000000 0.043978 -0.999033
+vn 0.000000 0.043978 -0.999033
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn -0.739452 -0.596163 -0.312731
+vn -0.742650 -0.669679 -0.000000
+vn -0.922339 0.060549 -0.381607
+vn -0.998866 0.047610 0.000000
+vn 0.739459 -0.596169 -0.312703
+vn 0.922348 0.060580 -0.381580
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.741037 -0.593385 0.314257
+vn 0.922245 0.060458 0.381850
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.742650 -0.669679 -0.000000
+vn 0.998866 0.047610 0.000000
+vn 0.000000 0.043978 -0.999033
+vn 0.382011 0.058628 -0.922296
+vn 0.000000 -0.679106 -0.734040
+vn 0.311907 -0.598482 -0.737925
+vn 0.000000 -0.675849 0.737040
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 0.044008 0.999031
+vn 0.000000 0.044069 0.999029
+vn -0.311879 -0.598488 -0.737932
+vn -0.381975 0.058596 -0.922313
+vn 0.000000 -0.679106 -0.734040
+vn 0.000000 0.043978 -0.999033
+vn 0.310045 -0.595674 0.740976
+vn 0.381762 0.058535 0.922405
+vn 0.000000 -0.675817 0.737069
+vn 0.000000 0.044038 0.999030
+vn -0.310013 -0.595732 0.740942
+vn 0.000000 -0.675849 0.737040
+vn -0.381763 0.058475 0.922409
+vn 0.000000 0.044008 0.999031
+vn -0.742650 -0.669679 -0.000000
+vn -0.742650 -0.669679 -0.000000
+vn -0.998866 0.047610 0.000000
+vn -0.998866 0.047610 0.000000
+vn 0.738513 0.674239 0.000000
+vn 0.989641 -0.143562 -0.000641
+vn 0.739720 0.672914 0.000000
+vn 0.989089 -0.147315 0.001312
+vn 0.946362 -0.321059 -0.036318
+vn 0.990438 -0.137855 0.005310
+vn 0.981702 -0.190316 0.006439
+vn 0.989089 -0.147315 0.001312
+vn 0.949985 -0.312053 -0.012299
+vn 0.992243 -0.124275 0.003082
+vn 0.946362 -0.321059 -0.036318
+vn 0.990438 -0.137855 0.005310
+vn 0.737157 0.675722 -0.000092
+vn 0.735447 0.677583 -0.000153
+vn 0.990046 -0.140663 -0.004853
+vn 0.990875 -0.134469 -0.009247
+vn -0.737682 0.675148 0.000000
+vn -0.738163 0.674622 0.000000
+vn -0.990001 -0.141058 -0.000305
+vn -0.989839 -0.142190 0.001251
+vn -0.949451 -0.312272 -0.032076
+vn -0.939019 -0.338459 -0.060733
+vn -0.990831 -0.134986 0.005646
+vn -0.989839 -0.142190 0.001251
+vn 0.957065 -0.289866 0.002228
+vn 0.950440 -0.308980 0.034579
+vn 0.992707 -0.120552 -0.000946
+vn 0.990875 -0.134469 -0.009247
+vn 0.949985 -0.312053 -0.012299
+vn 0.957065 -0.289866 0.002228
+vn 0.992243 -0.124275 0.003082
+vn 0.992707 -0.120552 -0.000946
+vn 0.985937 -0.163336 -0.035341
+vn 0.904830 -0.170541 -0.390126
+vn 0.990046 -0.140663 -0.004853
+vn 0.913199 -0.133217 -0.385123
+vn -0.909937 -0.184063 -0.371666
+vn -0.916742 -0.125557 -0.379234
+vn -0.385668 -0.101262 -0.917064
+vn -0.379990 -0.085209 -0.921058
+vn -0.990831 -0.134986 0.005646
+vn -0.992527 -0.122013 0.001740
+vn -0.949451 -0.312272 -0.032076
+vn -0.954279 -0.298907 0.002472
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn -0.745103 0.591285 -0.308552
+vn -0.916742 -0.125557 -0.379234
+vn -0.734685 0.678408 0.000000
+vn -0.991018 -0.133704 -0.002472
+vn -0.730909 0.682475 0.000000
+vn -0.730939 0.682443 -0.000031
+vn -0.992527 -0.122013 0.001740
+vn -0.992570 -0.121649 -0.002442
+vn -0.949874 -0.309738 0.042452
+vn -0.951202 -0.308125 0.016572
+vn -0.991137 -0.132636 -0.007416
+vn -0.992570 -0.121649 -0.002442
+vn -0.912094 -0.149238 0.381855
+vn -0.990001 -0.141058 -0.000305
+vn -0.902001 -0.203565 0.380730
+vn -0.984672 -0.174417 0.000336
+vn 0.000000 0.703021 0.711169
+vn 0.000000 -0.052279 0.998633
+vn 0.000000 0.702990 0.711200
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052339 0.998629
+vn 0.000000 -0.052370 0.998628
+vn 0.000000 0.702990 0.711200
+vn 0.000000 0.702975 0.711215
+vn 0.000000 -0.052309 0.998631
+vn 0.000000 -0.052279 0.998633
+vn -0.377613 -0.138923 0.915483
+vn -0.375319 -0.102543 0.921206
+vn 0.000000 -0.052370 0.998628
+vn 0.375234 -0.102697 0.921223
+vn 0.000000 0.702975 0.711215
+vn 0.329149 0.602020 0.727484
+vn -0.385668 -0.101262 -0.917064
+vn -0.379990 -0.085209 -0.921058
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.362879 -0.103340 -0.926089
+vn 0.372764 -0.088140 -0.923731
+vn -0.750227 0.586307 0.305621
+vn -0.912094 -0.149238 0.381855
+vn -0.329091 0.602117 0.727430
+vn -0.375319 -0.102543 0.921206
+vn 0.377487 -0.139135 0.915503
+vn 0.901970 -0.203534 0.380821
+vn 0.375234 -0.102697 0.921223
+vn 0.912059 -0.149299 0.381914
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.000000 -0.052309 -0.998631
+vn 0.984351 -0.176215 -0.001099
+vn 0.989641 -0.143562 -0.000641
+vn 0.901970 -0.203534 0.380821
+vn 0.912059 -0.149299 0.381914
+vn -0.991137 -0.132636 -0.007416
+vn -0.991018 -0.133704 -0.002472
+vn -0.949874 -0.309738 0.042452
+vn -0.939317 -0.333574 0.080082
+vn 0.362879 -0.103340 -0.926089
+vn 0.372764 -0.088140 -0.923731
+vn 0.904830 -0.170541 -0.390126
+vn 0.913199 -0.133217 -0.385123
+f 96/173/1 91/443/2 296/560/3
+f 296/560/3 91/443/2 293/381/4
+f 585/367/5 178/687/6 589/699/7
+f 589/699/7 178/687/6 124/80/8
+f 386/509/9 334/510/10 387/48/11
+f 387/48/11 334/510/10 335/178/12
+f 353/597/13 352/373/14 337/266/15
+f 337/266/15 352/373/14 343/631/16
+f 100/392/17 97/511/18 295/614/19
+f 295/614/19 97/511/18 292/726/20
+f 420/481/21 37/2/22 82/707/23
+f 82/707/23 37/2/22 38/423/24
+f 51/340/25 44/153/26 42/598/27
+f 42/598/27 44/153/26 43/692/28
+f 84/536/29 38/423/30 419/121/31
+f 419/121/31 38/423/30 37/2/32
+f 83/260/33 84/536/34 41/290/35
+f 41/290/35 84/536/34 419/121/36
+f 46/513/37 45/357/38 168/291/39
+f 168/291/39 45/357/38 152/292/40
+f 152/292/41 45/357/42 48/6/43
+f 48/6/43 45/357/42 49/505/44
+f 87/72/45 86/177/46 44/153/47
+f 44/153/47 86/177/46 43/692/48
+f 153/460/49 152/292/50 151/596/51
+f 151/596/51 152/292/50 48/6/52
+f 168/291/53 152/292/54 167/479/55
+f 167/479/55 152/292/54 153/460/56
+f 52/322/57 49/165/58 51/424/59
+f 47/228/60 48/136/61 49/165/62
+f 52/322/63 47/228/64 49/165/65
+f 50/489/66 51/424/67 42/189/68
+f 50/489/69 52/322/70 51/424/71
+f 50/489/72 42/189/73 39/120/74
+f 223/611/75 39/120/76 40/172/77
+f 50/489/78 39/120/79 223/611/80
+f 54/87/81 40/172/82 53/499/83
+f 223/611/84 40/172/85 54/87/86
+f 222/546/87 53/499/88 36/309/89
+f 54/87/90 53/499/91 222/546/92
+f 222/546/93 36/309/94 35/644/95
+f 55/405/96 35/644/97 56/331/98
+f 222/546/99 35/644/100 55/405/101
+f 120/355/102 429/179/103 119/682/104
+f 119/682/104 429/179/103 379/558/105
+f 55/405/106 56/331/107 57/602/108
+f 219/12/109 57/602/110 59/89/111
+f 64/388/112 63/429/113 58/603/114
+f 58/603/114 63/429/113 60/148/115
+f 55/405/116 57/602/117 219/12/118
+f 61/704/119 59/89/120 60/439/121
+f 219/12/122 59/89/123 61/704/124
+f 68/197/125 62/154/126 64/388/127
+f 64/388/127 62/154/126 63/429/128
+f 61/704/129 60/439/130 63/249/131
+f 65/615/132 63/249/133 62/629/134
+f 61/704/135 63/249/136 65/615/137
+f 67/105/138 62/629/139 69/244/140
+f 65/615/141 62/629/142 67/105/143
+f 76/725/144 69/635/145 68/197/146
+f 68/197/146 69/635/145 62/154/147
+f 67/105/148 69/244/149 66/393/150
+f 66/47/151 75/430/152 70/425/153
+f 70/425/153 75/430/152 285/382/154
+f 214/293/155 66/393/156 70/262/157
+f 67/105/158 66/393/159 214/293/160
+f 73/693/161 71/338/162 285/382/163
+f 285/382/163 71/338/162 70/425/164
+f 71/245/165 213/76/166 70/262/167
+f 73/693/168 285/382/169 173/123/170
+f 173/123/170 285/382/169 72/106/171
+f 166/52/172 130/708/173 128/356/174
+f 128/356/174 130/708/173 131/537/175
+f 213/76/176 71/245/177 128/694/178
+f 177/180/179 74/49/180 76/725/181
+f 76/725/181 74/49/180 75/430/182
+f 165/107/183 166/52/184 173/652/185
+f 173/652/185 166/52/184 73/703/186
+f 77/514/187 177/180/188 68/197/189
+f 68/197/189 177/180/188 76/725/190
+f 176/181/191 77/514/192 64/388/193
+f 64/388/193 77/514/192 68/197/194
+f 598/108/195 324/182/196 599/184/197
+f 599/184/197 324/182/196 371/183/198
+f 229/516/199 308/639/200 170/515/201
+f 170/515/201 308/639/200 307/103/202
+f 472/288/203 469/406/204 528/93/205
+f 528/93/205 469/406/204 525/672/206
+f 233/696/207 88/50/208 311/467/209
+f 311/467/209 88/50/208 305/201/210
+f 230/246/211 309/376/212 346/198/213
+f 346/198/213 309/376/212 345/150/214
+f 85/554/215 234/640/216 79/689/217
+f 79/689/217 234/640/216 312/204/218
+f 235/538/219 347/691/220 84/536/221
+f 84/536/221 347/691/220 38/423/222
+f 170/539/223 154/557/224 229/88/225
+f 229/88/225 154/557/224 155/94/226
+f 347/691/227 81/285/228 38/423/229
+f 38/423/229 81/285/228 82/707/230
+f 88/263/231 233/303/232 46/377/233
+f 46/377/233 233/303/232 87/72/234
+f 234/195/235 85/101/236 86/177/237
+f 86/177/237 85/101/236 83/260/238
+f 168/291/239 232/54/240 46/513/241
+f 46/513/241 232/54/240 88/445/242
+f 85/101/243 235/538/244 83/260/245
+f 83/260/245 235/538/244 84/536/246
+f 104/464/247 103/695/248 100/392/249
+f 100/392/249 103/695/248 101/482/250
+f 297/520/251 127/122/252 112/555/253
+f 112/555/253 127/122/252 111/264/254
+f 98/51/255 97/511/256 101/482/257
+f 101/482/257 97/511/256 100/392/258
+f 294/636/259 104/464/260 295/614/261
+f 295/614/261 104/464/260 100/392/262
+f 91/443/263 226/547/264 95/77/265
+f 95/77/265 226/547/264 92/196/266
+f 373/78/267 388/427/268 99/167/269
+f 99/167/269 388/427/268 107/374/270
+f 374/428/271 370/540/272 109/690/273
+f 109/690/273 370/540/272 89/541/274
+f 226/547/275 91/443/276 225/92/277
+f 225/92/277 91/443/276 96/173/278
+f 388/427/279 374/428/280 107/374/281
+f 107/374/281 374/428/280 109/690/282
+f 387/48/283 373/78/284 106/265/285
+f 106/265/285 373/78/284 99/167/286
+f 91/443/287 95/77/288 293/381/289
+f 293/381/289 95/77/288 291/304/290
+f 225/92/291 96/173/292 98/51/293
+f 98/51/293 96/173/292 97/511/294
+f 181/250/295 182/174/296 99/167/297
+f 99/167/297 182/174/296 106/265/298
+f 97/511/299 96/173/300 292/726/301
+f 292/726/301 96/173/300 296/560/302
+f 108/422/303 181/250/304 107/374/305
+f 107/374/305 181/250/304 99/167/306
+f 180/527/307 108/422/308 109/690/309
+f 109/690/309 108/422/308 107/374/310
+f 90/310/311 180/527/312 89/541/313
+f 89/541/313 180/527/312 109/690/314
+f 564/599/315 559/56/316 386/509/317
+f 386/509/317 559/56/316 334/510/318
+f 271/586/319 270/583/320 182/585/321
+f 182/585/321 270/583/320 105/341/322
+f 360/556/323 516/342/324 354/559/325
+f 354/559/325 516/342/324 515/507/326
+f 93/612/327 227/426/328 94/723/329
+f 94/723/329 227/426/328 117/124/330
+f 113/202/331 116/604/332 298/96/333
+f 298/96/333 116/604/332 115/461/334
+f 227/426/335 115/461/336 117/124/337
+f 117/124/337 115/461/336 116/604/338
+f 376/600/339 384/185/340 114/53/341
+f 114/53/341 384/185/340 110/199/342
+f 474/251/343 471/102/344 530/380/345
+f 530/380/345 471/102/344 527/632/346
+f 375/44/347 385/301/348 118/483/349
+f 118/483/349 385/301/348 123/253/350
+f 385/301/351 376/600/352 123/253/353
+f 123/253/353 376/600/352 114/53/354
+f 112/555/355 113/202/356 297/520/357
+f 297/520/357 113/202/356 298/96/358
+f 377/653/359 375/44/360 121/378/361
+f 121/378/361 375/44/360 118/483/362
+f 372/542/363 377/653/364 119/682/365
+f 119/682/365 377/653/364 121/378/366
+f 266/186/367 123/253/368 184/75/369
+f 184/75/369 123/253/368 114/53/370
+f 569/252/371 573/543/372 103/552/373
+f 103/552/373 573/543/372 302/3/374
+f 402/55/375 399/512/376 397/57/377
+f 397/57/377 399/512/376 394/407/378
+f 347/462/379 235/648/380 344/1/381
+f 344/1/381 235/648/380 313/155/382
+f 209/633/383 128/694/384 131/95/385
+f 209/633/386 213/76/387 128/694/388
+f 164/613/389 162/465/390 129/100/391
+f 129/100/391 162/465/390 135/110/392
+f 209/633/393 131/95/394 129/484/395
+f 132/176/396 129/484/397 135/156/398
+f 132/176/399 209/633/400 129/484/401
+f 133/411/402 135/156/403 137/286/404
+f 133/411/405 132/176/406 135/156/407
+f 159/408/408 134/446/409 161/375/410
+f 161/375/410 134/446/409 162/465/411
+f 133/411/412 137/286/413 136/544/414
+f 137/637/415 135/110/416 134/446/417
+f 134/446/417 135/110/416 162/465/418
+f 206/553/419 136/544/420 138/412/421
+f 206/553/422 133/411/423 136/544/424
+f 206/553/425 138/412/426 140/409/427
+f 430/372/428 137/637/429 160/74/430
+f 160/74/430 137/637/429 134/446/431
+f 202/545/432 140/409/433 139/688/434
+f 202/545/435 206/553/436 140/409/437
+f 202/545/438 139/688/439 141/709/440
+f 145/203/441 141/709/442 143/438/443
+f 145/203/444 202/545/445 141/709/446
+f 587/404/447 600/410/448 126/305/449
+f 126/305/449 600/410/448 125/37/450
+f 144/638/451 143/438/452 142/379/453
+f 144/638/454 145/203/455 143/438/456
+f 143/91/457 434/63/458 142/306/459
+f 144/638/460 142/379/461 146/104/462
+f 146/444/463 156/607/464 148/97/465
+f 148/97/465 156/607/464 389/673/466
+f 147/649/467 146/104/468 148/31/469
+f 147/649/470 144/638/471 146/104/472
+f 148/97/473 389/673/474 149/248/475
+f 149/248/475 389/673/474 157/32/476
+f 147/649/477 148/31/478 149/39/479
+f 157/32/480 153/460/481 149/248/482
+f 149/248/482 153/460/481 151/596/483
+f 150/582/484 149/39/485 151/490/486
+f 150/582/487 147/649/488 149/39/489
+f 42/598/490 43/692/491 39/299/492
+f 39/299/492 43/692/491 41/290/493
+f 47/228/494 151/490/495 48/136/496
+f 47/228/497 150/582/498 151/490/499
+f 167/479/500 153/460/501 154/557/502
+f 154/557/502 153/460/501 157/32/503
+f 86/177/504 83/260/505 43/692/506
+f 43/692/506 83/260/505 41/290/507
+f 277/151/508 278/616/509 413/525/510
+f 413/525/510 278/616/509 381/526/511
+f 595/323/512 590/663/513 412/41/514
+f 412/41/514 590/663/513 272/135/515
+f 413/525/516 283/175/517 418/320/518
+f 418/320/518 283/175/517 276/664/519
+f 74/49/520 72/106/521 75/430/522
+f 75/430/522 72/106/521 285/382/523
+f 232/54/524 168/291/525 169/298/526
+f 169/298/526 168/291/525 167/479/527
+f 169/298/528 167/479/529 170/539/530
+f 170/539/530 167/479/529 154/557/531
+f 229/88/532 155/94/533 346/98/534
+f 346/98/534 155/94/533 158/33/535
+f 172/276/536 230/330/537 158/33/538
+f 158/33/538 230/330/537 346/98/539
+f 583/267/540 587/404/541 228/643/542
+f 228/643/542 587/404/541 126/305/543
+f 179/205/544 121/378/545 122/34/546
+f 122/34/546 121/378/545 118/483/547
+f 120/355/548 119/682/549 179/205/550
+f 179/205/550 119/682/549 121/378/551
+f 224/706/552 186/324/553 185/674/554
+f 47/228/555 186/324/556 190/584/557
+f 52/322/558 186/324/559 47/228/560
+f 216/300/561 211/463/562 207/650/563
+f 207/650/563 211/463/562 208/79/564
+f 150/582/565 190/584/566 188/573/567
+f 47/228/568 190/584/569 150/582/570
+f 195/403/571 193/261/572 191/43/573
+f 191/43/573 193/261/572 192/227/574
+f 147/649/575 188/573/576 192/227/577
+f 150/582/578 188/573/579 147/649/580
+f 218/346/581 201/339/582 221/99/583
+f 221/99/583 201/339/582 199/35/584
+f 147/649/585 192/227/586 193/261/587
+f 221/99/588 199/35/589 220/64/590
+f 220/64/590 199/35/589 196/651/591
+f 144/638/592 193/261/593 194/454/594
+f 147/649/595 193/261/596 144/638/597
+f 205/335/598 203/466/599 218/346/600
+f 218/346/600 203/466/599 201/339/601
+f 144/638/602 194/454/603 198/601/604
+f 216/300/605 207/650/606 217/152/607
+f 217/152/607 207/650/606 204/20/608
+f 145/203/609 198/601/610 196/651/611
+f 144/638/612 198/601/613 145/203/614
+f 187/705/615 188/573/616 189/36/617
+f 189/36/617 188/573/616 190/584/618
+f 202/545/619 196/651/620 199/35/621
+f 145/203/622 196/651/623 202/545/624
+f 220/64/625 196/651/626 200/4/627
+f 200/4/627 196/651/626 198/601/628
+f 202/545/629 199/35/630 201/339/631
+f 197/675/632 194/454/633 195/403/634
+f 195/403/634 194/454/633 193/261/635
+f 206/553/636 201/339/637 203/466/638
+f 202/545/639 201/339/640 206/553/641
+f 133/411/642 203/466/643 204/20/644
+f 206/553/645 203/466/646 133/411/647
+f 200/4/648 198/601/649 197/675/650
+f 197/675/650 198/601/649 194/454/651
+f 133/411/652 204/20/653 207/650/654
+f 211/463/655 215/605/656 208/79/657
+f 208/79/657 215/605/656 210/354/658
+f 132/176/659 207/650/660 208/79/661
+f 133/411/662 207/650/663 132/176/664
+f 209/633/665 208/79/666 210/354/667
+f 132/176/668 208/79/669 209/633/670
+f 215/605/671 212/287/672 210/354/673
+f 209/633/674 210/354/675 213/76/676
+f 214/293/677 215/605/678 211/463/679
+f 213/76/680 214/293/681 70/262/682
+f 214/293/683 213/76/684 215/605/685
+f 67/105/686 211/463/687 216/300/688
+f 67/105/689 214/293/690 211/463/691
+f 67/105/692 216/300/693 217/152/694
+f 65/615/695 217/152/696 205/335/697
+f 65/615/698 67/105/699 217/152/700
+f 65/615/701 205/335/702 218/346/703
+f 61/704/704 218/346/705 221/99/706
+f 61/704/707 65/615/708 218/346/709
+f 219/12/710 221/99/711 220/64/712
+f 219/12/713 61/704/714 221/99/715
+f 219/12/716 220/64/717 200/4/718
+f 55/405/719 200/4/720 197/675/721
+f 55/405/722 219/12/723 200/4/724
+f 222/546/725 197/675/726 195/403/727
+f 222/546/728 55/405/729 197/675/730
+f 222/546/731 195/403/732 191/43/733
+f 54/87/734 222/546/735 191/43/736
+f 223/611/737 187/705/738 189/36/739
+f 223/611/740 54/87/741 187/705/742
+f 50/489/743 189/36/744 224/706/745
+f 50/489/746 223/611/747 189/36/748
+f 50/489/749 224/706/750 52/322/751
+f 34/38/752 371/183/753 498/289/754
+f 498/289/754 371/183/753 519/40/755
+f 170/515/756 307/103/757 169/149/758
+f 169/149/758 307/103/757 306/109/759
+f 567/297/760 127/634/761 554/247/762
+f 554/247/762 127/634/761 301/508/763
+f 304/337/764 101/157/765 302/3/766
+f 302/3/766 101/157/765 103/552/767
+f 553/302/768 105/341/769 571/134/770
+f 571/134/770 105/341/769 270/583/771
+f 175/662/772 503/42/773 272/135/774
+f 272/135/774 503/42/773 508/321/775
+f 182/174/776 105/284/777 106/265/778
+f 106/265/778 105/284/777 102/125/779
+f 169/149/780 306/109/781 232/336/782
+f 232/336/782 306/109/781 310/200/783
+f 582/480/784 82/707/785 581/5/786
+f 581/5/786 82/707/785 81/285/787
+f 573/543/788 558/641/789 302/3/790
+f 302/3/790 558/641/789 316/343/791
+f 267/229/792 183/587/793 570/665/794
+f 570/665/794 183/587/793 566/491/795
+f 235/648/796 85/554/797 313/155/798
+f 313/155/798 85/554/797 79/689/799
+f 254/115/800 260/81/801 255/7/802
+f 255/7/802 260/81/801 258/311/803
+f 255/7/804 258/311/805 251/698/806
+f 251/698/806 258/311/805 259/394/807
+f 263/82/808 260/81/809 253/158/810
+f 253/158/810 260/81/809 254/115/811
+f 264/83/812 263/82/813 252/208/814
+f 252/208/814 263/82/813 253/158/815
+f 251/698/816 259/394/817 250/161/818
+f 250/161/818 259/394/817 262/193/819
+f 248/171/820 243/504/821 246/574/822
+f 244/421/823 243/504/824 249/468/825
+f 249/468/825 243/504/824 248/171/826
+f 242/395/827 247/722/828 243/504/829
+f 243/504/829 247/722/828 246/574/830
+f 244/421/831 286/528/832 245/469/833
+f 245/469/833 286/528/832 427/8/834
+f 265/70/835 257/283/836 436/485/837
+f 123/253/838 266/186/839 118/483/840
+f 118/483/840 266/186/839 122/34/841
+f 110/199/842 183/710/843 114/53/844
+f 114/53/844 183/710/843 184/75/845
+f 580/455/846 422/472/847 557/194/848
+f 557/194/848 422/472/847 287/561/849
+f 183/587/850 267/229/851 184/588/852
+f 184/588/852 267/229/851 268/589/853
+f 175/239/854 34/38/855 503/344/856
+f 503/344/856 34/38/855 498/289/857
+f 382/590/858 383/277/859 271/586/860
+f 271/586/860 383/277/859 270/583/861
+f 184/588/862 268/589/863 266/591/864
+f 122/230/865 266/591/864 268/589/863
+f 122/230/866 268/589/867 179/231/868
+f 179/231/868 268/589/867 120/628/869
+f 182/585/870 181/16/871 271/586/872
+f 181/16/871 108/232/873 271/586/872
+f 90/233/874 271/586/875 180/112/876
+f 180/112/876 271/586/875 108/232/877
+f 429/126/878 120/628/879 382/590/880
+f 382/590/880 120/628/879 268/589/881
+f 174/617/882 165/358/883 282/359/884
+f 282/359/884 165/358/883 280/486/885
+f 74/618/886 177/345/887 274/654/888
+f 274/654/888 177/345/887 284/399/889
+f 159/413/890 161/666/891 277/151/892
+f 277/151/892 161/666/891 278/616/893
+f 176/332/894 283/175/895 77/21/896
+f 77/21/896 283/175/895 275/127/897
+f 165/358/898 173/17/899 280/486/900
+f 280/486/900 173/17/899 281/333/901
+f 177/345/902 77/21/903 284/399/904
+f 284/399/904 77/21/903 275/127/905
+f 176/332/906 78/608/907 283/175/908
+f 283/175/908 78/608/907 276/664/909
+f 74/618/910 274/654/911 72/655/912
+f 72/655/912 274/654/911 273/656/913
+f 174/617/914 282/359/915 163/312/916
+f 163/312/916 282/359/915 279/487/917
+f 72/655/918 273/656/919 173/17/920
+f 173/17/920 273/656/919 281/333/921
+f 161/666/922 163/312/923 278/616/924
+f 278/616/924 163/312/923 279/487/925
+f 601/137/926 269/325/927 585/492/928
+f 585/492/928 269/325/927 178/326/929
+f 175/662/930 272/135/931 586/493/932
+f 586/493/932 272/135/931 590/663/933
+f 128/356/934 71/243/935 166/52/936
+f 166/52/936 71/243/935 73/703/937
+f 174/206/938 130/708/939 165/107/940
+f 165/107/940 130/708/939 166/52/941
+f 113/202/942 112/555/943 288/447/944
+f 288/447/944 112/555/943 290/702/945
+f 116/604/946 113/202/947 289/619/948
+f 289/619/948 113/202/947 288/447/949
+f 112/555/950 111/264/951 290/702/952
+f 290/702/952 111/264/951 287/561/953
+f 287/561/954 111/264/955 557/194/956
+f 557/194/956 111/264/955 568/347/957
+f 363/313/958 294/636/959 364/278/960
+f 364/278/960 294/636/959 295/614/961
+f 400/169/962 402/55/963 395/163/964
+f 395/163/964 402/55/963 397/57/965
+f 314/419/966 315/128/967 94/723/968
+f 94/723/968 315/128/967 93/612/969
+f 127/634/970 297/521/971 301/508/972
+f 301/508/972 297/521/971 299/645/973
+f 318/676/974 304/337/975 316/343/976
+f 316/343/976 304/337/975 302/3/977
+f 101/157/978 304/337/979 98/606/980
+f 98/606/980 304/337/979 225/129/981
+f 92/226/982 226/327/983 304/337/984
+f 304/337/984 226/327/983 225/129/985
+f 297/521/986 298/431/987 299/645/988
+f 298/431/987 115/569/989 299/645/988
+f 115/569/990 227/476/991 299/645/992
+f 299/645/992 227/476/991 93/548/993
+f 315/236/994 318/676/995 93/548/996
+f 93/548/996 318/676/995 299/645/997
+f 234/640/998 233/696/999 312/204/1000
+f 312/204/1000 233/696/999 311/467/1001
+f 232/336/1002 310/200/1003 88/50/1004
+f 88/50/1004 310/200/1003 305/201/1005
+f 309/376/1006 230/246/1007 591/442/1008
+f 591/442/1008 230/246/1007 583/142/1009
+f 592/434/1010 303/168/1011 581/271/1012
+f 581/271/1012 303/168/1011 231/143/1013
+f 319/147/1014 309/376/1015 593/565/1016
+f 593/565/1016 309/376/1015 591/442/1017
+f 320/23/1018 308/639/1019 348/701/1020
+f 348/701/1020 308/639/1019 345/150/1021
+f 321/164/1022 307/103/1023 320/23/1024
+f 320/23/1024 307/103/1023 308/639/1025
+f 322/571/1026 306/109/1027 321/164/1028
+f 321/164/1028 306/109/1027 307/103/1029
+f 79/689/1030 312/204/1031 321/164/1032
+f 321/164/1032 312/204/1031 322/571/1033
+f 313/155/1034 79/689/1035 320/23/1036
+f 320/23/1036 79/689/1035 321/164/1037
+f 344/1/1038 313/155/1039 348/701/1040
+f 348/701/1040 313/155/1039 320/23/1041
+f 592/434/1042 171/307/1043 593/565/1044
+f 593/565/1044 171/307/1043 319/147/1045
+f 304/337/1046 318/676/1047 92/226/1048
+f 92/226/1048 318/676/1047 315/236/1049
+f 301/508/1050 299/645/1051 316/343/1052
+f 316/343/1052 299/645/1051 318/676/1053
+f 95/77/1054 92/196/1055 314/419/1056
+f 314/419/1056 92/196/1055 315/128/1057
+f 306/109/1058 322/571/1059 310/200/1060
+f 310/200/1060 322/571/1059 305/201/1061
+f 312/204/1062 311/467/1063 322/571/1064
+f 322/571/1064 311/467/1063 305/201/1065
+f 154/557/1066 157/32/1067 155/94/1068
+f 155/94/1068 157/32/1067 389/673/1069
+f 474/256/1070 530/141/1071 475/353/1072
+f 475/353/1072 530/141/1071 531/84/1073
+f 217/152/1074 204/20/1075 205/335/1076
+f 205/335/1076 204/20/1075 203/466/1077
+f 224/706/1078 189/36/1079 186/324/1080
+f 186/324/1080 189/36/1079 190/584/1081
+f 52/322/1082 185/674/1083 186/324/1084
+f 52/322/1085 224/706/1086 185/674/1087
+f 213/76/1088 210/354/1089 212/287/1090
+f 213/76/1091 212/287/1092 215/605/1093
+f 130/708/1094 164/613/1095 131/537/1096
+f 131/537/1096 164/613/1095 129/100/1097
+f 163/215/1098 164/613/1099 174/206/1100
+f 174/206/1100 164/613/1099 130/708/1101
+f 161/375/1102 162/465/1103 163/215/1104
+f 163/215/1104 162/465/1103 164/613/1105
+f 75/430/1106 66/47/1107 76/725/1108
+f 76/725/1108 66/47/1107 69/635/1109
+f 191/43/1110 192/227/1111 187/705/1112
+f 187/705/1112 192/227/1111 188/573/1113
+f 54/87/1114 191/43/1115 187/705/1116
+f 49/308/1117 45/471/1118 51/340/1119
+f 51/340/1119 45/471/1118 44/153/1120
+f 401/575/1121 400/169/1122 396/711/1123
+f 396/711/1123 400/169/1122 395/163/1124
+f 563/368/1125 572/498/1126 363/313/1127
+f 363/313/1127 572/498/1126 294/636/1128
+f 395/163/1129 397/57/1130 333/216/1131
+f 333/216/1131 397/57/1130 330/360/1132
+f 396/711/1133 395/163/1134 329/712/1135
+f 329/712/1135 395/163/1134 333/216/1136
+f 397/57/1137 394/407/1138 330/360/1139
+f 330/360/1139 394/407/1138 332/361/1140
+f 342/334/1141 337/266/1142 335/178/1143
+f 335/178/1143 337/266/1142 327/697/1144
+f 343/631/1145 339/362/1146 336/576/1147
+f 336/576/1147 339/362/1146 328/268/1148
+f 339/362/1149 340/22/1150 328/268/1151
+f 328/268/1151 340/22/1150 323/217/1152
+f 337/266/1153 343/631/1154 327/697/1155
+f 327/697/1155 343/631/1154 336/576/1156
+f 352/373/1157 355/24/1158 343/631/1159
+f 343/631/1159 355/24/1158 339/362/1160
+f 351/58/1161 353/597/1162 342/334/1163
+f 342/334/1163 353/597/1162 337/266/1164
+f 334/510/1165 341/534/1166 335/178/1167
+f 335/178/1167 341/534/1166 342/334/1168
+f 576/116/1169 384/185/1170 574/414/1171
+f 574/414/1171 384/185/1170 332/361/1172
+f 582/480/1173 80/724/1174 588/25/1175
+f 588/25/1175 80/724/1174 33/363/1176
+f 598/108/1177 597/295/1178 324/182/1179
+f 324/182/1179 597/295/1178 338/370/1180
+f 596/61/1181 597/295/1182 56/364/1183
+f 56/364/1183 597/295/1182 57/488/1184
+f 233/303/1185 234/195/1186 87/72/1187
+f 87/72/1187 234/195/1186 86/177/1188
+f 46/377/1189 87/72/1190 45/471/1191
+f 45/471/1191 87/72/1190 44/153/1192
+f 171/307/1193 344/1/1194 319/147/1195
+f 319/147/1195 344/1/1194 348/701/1196
+f 348/701/1197 345/150/1198 319/147/1199
+f 319/147/1199 345/150/1198 309/376/1200
+f 81/73/1201 347/462/1202 171/307/1203
+f 171/307/1203 347/462/1202 344/1/1204
+f 346/198/1205 345/150/1206 229/516/1207
+f 229/516/1207 345/150/1206 308/639/1208
+f 40/562/1209 39/299/1210 419/121/1211
+f 419/121/1211 39/299/1210 41/290/1212
+f 267/229/1213 383/277/1214 268/589/1215
+f 268/589/1215 383/277/1214 382/590/1216
+f 284/399/1217 378/630/1218 274/654/1219
+f 274/654/1219 378/630/1218 349/716/1220
+f 584/684/1221 58/603/1222 599/184/1223
+f 599/184/1223 58/603/1222 60/148/1224
+f 357/391/1225 359/660/1226 351/58/1227
+f 351/58/1227 359/660/1226 353/597/1228
+f 358/622/1229 361/529/1230 352/373/1231
+f 352/373/1231 361/529/1230 355/24/1232
+f 341/534/1233 350/272/1234 342/334/1235
+f 342/334/1235 350/272/1234 351/58/1236
+f 559/56/1237 560/478/1238 334/510/1239
+f 334/510/1239 560/478/1238 341/534/1240
+f 359/660/1241 358/622/1242 353/597/1243
+f 353/597/1243 358/622/1242 352/373/1244
+f 366/420/1245 365/570/1246 359/660/1247
+f 359/660/1247 365/570/1246 358/622/1248
+f 370/540/1249 380/46/1250 89/541/1251
+f 89/541/1251 380/46/1250 379/558/1252
+f 365/570/1253 368/224/1254 358/622/1255
+f 358/622/1255 368/224/1254 361/529/1256
+f 364/278/1257 366/420/1258 357/391/1259
+f 357/391/1259 366/420/1258 359/660/1260
+f 350/272/1261 356/623/1262 351/58/1263
+f 351/58/1263 356/623/1262 357/391/1264
+f 560/478/1265 561/620/1266 341/534/1267
+f 341/534/1267 561/620/1266 350/272/1268
+f 295/614/1269 292/726/1270 364/278/1271
+f 364/278/1271 292/726/1270 366/420/1272
+f 296/560/1273 293/381/1274 365/570/1275
+f 365/570/1275 293/381/1274 368/224/1276
+f 292/726/1277 296/560/1278 366/420/1279
+f 366/420/1279 296/560/1278 365/570/1280
+f 356/623/1281 363/313/1282 357/391/1283
+f 357/391/1283 363/313/1282 364/278/1284
+f 561/620/1285 562/642/1286 350/272/1287
+f 350/272/1287 562/642/1286 356/623/1288
+f 562/642/1289 563/368/1290 356/623/1291
+f 356/623/1291 563/368/1290 363/313/1292
+f 378/630/1293 381/526/1294 279/487/1295
+f 279/487/1295 381/526/1294 278/616/1296
+f 392/328/1297 277/151/1298 418/320/1299
+f 418/320/1299 277/151/1298 413/525/1300
+f 370/540/1301 323/217/1302 380/46/1303
+f 380/46/1303 323/217/1302 237/190/1304
+f 595/323/1305 412/41/1306 601/137/1307
+f 601/137/1307 412/41/1306 269/325/1308
+f 372/542/1309 380/46/1310 326/389/1311
+f 326/389/1311 380/46/1310 237/190/1312
+f 326/389/1313 331/212/1314 372/542/1315
+f 372/542/1315 331/212/1314 377/653/1316
+f 331/212/1317 329/712/1318 377/653/1319
+f 377/653/1319 329/712/1318 375/44/1320
+f 333/216/1321 330/360/1322 385/301/1323
+f 385/301/1323 330/360/1322 376/600/1324
+f 329/712/1325 333/216/1326 375/44/1327
+f 375/44/1327 333/216/1326 385/301/1328
+f 330/360/1329 332/361/1330 376/600/1331
+f 376/600/1331 332/361/1330 384/185/1332
+f 335/178/1333 327/697/1334 387/48/1335
+f 387/48/1335 327/697/1334 373/78/1336
+f 336/576/1337 328/268/1338 388/427/1339
+f 388/427/1339 328/268/1338 374/428/1340
+f 328/268/1341 323/217/1342 374/428/1343
+f 374/428/1343 323/217/1342 370/540/1344
+f 327/697/1345 336/576/1346 373/78/1347
+f 373/78/1347 336/576/1346 388/427/1348
+f 102/125/1349 386/509/1350 106/265/1351
+f 106/265/1351 386/509/1350 387/48/1352
+f 565/610/1353 110/199/1354 576/116/1355
+f 576/116/1355 110/199/1354 384/185/1356
+f 360/556/1357 354/559/1358 594/140/1359
+f 594/140/1359 354/559/1358 596/61/1360
+f 586/209/1361 584/684/1362 175/239/1363
+f 175/239/1363 584/684/1362 34/38/1364
+f 602/387/1365 589/699/1366 369/657/1367
+f 369/657/1367 589/699/1366 124/80/1368
+f 155/94/1369 389/673/1370 158/33/1371
+f 158/33/1371 389/673/1370 156/607/1372
+f 390/26/1373 172/276/1374 156/607/1375
+f 156/607/1375 172/276/1374 158/33/1376
+f 277/151/1377 392/328/1378 159/413/1379
+f 159/413/1379 392/328/1378 391/90/1380
+f 160/74/1381 134/446/1382 391/532/1383
+f 391/532/1383 134/446/1382 159/408/1384
+f 78/609/1385 176/181/1386 58/603/1387
+f 58/603/1387 176/181/1386 64/388/1388
+f 137/637/1389 430/372/1390 136/677/1391
+f 555/530/1392 564/599/1393 102/125/1394
+f 102/125/1394 564/599/1393 386/509/1395
+f 407/500/1396 406/192/1397 401/575/1398
+f 401/575/1398 406/192/1397 400/169/1399
+f 406/192/1400 408/448/1401 400/169/1402
+f 400/169/1402 408/448/1401 402/55/1403
+f 574/414/1404 332/361/1405 577/566/1406
+f 577/566/1406 332/361/1405 394/407/1407
+f 408/448/1408 405/456/1409 402/55/1410
+f 402/55/1410 405/456/1409 399/512/1411
+f 425/269/1412 422/472/1413 408/448/1414
+f 408/448/1414 422/472/1413 405/456/1415
+f 423/577/1416 425/269/1417 406/192/1418
+f 406/192/1418 425/269/1417 408/448/1419
+f 424/433/1420 423/577/1421 407/500/1422
+f 407/500/1422 423/577/1421 406/192/1423
+f 577/566/1424 394/407/1425 578/680/1426
+f 578/680/1426 394/407/1425 399/512/1427
+f 470/329/1428 479/494/1429 526/275/1430
+f 526/275/1430 479/494/1429 535/495/1431
+f 245/469/1432 427/8/1433 403/626/1434
+f 403/626/1434 427/8/1433 414/296/1435
+f 429/126/1436 382/590/1437 90/233/1438
+f 90/233/1438 382/590/1437 271/586/1439
+f 331/212/1440 326/389/1441 411/146/1442
+f 411/146/1442 326/389/1441 410/518/1443
+f 411/146/1444 410/518/1445 409/721/1446
+f 409/721/1446 410/518/1445 416/661/1447
+f 94/723/1448 117/124/1449 286/528/1450
+f 286/528/1450 117/124/1449 417/254/1451
+f 117/124/1452 116/604/1453 417/254/1454
+f 417/254/1454 116/604/1453 289/619/1455
+f 606/27/1456 604/415/1457 398/314/1458
+f 398/314/1458 604/415/1457 393/450/1459
+f 396/711/1460 329/712/1461 411/146/1462
+f 411/146/1462 329/712/1461 331/212/1463
+f 283/175/1464 413/525/1465 275/127/1466
+f 275/127/1466 413/525/1465 381/526/1467
+f 604/415/1468 605/715/1469 393/450/1470
+f 393/450/1470 605/715/1469 325/225/1471
+f 426/671/1472 424/433/1473 415/315/1474
+f 415/315/1474 424/433/1473 407/500/1475
+f 426/671/1476 427/8/1477 417/254/1478
+f 417/254/1478 427/8/1477 286/528/1479
+f 409/721/1480 416/661/1481 415/315/1482
+f 415/315/1482 416/661/1481 414/296/1483
+f 401/575/1484 396/711/1485 409/721/1486
+f 409/721/1486 396/711/1485 411/146/1487
+f 415/315/1488 407/500/1489 409/721/1490
+f 409/721/1490 407/500/1489 401/575/1491
+f 244/421/1492 249/468/1493 286/528/1494
+f 286/528/1494 249/468/1493 250/161/1495
+f 340/22/1496 239/218/1497 323/217/1498
+f 323/217/1498 239/218/1497 237/190/1499
+f 416/661/1500 410/518/1501 236/211/1502
+f 236/211/1502 410/518/1501 240/234/1503
+f 419/121/1504 37/2/1505 40/562/1506
+f 40/562/1506 37/2/1505 53/10/1507
+f 37/2/1508 420/481/1509 53/10/1510
+f 53/10/1510 420/481/1509 36/71/1511
+f 594/140/1512 607/15/1513 360/556/1514
+f 360/556/1514 607/15/1513 367/59/1515
+f 588/25/1516 607/15/1517 420/481/1518
+f 420/481/1518 607/15/1517 36/71/1519
+f 237/190/1520 240/234/1521 326/389/1522
+f 326/389/1522 240/234/1521 410/518/1523
+f 429/179/1524 90/310/1525 379/558/1526
+f 379/558/1526 90/310/1525 89/541/1527
+f 414/296/1528 416/661/1529 403/626/1530
+f 403/626/1530 416/661/1529 236/211/1531
+f 415/315/1532 414/296/1533 426/671/1534
+f 426/671/1534 414/296/1533 427/8/1535
+f 417/254/1536 289/619/1537 426/671/1538
+f 426/671/1538 289/619/1537 424/433/1539
+f 289/619/1540 288/447/1541 424/433/1542
+f 424/433/1542 288/447/1541 423/577/1543
+f 288/447/1544 290/702/1545 423/577/1546
+f 423/577/1546 290/702/1545 425/269/1547
+f 290/702/1548 287/561/1549 425/269/1550
+f 425/269/1550 287/561/1549 422/472/1551
+f 578/680/1552 399/512/1553 579/144/1554
+f 579/144/1554 399/512/1553 405/456/1555
+f 252/208/1556 247/722/1557 291/304/1558
+f 291/304/1558 247/722/1557 242/395/1559
+f 378/630/1560 284/399/1561 381/526/1562
+f 381/526/1562 284/399/1561 275/127/1563
+f 349/716/1564 378/630/1565 282/359/1566
+f 282/359/1566 378/630/1565 279/487/1567
+f 380/46/1568 372/542/1569 379/558/1570
+f 379/558/1570 372/542/1569 119/682/1571
+f 362/294/1572 431/133/1573 428/522/1574
+f 428/522/1574 431/133/1573 238/316/1575
+f 241/119/1576 431/133/1577 432/625/1578
+f 432/625/1578 431/133/1577 362/294/1579
+f 355/24/1580 428/522/1581 339/362/1582
+f 339/362/1582 428/522/1581 340/22/1583
+f 361/529/1584 362/294/1585 355/24/1586
+f 355/24/1586 362/294/1585 428/522/1587
+f 368/224/1588 432/625/1589 361/529/1590
+f 361/529/1590 432/625/1589 362/294/1591
+f 293/381/1592 291/304/1593 368/224/1594
+f 368/224/1594 291/304/1593 432/625/1595
+f 239/218/1596 340/22/1597 238/316/1598
+f 238/316/1598 340/22/1597 428/522/1599
+f 242/395/1600 241/119/1601 291/304/1602
+f 291/304/1602 241/119/1601 432/625/1603
+f 138/473/1604 136/677/1605 430/372/1606
+f 140/113/1607 138/473/1608 430/372/1609
+f 602/387/1610 605/715/1611 430/372/1612
+f 430/372/1612 605/715/1611 140/113/1613
+f 606/27/1614 398/314/1615 603/646/1616
+f 603/646/1616 398/314/1615 404/501/1617
+f 603/646/1618 404/501/1619 608/159/1620
+f 608/159/1620 404/501/1619 421/348/1621
+f 600/410/1622 390/26/1623 608/159/1624
+f 608/159/1624 390/26/1623 434/63/1625
+f 146/444/1626 434/63/1627 156/607/1628
+f 156/607/1628 434/63/1627 390/26/1629
+f 146/444/1630 142/306/1631 434/63/1632
+f 274/654/1633 349/716/1634 273/656/1635
+f 273/656/1635 349/716/1634 281/333/1636
+f 282/359/1637 280/486/1638 349/716/1639
+f 349/716/1639 280/486/1638 281/333/1640
+f 433/60/1641 436/485/1642 256/365/1643
+f 256/365/1643 436/485/1642 257/283/1644
+f 262/193/1645 261/658/1646 435/242/1647
+f 264/83/1648 433/60/1649 256/365/1650
+f 252/208/1651 291/304/1652 264/83/1653
+f 264/83/1653 291/304/1652 433/60/1654
+f 435/242/1655 286/528/1656 262/193/1657
+f 262/193/1657 286/528/1656 250/161/1658
+f 94/723/1659 286/528/1660 435/242/1661
+f 291/304/1662 95/77/1663 433/60/1664
+f 95/77/1665 314/419/1666 433/60/1667
+f 433/60/1667 314/419/1666 436/485/1668
+f 94/723/1669 435/242/1670 314/419/1671
+f 314/419/1671 435/242/1670 436/485/1672
+f 265/70/1673 436/485/1674 261/658/1675
+f 261/658/1675 436/485/1674 435/242/1676
+f 27/578/1677 241/119/1678 26/383/1679
+f 26/383/1679 241/119/1678 242/395/1680
+f 26/383/1681 242/395/1682 25/130/1683
+f 25/130/1683 242/395/1682 243/504/1684
+f 25/130/1685 243/504/1686 24/535/1687
+f 24/535/1687 243/504/1686 244/421/1688
+f 24/535/1689 244/421/1690 23/219/1691
+f 23/219/1691 244/421/1690 245/469/1692
+f 23/219/1693 245/469/1694 22/685/1695
+f 22/685/1695 245/469/1694 403/626/1696
+f 22/685/1697 403/626/1698 32/579/1699
+f 32/579/1699 403/626/1698 236/211/1700
+f 32/579/1701 236/211/1702 28/531/1703
+f 28/531/1703 236/211/1702 240/234/1704
+f 28/531/1705 240/234/1706 31/351/1707
+f 31/351/1707 240/234/1706 237/190/1708
+f 31/351/1709 237/190/1710 29/9/1711
+f 29/9/1711 237/190/1710 239/218/1712
+f 29/9/1713 239/218/1714 30/317/1715
+f 30/317/1715 239/218/1714 238/316/1716
+f 30/317/1717 238/316/1718 21/281/1719
+f 21/281/1719 238/316/1718 431/133/1720
+f 21/281/1721 431/133/1722 27/578/1723
+f 27/578/1723 431/133/1722 241/119/1724
+f 29/9/1725 30/317/1726 445/386/1727
+f 445/386/1727 30/317/1726 446/318/1728
+f 32/579/1729 28/531/1730 448/371/1731
+f 448/371/1731 28/531/1730 444/11/1732
+f 31/351/1733 29/9/1734 447/449/1735
+f 447/449/1735 29/9/1734 445/386/1736
+f 24/535/1737 23/219/1738 440/523/1739
+f 440/523/1739 23/219/1738 439/188/1740
+f 26/383/1741 25/130/1742 442/238/1743
+f 442/238/1743 25/130/1742 441/436/1744
+f 21/281/1745 27/578/1746 437/390/1747
+f 437/390/1747 27/578/1746 443/594/1748
+f 23/219/1749 22/685/1750 439/188/1751
+f 439/188/1751 22/685/1750 438/220/1752
+f 22/685/1753 32/579/1754 438/220/1755
+f 438/220/1755 32/579/1754 448/371/1756
+f 28/531/1757 31/351/1758 444/11/1759
+f 444/11/1759 31/351/1758 447/449/1760
+f 27/578/1761 26/383/1762 443/594/1763
+f 443/594/1763 26/383/1762 442/238/1764
+f 30/317/1765 21/281/1766 446/318/1767
+f 446/318/1767 21/281/1766 437/390/1768
+f 25/130/1769 24/535/1770 441/436/1771
+f 441/436/1771 24/535/1770 440/523/1772
+f 18/396/1773 253/158/1774 19/451/1775
+f 19/451/1775 253/158/1774 254/115/1776
+f 19/451/1777 254/115/1778 20/397/1779
+f 20/397/1779 254/115/1778 255/7/1780
+f 20/397/1781 255/7/1782 16/280/1783
+f 16/280/1783 255/7/1782 251/698/1784
+f 16/280/1785 251/698/1786 15/240/1787
+f 15/240/1787 251/698/1786 250/161/1788
+f 15/240/1789 250/161/1790 14/86/1791
+f 14/86/1791 250/161/1790 249/468/1792
+f 14/86/1793 249/468/1794 13/28/1795
+f 13/28/1795 249/468/1794 248/171/1796
+f 13/28/1797 248/171/1798 11/19/1799
+f 11/19/1799 248/171/1798 246/574/1800
+f 11/19/1801 246/574/1802 12/624/1803
+f 12/624/1803 246/574/1802 247/722/1804
+f 12/624/1805 247/722/1806 17/319/1807
+f 17/319/1807 247/722/1806 252/208/1808
+f 17/319/1809 252/208/1810 18/396/1811
+f 18/396/1811 252/208/1810 253/158/1812
+f 1/274/1813 265/70/1814 5/681/1815
+f 5/681/1815 265/70/1814 261/658/1816
+f 5/681/1817 261/658/1818 4/67/1819
+f 4/67/1819 261/658/1818 262/193/1820
+f 4/67/1821 262/193/1822 7/29/1823
+f 7/29/1823 262/193/1822 259/394/1824
+f 7/29/1825 259/394/1826 8/398/1827
+f 8/398/1827 259/394/1826 258/311/1828
+f 8/398/1829 258/311/1830 6/62/1831
+f 6/62/1831 258/311/1830 260/81/1832
+f 6/62/1833 260/81/1834 3/270/1835
+f 3/270/1835 260/81/1834 263/82/1836
+f 3/270/1837 263/82/1838 2/117/1839
+f 2/117/1839 263/82/1838 264/83/1840
+f 2/117/1841 264/83/1842 10/477/1843
+f 10/477/1843 264/83/1842 256/365/1844
+f 10/477/1845 256/365/1846 9/214/1847
+f 9/214/1847 256/365/1846 257/283/1848
+f 9/214/1849 257/283/1850 1/274/1851
+f 1/274/1851 257/283/1850 265/70/1852
+f 4/67/1853 7/29/1854 452/496/1855
+f 452/496/1855 7/29/1854 455/258/1856
+f 7/29/1857 8/398/1858 455/258/1859
+f 455/258/1859 8/398/1858 456/160/1860
+f 10/477/1861 9/214/1862 458/506/1863
+f 458/506/1863 9/214/1862 457/111/1864
+f 5/681/1865 4/67/1866 453/221/1867
+f 453/221/1867 4/67/1866 452/496/1868
+f 9/214/1869 1/274/1870 457/111/1871
+f 457/111/1871 1/274/1870 449/627/1872
+f 8/398/1873 6/62/1874 456/160/1875
+f 456/160/1875 6/62/1874 454/621/1876
+f 3/270/1877 2/117/1878 451/717/1879
+f 451/717/1879 2/117/1878 450/257/1880
+f 6/62/1881 3/270/1882 454/621/1883
+f 454/621/1883 3/270/1882 451/717/1884
+f 2/117/1885 10/477/1886 450/257/1887
+f 450/257/1887 10/477/1886 458/506/1888
+f 1/274/1889 5/681/1890 449/627/1891
+f 449/627/1891 5/681/1890 453/221/1892
+f 14/86/1893 13/28/1894 462/282/1895
+f 462/282/1895 13/28/1894 461/145/1896
+f 13/28/1897 11/19/1898 461/145/1899
+f 461/145/1899 11/19/1898 459/533/1900
+f 15/240/1901 14/86/1902 463/457/1903
+f 463/457/1903 14/86/1902 462/282/1904
+f 17/319/1905 18/396/1906 465/366/1907
+f 465/366/1907 18/396/1906 466/384/1908
+f 11/19/1909 12/624/1910 459/533/1911
+f 459/533/1911 12/624/1910 460/470/1912
+f 12/624/1913 17/319/1914 460/470/1915
+f 460/470/1915 17/319/1914 465/366/1916
+f 20/397/1917 16/280/1918 468/255/1919
+f 468/255/1919 16/280/1918 464/564/1920
+f 19/451/1921 20/397/1922 467/416/1923
+f 467/416/1923 20/397/1922 468/255/1924
+f 18/396/1925 19/451/1926 466/384/1927
+f 466/384/1927 19/451/1926 467/416/1928
+f 16/280/1929 15/240/1930 464/564/1931
+f 464/564/1931 15/240/1930 463/457/1932
+f 354/559/1933 515/507/1934 338/370/1935
+f 338/370/1935 515/507/1934 514/131/1936
+f 579/144/1937 405/456/1938 580/455/1939
+f 580/455/1939 405/456/1938 422/472/1940
+f 33/363/1941 497/170/1942 367/59/1943
+f 367/59/1943 497/170/1942 517/14/1944
+f 325/225/1945 369/657/1946 513/352/1947
+f 513/352/1947 369/657/1946 518/713/1948
+f 272/135/1949 508/321/1950 412/41/1951
+f 412/41/1951 508/321/1950 523/138/1952
+f 482/581/1953 538/385/1954 483/595/1955
+f 483/595/1955 538/385/1954 539/18/1956
+f 178/326/1957 269/325/1958 504/162/1959
+f 504/162/1959 269/325/1958 507/497/1960
+f 231/143/1961 303/168/1962 506/235/1963
+f 506/235/1963 303/168/1962 510/85/1964
+f 228/458/1965 505/502/1966 300/118/1967
+f 300/118/1967 505/502/1966 509/459/1968
+f 470/453/1969 526/432/1970 473/719/1971
+f 473/719/1971 526/432/1970 529/259/1972
+f 472/139/1973 528/667/1974 478/241/1975
+f 478/241/1975 528/667/1974 534/210/1976
+f 269/325/1977 412/41/1978 507/497/1979
+f 507/497/1979 412/41/1978 523/138/1980
+f 571/134/1981 575/668/1982 479/494/1983
+f 479/494/1983 575/668/1982 487/669/1984
+f 125/37/1985 421/348/1986 501/114/1987
+f 501/114/1987 421/348/1986 524/567/1988
+f 572/498/1989 481/400/1990 556/68/1991
+f 556/68/1991 481/400/1990 477/401/1992
+f 480/349/1993 536/350/1994 496/568/1995
+f 496/568/1995 536/350/1994 552/474/1996
+f 338/370/1997 514/131/1998 324/182/1999
+f 324/182/1999 514/131/1998 512/572/2000
+f 475/353/2001 531/84/2002 480/349/2003
+f 480/349/2003 531/84/2002 536/350/2004
+f 556/68/2005 477/401/2006 569/580/2007
+f 569/580/2007 477/401/2006 476/69/2008
+f 126/305/2009 125/37/2010 502/45/2011
+f 502/45/2011 125/37/2010 501/114/2012
+f 476/69/2013 477/401/2014 532/207/2015
+f 532/207/2015 477/401/2014 533/273/2016
+f 553/647/2017 470/453/2018 555/530/2019
+f 555/530/2019 470/453/2018 473/719/2020
+f 481/400/2021 490/66/2022 537/551/2023
+f 537/551/2023 490/66/2022 546/659/2024
+f 568/347/2025 567/191/2026 475/353/2027
+f 475/353/2027 567/191/2026 474/256/2028
+f 565/610/2029 469/406/2030 566/592/2031
+f 566/592/2031 469/406/2030 472/288/2032
+f 404/501/2033 398/314/2034 522/166/2035
+f 522/166/2035 398/314/2034 521/65/2036
+f 575/668/2037 570/665/2038 487/669/2039
+f 487/669/2039 570/665/2038 478/241/2040
+f 477/401/2041 481/400/2042 533/273/2043
+f 533/273/2043 481/400/2042 537/551/2044
+f 539/18/2045 511/279/2046 527/632/2047
+f 527/632/2047 511/279/2046 509/459/2048
+f 543/670/2049 534/210/2050 523/138/2051
+f 523/138/2051 534/210/2050 507/497/2052
+f 500/503/2053 504/440/2054 525/672/2055
+f 525/672/2055 504/440/2054 528/93/2056
+f 531/84/2057 530/141/2058 502/45/2059
+f 502/45/2059 530/141/2058 505/417/2060
+f 526/432/2061 503/344/2062 529/259/2063
+f 529/259/2063 503/344/2062 498/289/2064
+f 533/273/2065 499/686/2066 532/207/2067
+f 532/207/2067 499/686/2066 506/679/2068
+f 537/551/2069 497/170/2070 533/273/2071
+f 533/273/2071 497/170/2070 499/686/2072
+f 535/495/2073 543/670/2074 508/321/2075
+f 508/321/2075 543/670/2074 523/138/2076
+f 522/166/2077 551/369/2078 524/567/2079
+f 524/567/2079 551/369/2078 552/474/2080
+f 521/65/2081 550/13/2082 522/166/2083
+f 522/166/2083 550/13/2082 551/369/2084
+f 520/452/2085 549/700/2086 521/65/2087
+f 521/65/2087 549/700/2086 550/13/2088
+f 513/352/2089 541/187/2090 520/452/2091
+f 520/452/2091 541/187/2090 549/700/2092
+f 498/289/2093 519/40/2094 529/259/2095
+f 529/259/2095 519/40/2094 548/683/2096
+f 500/503/2097 525/672/2098 518/713/2099
+f 518/713/2099 525/672/2098 547/222/2100
+f 516/342/2101 517/14/2102 545/549/2103
+f 545/549/2103 517/14/2102 546/659/2104
+f 515/507/2105 516/342/2106 544/132/2107
+f 544/132/2107 516/342/2106 545/549/2108
+f 514/131/2109 515/507/2110 542/718/2111
+f 542/718/2111 515/507/2110 544/132/2112
+f 512/572/2113 514/131/2114 540/550/2115
+f 540/550/2115 514/131/2114 542/718/2116
+f 518/713/2117 547/222/2118 513/352/2119
+f 513/352/2119 547/222/2118 541/187/2120
+f 517/14/2121 497/170/2122 546/659/2123
+f 546/659/2123 497/170/2122 537/551/2124
+f 501/114/2125 536/350/2126 502/45/2127
+f 502/45/2127 536/350/2126 531/84/2128
+f 524/567/2129 552/474/2130 501/114/2131
+f 501/114/2131 552/474/2130 536/350/2132
+f 534/210/2133 528/667/2134 507/497/2135
+f 507/497/2135 528/667/2134 504/162/2136
+f 510/85/2137 511/279/2138 538/385/2139
+f 538/385/2139 511/279/2138 539/18/2140
+f 503/42/2141 526/275/2142 508/321/2143
+f 508/321/2143 526/275/2142 535/495/2144
+f 505/502/2145 530/380/2146 509/459/2147
+f 509/459/2147 530/380/2146 527/632/2148
+f 506/235/2149 510/85/2150 532/30/2151
+f 532/30/2151 510/85/2150 538/385/2152
+f 519/40/2153 512/572/2154 548/683/2155
+f 548/683/2155 512/572/2154 540/550/2156
+f 393/450/2157 325/225/2158 520/452/2159
+f 520/452/2159 325/225/2158 513/352/2160
+f 228/643/2161 126/305/2162 505/417/2163
+f 505/417/2163 126/305/2162 502/45/2164
+f 478/241/2165 534/210/2166 487/669/2167
+f 487/669/2167 534/210/2166 543/670/2168
+f 231/223/2169 506/679/2170 80/724/2171
+f 80/724/2171 506/679/2170 499/686/2172
+f 476/720/2173 532/30/2174 482/581/2175
+f 482/581/2175 532/30/2174 538/385/2176
+f 471/102/2177 483/595/2178 527/632/2179
+f 527/632/2179 483/595/2178 539/18/2180
+f 496/568/2181 552/474/2182 495/441/2183
+f 495/441/2183 552/474/2182 551/369/2184
+f 484/517/2185 492/524/2186 540/550/2187
+f 540/550/2187 492/524/2186 548/683/2188
+f 479/494/2189 487/669/2190 535/495/2191
+f 535/495/2191 487/669/2190 543/670/2192
+f 303/168/2193 317/213/2194 510/85/2195
+f 510/85/2195 317/213/2194 511/279/2196
+f 124/80/2197 500/503/2198 369/657/2199
+f 369/657/2199 500/503/2198 518/713/2200
+f 495/441/2201 551/369/2202 494/678/2203
+f 494/678/2203 551/369/2202 550/13/2204
+f 494/678/2205 550/13/2206 493/237/2207
+f 493/237/2207 550/13/2206 549/700/2208
+f 300/118/2209 509/459/2210 317/213/2211
+f 317/213/2211 509/459/2210 511/279/2212
+f 324/182/2213 512/572/2214 371/183/2215
+f 371/183/2215 512/572/2214 519/40/2216
+f 178/687/2217 504/440/2218 124/80/2219
+f 124/80/2219 504/440/2218 500/503/2220
+f 398/314/2221 393/450/2222 521/65/2223
+f 521/65/2223 393/450/2222 520/452/2224
+f 493/237/2225 549/700/2226 485/475/2227
+f 485/475/2227 549/700/2226 541/187/2228
+f 473/719/2229 529/259/2230 492/524/2231
+f 492/524/2231 529/259/2230 548/683/2232
+f 469/406/2233 491/593/2234 525/672/2235
+f 525/672/2235 491/593/2234 547/222/2236
+f 490/66/2237 489/437/2238 546/659/2239
+f 546/659/2239 489/437/2238 545/549/2240
+f 367/59/2241 517/14/2242 360/556/2243
+f 360/556/2243 517/14/2242 516/342/2244
+f 489/437/2245 488/714/2246 545/549/2247
+f 545/549/2247 488/714/2246 544/132/2248
+f 421/348/2249 404/501/2250 524/567/2251
+f 524/567/2251 404/501/2250 522/166/2252
+f 488/714/2253 486/563/2254 544/132/2255
+f 544/132/2255 486/563/2254 542/718/2256
+f 486/563/2257 484/517/2258 542/718/2259
+f 542/718/2259 484/517/2258 540/550/2260
+f 80/724/2261 499/686/2262 33/363/2263
+f 33/363/2263 499/686/2262 497/170/2264
+f 485/475/2265 541/187/2266 491/593/2267
+f 491/593/2267 541/187/2266 547/222/2268
+f 554/247/2269 558/641/2270 471/102/2271
+f 471/102/2271 558/641/2270 483/595/2272
+f 316/343/2273 558/641/2274 301/508/2275
+f 301/508/2275 558/641/2274 554/247/2276
+f 383/277/2277 267/229/2278 575/668/2279
+f 575/668/2279 267/229/2278 570/665/2280
+f 565/610/2281 566/592/2282 110/199/2283
+f 110/199/2283 566/592/2282 183/710/2284
+f 127/122/2285 567/191/2286 111/264/2287
+f 111/264/2287 567/191/2286 568/347/2288
+f 105/284/2289 553/647/2290 102/125/2291
+f 102/125/2291 553/647/2290 555/530/2292
+f 556/68/2293 569/580/2294 104/464/2295
+f 104/464/2295 569/580/2294 103/695/2296
+f 294/636/2297 572/498/2298 104/464/2299
+f 104/464/2299 572/498/2298 556/68/2300
+f 270/583/2301 383/277/2302 571/134/2303
+f 571/134/2303 383/277/2302 575/668/2304
+f 495/441/2305 579/144/2306 496/568/2307
+f 496/568/2307 579/144/2306 580/455/2308
+f 494/678/2309 578/680/2310 495/441/2311
+f 495/441/2311 578/680/2310 579/144/2312
+f 493/237/2313 577/566/2314 494/678/2315
+f 494/678/2315 577/566/2314 578/680/2316
+f 485/475/2317 574/414/2318 493/237/2319
+f 493/237/2319 574/414/2318 577/566/2320
+f 473/719/2321 492/524/2322 555/530/2323
+f 555/530/2323 492/524/2322 564/599/2324
+f 469/406/2325 565/610/2326 491/593/2327
+f 491/593/2327 565/610/2326 576/116/2328
+f 489/437/2329 490/66/2330 562/642/2331
+f 562/642/2331 490/66/2330 563/368/2332
+f 488/714/2333 489/437/2334 561/620/2335
+f 561/620/2335 489/437/2334 562/642/2336
+f 486/563/2337 488/714/2338 560/478/2339
+f 560/478/2339 488/714/2338 561/620/2340
+f 484/517/2341 486/563/2342 559/56/2343
+f 559/56/2343 486/563/2342 560/478/2344
+f 491/593/2345 576/116/2346 485/475/2347
+f 485/475/2347 576/116/2346 574/414/2348
+f 490/66/2349 481/400/2350 563/368/2351
+f 563/368/2351 481/400/2350 572/498/2352
+f 480/349/2353 557/194/2354 475/353/2355
+f 475/353/2355 557/194/2354 568/347/2356
+f 496/568/2357 580/455/2358 480/349/2359
+f 480/349/2359 580/455/2358 557/194/2360
+f 570/665/2361 566/491/2362 478/241/2363
+f 478/241/2363 566/491/2362 472/139/2364
+f 482/581/2365 483/595/2366 573/543/2367
+f 573/543/2367 483/595/2366 558/641/2368
+f 470/329/2369 553/302/2370 479/494/2371
+f 479/494/2371 553/302/2370 571/134/2372
+f 474/251/2373 567/297/2374 471/102/2375
+f 471/102/2375 567/297/2374 554/247/2376
+f 476/720/2377 482/581/2378 569/252/2379
+f 569/252/2379 482/581/2378 573/543/2380
+f 492/524/2381 484/517/2382 564/599/2383
+f 564/599/2383 484/517/2382 559/56/2384
+f 125/37/2385 600/410/2386 421/348/2387
+f 421/348/2387 600/410/2386 608/159/2388
+f 143/91/2389 603/646/2390 434/63/2391
+f 434/63/2391 603/646/2390 608/159/2392
+f 141/418/2393 606/27/2394 143/91/2395
+f 143/91/2395 606/27/2394 603/646/2396
+f 369/657/2397 325/225/2398 602/387/2399
+f 602/387/2399 325/225/2398 605/715/2400
+f 33/363/2401 367/59/2402 588/25/2403
+f 588/25/2403 367/59/2402 607/15/2404
+f 35/519/2405 36/71/2406 594/140/2407
+f 594/140/2407 36/71/2406 607/15/2408
+f 139/402/2409 140/113/2410 604/415/2411
+f 604/415/2411 140/113/2410 605/715/2412
+f 141/418/2413 139/402/2414 606/27/2415
+f 606/27/2415 139/402/2414 604/415/2416
+f 430/372/2417 160/74/2418 602/387/2419
+f 602/387/2419 160/74/2418 589/699/2420
+f 58/603/2421 584/684/2422 78/609/2423
+f 78/609/2423 584/684/2422 586/209/2424
+f 594/140/2425 596/61/2426 35/519/2427
+f 35/519/2427 596/61/2426 56/364/2428
+f 418/320/2429 595/323/2430 392/328/2431
+f 392/328/2431 595/323/2430 601/137/2432
+f 34/38/2433 584/684/2434 371/183/2435
+f 371/183/2435 584/684/2434 599/184/2436
+f 354/559/2437 338/370/2438 596/61/2439
+f 596/61/2439 338/370/2438 597/295/2440
+f 59/435/2441 57/488/2442 598/108/2443
+f 598/108/2443 57/488/2442 597/295/2444
+f 582/480/2445 588/25/2446 82/707/2447
+f 82/707/2447 588/25/2446 420/481/2448
+f 303/168/2449 592/434/2450 317/213/2451
+f 317/213/2451 592/434/2450 593/565/2452
+f 593/565/2453 591/442/2454 317/213/2455
+f 317/213/2455 591/442/2454 300/118/2456
+f 171/307/2457 592/434/2458 81/73/2459
+f 81/73/2459 592/434/2458 581/271/2460
+f 591/442/2461 583/142/2462 300/118/2463
+f 300/118/2463 583/142/2462 228/458/2464
+f 78/608/2465 586/493/2466 276/664/2467
+f 276/664/2467 586/493/2466 590/663/2468
+f 392/328/2469 601/137/2470 391/90/2471
+f 391/90/2471 601/137/2470 585/492/2472
+f 80/724/2473 582/480/2474 231/223/2475
+f 231/223/2475 582/480/2474 581/5/2476
+f 230/330/2477 172/276/2478 583/267/2479
+f 583/267/2479 172/276/2478 587/404/2480
+f 418/320/2481 276/664/2482 595/323/2483
+f 595/323/2483 276/664/2482 590/663/2484
+f 390/26/2485 600/410/2486 172/276/2487
+f 172/276/2487 600/410/2486 587/404/2488
+f 598/108/2489 599/184/2490 59/435/2491
+f 59/435/2491 599/184/2490 60/148/2492
+f 391/532/2493 585/367/2494 160/74/2495
+f 160/74/2495 585/367/2494 589/699/2496
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_button.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_button.obj
new file mode 100644
index 0000000..2057c27
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_button.obj
@@ -0,0 +1,595 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v -0.003544 0.012417 0.078987
+v -0.003544 0.013694 0.078987
+v -0.003544 0.012417 0.078281
+v -0.003544 0.013694 0.078281
+v 0.003544 0.012417 0.078987
+v 0.003544 0.013694 0.078987
+v 0.003544 0.012417 0.078281
+v 0.003544 0.013694 0.078281
+v -0.003685 0.013694 0.078769
+v -0.003685 0.013694 0.078498
+v -0.003685 0.012417 0.078498
+v -0.003685 0.012417 0.078769
+v 0.003685 0.013694 0.078498
+v 0.003685 0.013694 0.078769
+v 0.003685 0.012417 0.078769
+v 0.003685 0.012417 0.078498
+v 0.005601 0.013400 0.078650
+v 0.004531 0.013400 0.081942
+v 0.004531 0.013400 0.075358
+v -0.001731 0.013400 0.073324
+v -0.005601 0.013400 0.078650
+v -0.001731 0.013400 0.083977
+v 0.001731 0.013400 0.083977
+v -0.004531 0.013400 0.081942
+v 0.001731 0.013400 0.073324
+v -0.004531 0.013400 0.075358
+v 0.005463 0.013400 0.078650
+v 0.004420 0.013400 0.081861
+v 0.004420 0.013400 0.075439
+v -0.001688 0.013400 0.073454
+v -0.005463 0.013400 0.078650
+v -0.001688 0.013400 0.083846
+v 0.001688 0.013400 0.083846
+v -0.004420 0.013400 0.081861
+v 0.001688 0.013400 0.073454
+v -0.004420 0.013400 0.075439
+v 0.000000 0.013400 0.078650
+v -0.003544 0.013694 0.078987
+v 0.005601 0.008414 0.078650
+v 0.004531 0.008414 0.081942
+v 0.004531 0.008414 0.075358
+v -0.001731 0.008414 0.073324
+v -0.005601 0.008414 0.078650
+v -0.001731 0.008414 0.083977
+v 0.001731 0.008414 0.083977
+v -0.004531 0.008414 0.081942
+v 0.001731 0.008414 0.073324
+v -0.004531 0.008414 0.075358
+v 0.007115 0.008414 0.078650
+v 0.005756 0.008414 0.082832
+v 0.005756 0.008414 0.074468
+v -0.002199 0.008414 0.071883
+v -0.007115 0.008414 0.078650
+v -0.002199 0.008414 0.085417
+v 0.002199 0.008414 0.085417
+v -0.005756 0.008414 0.082832
+v 0.002199 0.008414 0.071883
+v -0.005756 0.008414 0.074468
+v -0.001731 0.013280 0.083977
+v 0.001731 0.013280 0.083977
+v 0.005601 0.013280 0.078650
+v 0.004531 0.013280 0.075358
+v 0.001731 0.013280 0.073324
+v -0.004531 0.013280 0.081942
+v -0.005601 0.013280 0.078650
+v -0.004531 0.013280 0.075358
+v -0.001731 0.013280 0.073324
+v 0.004531 0.013280 0.081942
+v -0.001731 0.008498 0.083977
+v 0.005601 0.008498 0.078650
+v -0.004531 0.008498 0.081942
+v -0.005601 0.008498 0.078650
+v -0.004531 0.008498 0.075358
+v -0.001731 0.008498 0.073324
+v 0.004531 0.008498 0.081942
+v 0.001731 0.008498 0.083977
+v 0.004531 0.008498 0.075358
+v 0.001731 0.008498 0.073324
+vt 0.842894 0.803524
+vt 0.835648 0.808965
+vt 0.842327 0.831331
+vt 0.887089 0.817309
+vt 0.918325 0.883860
+vt 0.914475 0.893532
+vt 0.851925 0.804110
+vt 0.831948 0.817360
+vt 0.834920 0.826127
+vt 0.835239 0.825914
+vt 0.861947 0.818080
+vt 0.858905 0.826714
+vt 0.796511 0.818262
+vt 0.834940 0.856335
+vt 0.851468 0.832257
+vt 0.879150 0.794052
+vt 0.814609 0.794688
+vt 0.879631 0.793683
+vt 0.859802 0.855418
+vt 0.918325 0.877630
+vt 0.952893 0.877630
+vt 0.917637 0.875247
+vt 0.860005 0.855991
+vt 0.953582 0.876570
+vt 0.954157 0.893532
+vt 0.914475 0.887302
+vt 0.915739 0.887302
+vt 0.831492 0.866397
+vt 0.859621 0.809104
+vt 0.814110 0.794343
+vt 0.955480 0.893532
+vt 0.918325 0.874188
+vt 0.952893 0.874188
+vt 0.953582 0.875247
+vt 0.953582 0.886243
+vt 0.917637 0.886242
+vt 0.953582 0.884920
+vt 0.917637 0.884919
+vt 0.897742 0.817179
+vt 0.842083 0.832031
+vt 0.835347 0.808727
+vt 0.842998 0.803894
+vt 0.842194 0.831691
+vt 0.880142 0.841098
+vt 0.835113 0.855751
+vt 0.952893 0.883860
+vt 0.917637 0.876570
+vt 0.830731 0.769044
+vt 0.852058 0.803750
+vt 0.835058 0.808516
+vt 0.852169 0.803410
+vt 0.887696 0.817293
+vt 0.806054 0.847763
+vt 0.814622 0.841758
+vt 0.888895 0.846984
+vt 0.862304 0.818081
+vt 0.805357 0.788458
+vt 0.815102 0.841390
+vt 0.956743 0.887302
+vt 0.956743 0.893532
+vt 0.851358 0.831917
+vt 0.862760 0.769044
+vt 0.954157 0.887302
+vt 0.915739 0.893532
+vt 0.859312 0.779106
+vt 0.806556 0.818148
+vt 0.834631 0.826337
+vt 0.859194 0.826925
+vt 0.859332 0.809314
+vt 0.859013 0.809527
+vt 0.832305 0.817361
+vt 0.832688 0.817376
+vt 0.847126 0.817721
+vt 0.859139 0.779690
+vt 0.834450 0.780024
+vt 0.834248 0.779451
+vt 0.807163 0.818133
+vt 0.863522 0.866397
+vt 0.952893 0.893532
+vt 0.917062 0.887302
+vt 0.918325 0.887302
+vt 0.918325 0.893532
+vt 0.917062 0.893532
+vt 0.952893 0.887302
+vt 0.955480 0.887302
+vt 0.879643 0.840753
+vt 0.888198 0.787678
+vt 0.861564 0.818065
+vt 0.842784 0.803184
+vt 0.851254 0.831547
+vt 0.858604 0.826476
+vn -0.838651 0.000004 -0.544669
+vn -0.838651 0.000004 -0.544669
+vn -0.838651 0.000004 -0.544669
+vn -0.838651 0.000004 -0.544669
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.838651 0.000004 0.544669
+vn 0.838651 0.000004 0.544669
+vn 0.838651 0.000004 0.544669
+vn 0.838651 0.000004 0.544669
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.838651 -0.000004 -0.544669
+vn 0.838651 -0.000004 -0.544669
+vn 0.838651 -0.000004 -0.544669
+vn 0.838651 -0.000004 -0.544669
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn -0.838651 -0.000004 0.544669
+vn -0.838651 -0.000004 0.544669
+vn -0.838651 -0.000004 0.544669
+vn -0.838651 -0.000004 0.544669
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn 0.619120 0.643718 -0.449797
+vn 0.236462 0.643710 -0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.619120 0.643718 0.449797
+vn -0.236462 0.643710 0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.236462 0.643710 -0.727821
+vn -0.236462 0.643710 -0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.765269 0.643711 0.000000
+vn 0.619120 0.643718 -0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 -0.727821
+vn -0.619120 0.643718 -0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 0.727821
+vn 0.236462 0.643710 0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.236462 0.643710 0.727821
+vn 0.619120 0.643718 0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.619120 0.643718 0.449797
+vn 0.765269 0.643711 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.765269 0.643711 0.000000
+vn -0.619120 0.643718 0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -0.765269 0.643711 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.309004 -0.000000 0.951061
+vn -0.309004 -0.000000 0.951061
+vn 0.191936 0.783706 0.590733
+vn -0.191936 0.783706 0.590733
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.191936 0.783706 -0.590733
+vn 0.502506 0.783714 -0.365076
+vn -0.309004 -0.000000 0.951061
+vn -0.809030 -0.000000 0.587767
+vn -0.191936 0.783706 0.590733
+vn -0.502506 0.783714 0.365076
+vn -0.809030 -0.000000 0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn -0.621130 0.783707 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 -0.365076
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.502506 0.783714 -0.365076
+vn -0.191936 0.783706 -0.590733
+vn 1.000000 0.000000 0.000000
+vn 0.809030 -0.000000 0.587767
+vn 0.621130 0.783707 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.809030 -0.000000 0.587767
+vn 0.309004 -0.000000 0.951061
+vn 0.502506 0.783714 0.365076
+vn 0.191936 0.783706 0.590733
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn -0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.621130 0.783707 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 0.365076
+vn 0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 0.590733
+vn -0.191936 0.783706 0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.191936 0.783706 -0.590733
+vn 0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 0.590733
+vn -0.502506 0.783714 0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 0.365076
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 -0.365076
+vn -0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 -0.727821
+vn 0.236462 0.643710 -0.727821
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn 0.619120 0.643718 0.449797
+vn 0.236462 0.643710 0.727821
+vn 0.809030 -0.000000 0.587767
+vn 0.309004 -0.000000 0.951061
+vn 0.765269 0.643711 0.000000
+vn 0.619120 0.643718 0.449797
+vn 1.000000 0.000000 0.000000
+vn 0.809030 -0.000000 0.587767
+vn -0.619120 0.643718 -0.449797
+vn -0.236462 0.643710 -0.727821
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.765269 0.643711 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.619120 0.643718 0.449797
+vn -0.765269 0.643711 0.000000
+vn -0.809030 -0.000000 0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.236462 0.643710 0.727821
+vn -0.619120 0.643718 0.449797
+vn -0.309004 -0.000000 0.951061
+vn -0.809030 -0.000000 0.587767
+vn 0.236462 0.643710 -0.727821
+vn 0.619120 0.643718 -0.449797
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.619120 0.643718 -0.449797
+vn 0.765269 0.643711 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.236462 0.643710 0.727821
+vn -0.236462 0.643710 0.727821
+vn 0.309004 -0.000000 0.951061
+vn -0.309004 -0.000000 0.951061
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 -0.000000 0.587767
+vn 0.309004 -0.000000 0.951061
+vn 0.809030 -0.000000 0.587767
+vn 0.309004 -0.000000 0.951061
+vn 1.000000 0.000000 0.000000
+vn 0.809030 -0.000000 0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.809030 -0.000000 0.587767
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.809030 -0.000000 0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.809030 -0.000000 0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.309004 -0.000000 0.951061
+vn -0.809030 -0.000000 0.587767
+vn -0.309004 -0.000000 0.951061
+vn -0.809030 -0.000000 0.587767
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.309004 -0.000000 0.951061
+vn -0.309004 -0.000000 0.951061
+vn 0.309004 -0.000000 0.951061
+vn -0.309004 -0.000000 0.951061
+f 10/80/1 4/81/2 11/83/3
+f 11/83/3 4/81/2 3/82/4
+f 4/81/5 8/84/6 3/82/7
+f 3/82/7 8/84/6 7/79/8
+f 14/85/9 6/59/10 15/31/11
+f 15/31/11 6/59/10 5/60/12
+f 6/46/13 2/5/14 5/21/15
+f 5/21/15 2/5/14 1/20/16
+f 11/22/17 3/32/18 16/34/19
+f 16/34/19 3/32/18 7/33/20
+f 13/35/21 8/84/22 10/36/23
+f 10/36/23 8/84/22 4/81/24
+f 6/46/25 14/37/26 2/5/27
+f 2/5/27 14/37/26 9/38/28
+f 14/37/29 13/35/30 9/38/31
+f 9/38/31 13/35/30 10/36/32
+f 1/20/33 12/47/34 5/21/35
+f 5/21/35 12/47/34 15/24/36
+f 12/47/37 11/22/38 15/24/39
+f 15/24/39 11/22/38 16/34/40
+f 8/84/41 13/63/42 7/79/43
+f 7/79/43 13/63/42 16/25/44
+f 13/63/45 14/85/46 16/25/47
+f 16/25/47 14/85/46 15/31/48
+f 2/26/49 9/27/50 1/6/51
+f 1/6/51 9/27/50 12/64/52
+f 9/27/53 10/80/54 12/64/55
+f 12/64/55 10/80/54 11/83/56
+f 19/12/57 25/61/58 29/91/59
+f 29/91/59 25/61/58 35/90/60
+f 24/41/61 22/1/62 34/2/63
+f 34/2/63 22/1/62 32/42/64
+f 25/61/65 20/43/66 35/90/67
+f 35/90/67 20/43/66 30/3/68
+f 17/11/69 19/12/70 27/88/71
+f 27/88/71 19/12/70 29/91/72
+f 20/43/73 26/9/74 30/3/75
+f 30/3/75 26/9/74 36/10/76
+f 22/1/77 23/49/78 32/42/79
+f 32/42/79 23/49/78 33/7/80
+f 23/49/81 18/69/82 33/7/83
+f 33/7/83 18/69/82 28/70/84
+f 18/69/85 17/11/86 28/70/87
+f 28/70/87 17/11/86 27/88/88
+f 21/71/89 24/41/90 31/72/91
+f 31/72/91 24/41/90 34/2/92
+f 26/9/93 21/71/94 36/10/95
+f 36/10/95 21/71/94 31/72/96
+f 33/7/97 28/70/98 37/73/99
+f 35/90/100 30/3/101 37/73/102
+f 32/42/103 33/7/104 37/73/105
+f 27/88/106 29/91/107 37/73/108
+f 29/91/109 35/90/110 37/73/111
+f 34/2/112 32/42/113 37/73/114
+f 31/72/115 34/2/116 37/73/117
+f 36/10/118 31/72/119 37/73/120
+f 30/3/121 36/10/122 37/73/123
+f 28/70/124 27/88/125 37/73/126
+f 76/74/127 69/75/128 45/65/129
+f 45/65/129 69/75/128 44/76/130
+f 77/86/131 70/4/132 41/44/133
+f 41/44/133 70/4/132 39/52/134
+f 78/19/135 77/86/136 47/23/137
+f 47/23/137 77/86/136 41/44/138
+f 69/75/139 71/17/140 44/76/141
+f 44/76/141 71/17/140 46/30/142
+f 71/17/143 72/77/144 46/30/145
+f 46/30/145 72/77/144 43/66/146
+f 72/77/147 73/58/148 43/66/149
+f 43/66/149 73/58/148 48/54/150
+f 73/58/151 74/45/152 48/54/153
+f 48/54/153 74/45/152 42/14/154
+f 70/4/155 75/16/156 39/52/157
+f 39/52/157 75/16/156 40/18/158
+f 75/16/159 76/74/160 40/18/161
+f 40/18/161 76/74/160 45/65/162
+f 74/45/163 78/19/164 42/14/165
+f 42/14/165 78/19/164 47/23/166
+f 39/52/167 40/18/168 49/39/169
+f 49/39/169 40/18/168 50/87/170
+f 40/18/171 45/65/172 50/87/173
+f 50/87/173 45/65/172 55/62/174
+f 42/14/175 47/23/176 52/28/177
+f 52/28/177 47/23/176 57/78/178
+f 45/65/179 44/76/180 55/62/181
+f 55/62/181 44/76/180 54/48/182
+f 41/44/183 39/52/184 51/55/185
+f 51/55/185 39/52/184 49/39/186
+f 47/23/187 41/44/188 57/78/189
+f 57/78/189 41/44/188 51/55/190
+f 44/76/191 46/30/192 54/48/193
+f 54/48/193 46/30/192 56/57/194
+f 46/30/195 43/66/196 56/57/197
+f 56/57/197 43/66/196 53/13/198
+f 43/66/199 48/54/200 53/13/201
+f 53/13/201 48/54/200 58/53/202
+f 48/54/203 42/14/204 58/53/205
+f 58/53/205 42/14/204 52/28/206
+f 20/43/207 25/61/208 67/40/209
+f 67/40/209 25/61/208 63/15/210
+f 18/69/211 23/49/212 68/29/213
+f 68/29/213 23/49/212 60/51/214
+f 17/11/215 18/69/216 61/56/217
+f 61/56/217 18/69/216 68/29/218
+f 26/9/219 20/43/220 66/67/221
+f 66/67/221 20/43/220 67/40/222
+f 21/71/223 26/9/224 65/8/225
+f 65/8/225 26/9/224 66/67/226
+f 24/41/227 21/71/228 64/50/229
+f 64/50/229 21/71/228 65/8/230
+f 22/1/231 24/41/232 59/89/233
+f 59/89/233 24/41/232 64/50/234
+f 25/61/235 19/12/236 63/15/237
+f 63/15/237 19/12/236 62/68/238
+f 19/12/239 17/11/240 62/68/241
+f 62/68/241 17/11/240 61/56/242
+f 23/49/243 22/1/244 60/51/245
+f 60/51/245 22/1/244 59/89/246
+f 67/40/247 63/15/248 74/45/249
+f 74/45/249 63/15/248 78/19/250
+f 68/29/251 60/51/252 75/16/253
+f 75/16/253 60/51/252 76/74/254
+f 61/56/255 68/29/256 70/4/257
+f 70/4/257 68/29/256 75/16/258
+f 66/67/259 67/40/260 73/58/261
+f 73/58/261 67/40/260 74/45/262
+f 65/8/263 66/67/264 72/77/265
+f 72/77/265 66/67/264 73/58/266
+f 64/50/267 65/8/268 71/17/269
+f 71/17/269 65/8/268 72/77/270
+f 59/89/271 64/50/272 69/75/273
+f 69/75/273 64/50/272 71/17/274
+f 63/15/275 62/68/276 78/19/277
+f 78/19/277 62/68/276 77/86/278
+f 62/68/279 61/56/280 77/86/281
+f 77/86/281 61/56/280 70/4/282
+f 60/51/283 59/89/284 76/74/285
+f 76/74/285 59/89/284 69/75/286
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_button2.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_button2.obj
new file mode 100644
index 0000000..2f3bd1e
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_button2.obj
@@ -0,0 +1,794 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v 0.001731 0.013400 0.090823
+v -0.005601 0.013400 0.096150
+v 0.001731 0.013400 0.101477
+v -0.004531 0.013400 0.099442
+v 0.004531 0.013400 0.099442
+v 0.005601 0.013400 0.096150
+v -0.001731 0.013400 0.101477
+v -0.001731 0.013400 0.090823
+v 0.004531 0.013400 0.092858
+v -0.004531 0.013400 0.092858
+v 0.001690 0.013400 0.090950
+v -0.005467 0.013400 0.096150
+v 0.001690 0.013400 0.101350
+v -0.004423 0.013400 0.099364
+v 0.004423 0.013400 0.099364
+v 0.005467 0.013400 0.096150
+v -0.001690 0.013400 0.101350
+v -0.001690 0.013400 0.090950
+v 0.004423 0.013400 0.092936
+v -0.004423 0.013400 0.092936
+v 0.000000 0.013400 0.096150
+v 0.001731 0.008414 0.090823
+v -0.005601 0.008414 0.096150
+v 0.001731 0.008414 0.101477
+v -0.004531 0.008414 0.099442
+v 0.004531 0.008414 0.099442
+v 0.005601 0.008414 0.096150
+v -0.001731 0.008414 0.101477
+v -0.001731 0.008414 0.090823
+v 0.004531 0.008414 0.092858
+v -0.004531 0.008414 0.092858
+v 0.002212 0.008414 0.089341
+v -0.007159 0.008414 0.096150
+v 0.002212 0.008414 0.102959
+v -0.005792 0.008414 0.100358
+v 0.005792 0.008414 0.100358
+v 0.007159 0.008414 0.096150
+v -0.002212 0.008414 0.102959
+v -0.002212 0.008414 0.089341
+v 0.005792 0.008414 0.091942
+v -0.005792 0.008414 0.091942
+v -0.001731 0.013209 0.101477
+v 0.001731 0.013209 0.101477
+v -0.004531 0.013209 0.099442
+v -0.005601 0.013209 0.096150
+v -0.004531 0.013209 0.092858
+v 0.004531 0.013209 0.092858
+v 0.005601 0.013209 0.096150
+v 0.004531 0.013209 0.099442
+v -0.001731 0.013209 0.090823
+v 0.001731 0.013209 0.090823
+v 0.001731 0.008535 0.101477
+v -0.005601 0.008535 0.096150
+v 0.004531 0.008535 0.099442
+v 0.001731 0.008535 0.090823
+v -0.001731 0.008535 0.101477
+v -0.004531 0.008535 0.099442
+v -0.004531 0.008535 0.092858
+v 0.004531 0.008535 0.092858
+v 0.005601 0.008535 0.096150
+v -0.001731 0.008535 0.090823
+v 0.001108 0.013694 0.092741
+v -0.003585 0.013694 0.096150
+v 0.001108 0.013694 0.099560
+v -0.002900 0.013694 0.098257
+v 0.002900 0.013694 0.098257
+v 0.003585 0.013694 0.096150
+v -0.001108 0.013694 0.099560
+v -0.001108 0.013694 0.092741
+v 0.002900 0.013694 0.094043
+v -0.002900 0.013694 0.094043
+v 0.000955 0.013694 0.093211
+v -0.003090 0.013694 0.096150
+v 0.000955 0.013694 0.099090
+v -0.002500 0.013694 0.097967
+v 0.002500 0.013694 0.097967
+v 0.003090 0.013694 0.096150
+v -0.000955 0.013694 0.099090
+v -0.000955 0.013694 0.093211
+v 0.002500 0.013694 0.094334
+v -0.002500 0.013694 0.094334
+v 0.001108 0.012417 0.092741
+v -0.003585 0.012417 0.096150
+v 0.001108 0.012417 0.099560
+v -0.002900 0.012417 0.098257
+v 0.002900 0.012417 0.098257
+v 0.003585 0.012417 0.096150
+v -0.001108 0.012417 0.099560
+v -0.001108 0.012417 0.092741
+v 0.002900 0.012417 0.094043
+v -0.002900 0.012417 0.094043
+v 0.000955 0.012417 0.093211
+v -0.003090 0.012417 0.096150
+v 0.000955 0.012417 0.099090
+v -0.002500 0.012417 0.097967
+v 0.002500 0.012417 0.097967
+v 0.003090 0.012417 0.096150
+v -0.000955 0.012417 0.099090
+v -0.000955 0.012417 0.093211
+v 0.002500 0.012417 0.094334
+v -0.002500 0.012417 0.094334
+vt 0.932232 0.937151
+vt 0.945647 0.941314
+vt 0.825593 0.923815
+vt 0.788802 0.909064
+vt 0.935598 0.930484
+vt 0.909311 0.932196
+vt 0.907009 0.929014
+vt 0.922229 0.927744
+vt 0.923371 0.959651
+vt 0.926270 0.980012
+vt 0.923981 0.964843
+vt 0.907571 0.956084
+vt 0.910064 0.946637
+vt 0.932187 0.961332
+vt 0.913791 0.964746
+vt 0.931115 0.938999
+vt 0.906593 0.976987
+vt 0.814667 0.938118
+vt 0.907941 0.945842
+vt 0.817792 0.929265
+vt 0.924220 0.967204
+vt 0.899768 0.959058
+vt 0.834177 0.952426
+vt 0.841570 0.947355
+vt 0.817336 0.900595
+vt 0.797907 0.915598
+vt 0.841862 0.947586
+vt 0.797489 0.962416
+vt 0.863053 0.962016
+vt 0.842840 0.976907
+vt 0.845500 0.938935
+vt 0.900358 0.943670
+vt 0.896353 0.942255
+vt 0.950018 0.956861
+vt 0.914874 0.948129
+vt 0.914648 0.953699
+vt 0.937578 0.926986
+vt 0.925672 0.975465
+vt 0.931991 0.947345
+vt 0.825215 0.952174
+vt 0.842325 0.930150
+vt 0.842016 0.930356
+vt 0.834916 0.924929
+vt 0.844928 0.938934
+vt 0.844556 0.938918
+vt 0.830059 0.938555
+vt 0.790654 0.938738
+vt 0.933703 0.963036
+vt 0.936911 0.954039
+vt 0.813826 0.890060
+vt 0.797202 0.915105
+vt 0.861850 0.915045
+vt 0.817977 0.976306
+vt 0.798179 0.961897
+vt 0.789797 0.938756
+vt 0.842554 0.976087
+vt 0.841940 0.901082
+vt 0.845641 0.890060
+vt 0.950018 0.939483
+vt 0.932035 0.952826
+vt 0.835221 0.924030
+vt 0.909829 0.955422
+vt 0.895358 0.960445
+vt 0.915261 0.939767
+vt 0.923140 0.936945
+vt 0.936417 0.945527
+vt 0.938885 0.954567
+vt 0.909516 0.972375
+vt 0.817813 0.946906
+vt 0.818124 0.946700
+vt 0.917914 0.958206
+vt 0.923549 0.941808
+vt 0.928754 0.943094
+vt 0.846431 0.987663
+vt 0.870407 0.938130
+vt 0.817616 0.901404
+vt 0.842181 0.900257
+vt 0.862534 0.914520
+vt 0.869546 0.938152
+vt 0.834277 0.952786
+vt 0.862338 0.961524
+vt 0.923050 0.934996
+vt 0.915227 0.962597
+vt 0.946468 0.956010
+vt 0.928741 0.957476
+vt 0.922006 0.924371
+vt 0.939379 0.968571
+vt 0.918286 0.943618
+vt 0.942241 0.971610
+vt 0.814114 0.987501
+vt 0.788568 0.968560
+vt 0.779102 0.938526
+vt 0.870794 0.908363
+vt 0.881150 0.937707
+vt 0.834453 0.953332
+vt 0.824904 0.953069
+vt 0.842788 0.929814
+vt 0.842325 0.947924
+vt 0.817349 0.947240
+vt 0.835045 0.924577
+vt 0.825883 0.924738
+vt 0.815235 0.938118
+vt 0.818251 0.929599
+vt 0.818529 0.929812
+vt 0.815603 0.938133
+vt 0.825771 0.924361
+vt 0.825084 0.952524
+vt 0.938637 0.944674
+vt 0.914016 0.937979
+vt 0.872150 0.968124
+vt 0.817725 0.977137
+vn -0.765269 0.643711 0.000000
+vn -0.619128 0.643727 0.449773
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.619128 0.643727 0.449773
+vn -0.236496 0.643720 0.727801
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 -0.727821
+vn -0.619120 0.643718 -0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.619120 0.643718 -0.449797
+vn 0.236462 0.643710 -0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.236496 0.643720 0.727801
+vn 0.619128 0.643727 0.449773
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.236462 0.643710 -0.727821
+vn -0.236462 0.643710 -0.727821
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.619128 0.643727 0.449773
+vn 0.765269 0.643711 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236496 0.643720 0.727801
+vn 0.236496 0.643720 0.727801
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -0.765269 0.643711 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.765269 0.643711 0.000000
+vn 0.619120 0.643718 -0.449797
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.191936 0.783706 -0.590733
+vn 0.502506 0.783714 -0.365076
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn 0.191935 0.783732 0.590698
+vn -0.191935 0.783732 0.590698
+vn -0.309041 -0.000000 0.951049
+vn -0.809045 -0.000000 0.587747
+vn -0.191935 0.783732 0.590698
+vn -0.502529 0.783702 0.365070
+vn 0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn 0.502529 0.783702 0.365070
+vn 0.191935 0.783732 0.590698
+vn -0.809045 -0.000000 0.587747
+vn -1.000000 0.000000 0.000000
+vn -0.502529 0.783702 0.365070
+vn -0.621130 0.783707 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.502506 0.783714 -0.365076
+vn -0.191936 0.783706 -0.590733
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.621130 0.783707 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 0.621130 0.783707 0.000000
+vn 0.502529 0.783702 0.365070
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn -0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.191935 0.783732 0.590698
+vn -0.191935 0.783732 0.590698
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.191935 0.783732 0.590698
+vn -0.502529 0.783702 0.365070
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502506 0.783714 -0.365076
+vn -0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.502529 0.783702 0.365070
+vn -0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502529 0.783702 0.365070
+vn 0.191935 0.783732 0.590698
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.191936 0.783706 -0.590733
+vn 0.191936 0.783706 -0.590733
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.621130 0.783707 0.000000
+vn 0.502529 0.783702 0.365070
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.502506 0.783714 -0.365076
+vn 0.621130 0.783707 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.621130 0.783707 0.000000
+vn -0.502506 0.783714 -0.365076
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.236462 0.643710 -0.727821
+vn 0.236462 0.643710 -0.727821
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn 0.765269 0.643711 0.000000
+vn 0.619128 0.643727 0.449773
+vn 1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 0.619120 0.643718 -0.449797
+vn 0.765269 0.643711 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -0.236462 0.643710 -0.727821
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.619128 0.643727 0.449773
+vn -0.765269 0.643711 0.000000
+vn -0.809045 -0.000000 0.587747
+vn -1.000000 0.000000 0.000000
+vn 0.619128 0.643727 0.449773
+vn 0.236496 0.643720 0.727801
+vn 0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn -0.236496 0.643720 0.727801
+vn -0.619128 0.643727 0.449773
+vn -0.309041 -0.000000 0.951049
+vn -0.809045 -0.000000 0.587747
+vn 0.236496 0.643720 0.727801
+vn -0.236496 0.643720 0.727801
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn 0.236462 0.643710 -0.727821
+vn 0.619120 0.643718 -0.449797
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn -0.765269 0.643711 0.000000
+vn -0.619120 0.643718 -0.449797
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn -0.309004 0.000000 -0.951061
+vn 0.309004 0.000000 -0.951061
+vn 1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn 0.809030 0.000000 -0.587767
+vn 1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.809030 0.000000 -0.587767
+vn -0.309004 0.000000 -0.951061
+vn -0.809045 -0.000000 0.587747
+vn -1.000000 0.000000 0.000000
+vn -0.809045 -0.000000 0.587747
+vn -1.000000 0.000000 0.000000
+vn 0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn 0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn -0.809045 -0.000000 0.587747
+vn -0.309041 -0.000000 0.951049
+vn -0.809045 -0.000000 0.587747
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn 0.309041 -0.000000 0.951049
+vn -0.309041 -0.000000 0.951049
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn 0.309004 0.000000 -0.951061
+vn 0.809030 0.000000 -0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn -1.000000 0.000000 0.000000
+vn -0.809030 0.000000 -0.587767
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn -0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn -0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.587858 0.000000 -0.808964
+vn 0.587858 0.000000 -0.808964
+vn 0.587858 0.000000 -0.808964
+vn 0.587858 0.000000 -0.808964
+vn 0.951057 -0.000000 0.309014
+vn 0.951057 -0.000000 0.309014
+vn 0.951057 -0.000000 0.309014
+vn 0.951057 -0.000000 0.309014
+vn -0.587858 0.000000 -0.808964
+vn -0.587858 0.000000 -0.808964
+vn -0.587858 0.000000 -0.808964
+vn -0.587858 0.000000 -0.808964
+vn 0.951058 0.000000 -0.309013
+vn 0.951058 0.000000 -0.309013
+vn 0.951058 0.000000 -0.309013
+vn 0.951058 0.000000 -0.309013
+vn -0.587785 0.000000 -0.809017
+vn -0.587785 0.000000 -0.809017
+vn -0.587785 0.000000 -0.809017
+vn -0.587785 0.000000 -0.809017
+vn 0.951057 0.000000 -0.309014
+vn 0.951057 0.000000 -0.309014
+vn 0.951057 0.000000 -0.309014
+vn 0.951057 0.000000 -0.309014
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn -0.951057 0.000000 -0.309014
+vn -0.951057 0.000000 -0.309014
+vn -0.951057 0.000000 -0.309014
+vn -0.951057 0.000000 -0.309014
+vn 0.587785 0.000000 -0.809017
+vn 0.587785 0.000000 -0.809017
+vn 0.587785 0.000000 -0.809017
+vn 0.587785 0.000000 -0.809017
+vn -0.951058 0.000000 -0.309013
+vn -0.951058 0.000000 -0.309013
+vn -0.951058 0.000000 -0.309013
+vn -0.951058 0.000000 -0.309013
+vn -0.951058 -0.000000 0.309012
+vn -0.951058 -0.000000 0.309012
+vn -0.951058 -0.000000 0.309012
+vn -0.951058 -0.000000 0.309012
+vn 0.587785 -0.000000 0.809018
+vn 0.587785 -0.000000 0.809018
+vn 0.587785 -0.000000 0.809018
+vn 0.587785 -0.000000 0.809018
+vn -0.587857 -0.000000 0.808965
+vn -0.587857 -0.000000 0.808965
+vn -0.587857 -0.000000 0.808965
+vn -0.587857 -0.000000 0.808965
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 0.000000 -1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.000000 -0.000000 1.000000
+vn 0.587857 -0.000000 0.808965
+vn 0.587857 -0.000000 0.808965
+vn 0.587857 -0.000000 0.808965
+vn 0.587857 -0.000000 0.808965
+vn -0.587785 -0.000000 0.809018
+vn -0.587785 -0.000000 0.809018
+vn -0.587785 -0.000000 0.809018
+vn -0.587785 -0.000000 0.809018
+vn 0.951058 -0.000000 0.309012
+vn 0.951058 -0.000000 0.309012
+vn 0.951058 -0.000000 0.309012
+vn 0.951058 -0.000000 0.309012
+vn -0.951057 -0.000000 0.309014
+vn -0.951057 -0.000000 0.309014
+vn -0.951057 -0.000000 0.309014
+vn -0.951057 -0.000000 0.309014
+f 2/102/1 4/103/2 12/105/3
+f 12/105/3 4/103/2 14/104/4
+f 4/103/5 7/106/6 14/104/7
+f 14/104/7 7/106/6 17/101/8
+f 8/107/9 10/69/10 18/40/11
+f 18/40/11 10/69/10 20/70/12
+f 9/27/13 1/80/14 19/24/15
+f 19/24/15 1/80/14 11/23/16
+f 3/100/17 5/41/18 13/43/19
+f 13/43/19 5/41/18 15/42/20
+f 1/80/21 8/107/22 11/23/23
+f 11/23/23 8/107/22 18/40/24
+f 5/41/25 6/44/26 15/42/27
+f 15/42/27 6/44/26 16/45/28
+f 7/106/29 3/100/30 17/101/31
+f 17/101/31 3/100/30 13/43/32
+f 10/69/33 2/102/34 20/70/35
+f 20/70/35 2/102/34 12/105/36
+f 6/44/37 9/27/38 16/45/39
+f 16/45/39 9/27/38 19/24/40
+f 12/105/41 14/104/42 21/46/43
+f 18/40/44 20/70/45 21/46/46
+f 14/104/47 17/101/48 21/46/49
+f 13/43/50 15/42/51 21/46/52
+f 15/42/53 16/45/54 21/46/55
+f 17/101/56 13/43/57 21/46/58
+f 16/45/59 19/24/60 21/46/61
+f 19/24/62 11/23/63 21/46/64
+f 11/23/65 18/40/66 21/46/67
+f 20/70/68 12/105/69 21/46/70
+f 53/47/71 58/54/72 23/55/73
+f 23/55/73 58/54/72 31/28/74
+f 55/56/75 59/81/76 22/30/77
+f 22/30/77 59/81/76 30/29/78
+f 52/57/79 56/76/80 24/77/81
+f 24/77/81 56/76/80 28/25/82
+f 56/76/83 57/26/84 28/25/85
+f 28/25/85 57/26/84 25/51/86
+f 54/52/87 52/57/88 26/78/89
+f 26/78/89 52/57/88 24/77/90
+f 57/26/91 53/47/92 25/51/93
+f 25/51/93 53/47/92 23/55/94
+f 58/54/95 61/53/96 31/28/97
+f 31/28/97 61/53/96 29/111/98
+f 59/81/99 60/79/100 30/29/101
+f 30/29/101 60/79/100 27/75/102
+f 60/79/103 54/52/104 27/75/105
+f 27/75/105 54/52/104 26/78/106
+f 61/53/107 55/56/108 29/111/109
+f 29/111/109 55/56/108 22/30/110
+f 22/30/111 30/29/112 32/74/113
+f 32/74/113 30/29/112 40/110/114
+f 24/77/115 28/25/116 34/58/117
+f 34/58/117 28/25/116 38/50/118
+f 28/25/119 25/51/120 38/50/121
+f 38/50/121 25/51/120 35/4/122
+f 31/28/123 29/111/124 41/91/125
+f 41/91/125 29/111/124 39/90/126
+f 25/51/127 23/55/128 35/4/129
+f 35/4/129 23/55/128 33/92/130
+f 26/78/131 24/77/132 36/93/133
+f 36/93/133 24/77/132 34/58/134
+f 29/111/135 22/30/136 39/90/137
+f 39/90/137 22/30/136 32/74/138
+f 27/75/139 26/78/140 37/94/141
+f 37/94/141 26/78/140 36/93/142
+f 30/29/143 27/75/144 40/110/145
+f 40/110/145 27/75/144 37/94/146
+f 23/55/147 31/28/148 33/92/149
+f 33/92/149 31/28/148 41/91/150
+f 8/107/151 1/80/152 50/96/153
+f 50/96/153 1/80/152 51/95/154
+f 6/44/155 5/41/156 48/31/157
+f 48/31/157 5/41/156 49/97/158
+f 9/27/159 6/44/160 47/98/161
+f 47/98/161 6/44/160 48/31/162
+f 10/69/163 8/107/164 46/99/165
+f 46/99/165 8/107/164 50/96/166
+f 4/103/167 2/102/168 44/20/169
+f 44/20/169 2/102/168 45/18/170
+f 5/41/171 3/100/172 49/97/173
+f 49/97/173 3/100/172 43/61/174
+f 7/106/175 4/103/176 42/3/177
+f 42/3/177 4/103/176 44/20/178
+f 3/100/179 7/106/180 43/61/181
+f 43/61/181 7/106/180 42/3/182
+f 1/80/183 9/27/184 51/95/185
+f 51/95/185 9/27/184 47/98/186
+f 2/102/187 10/69/188 45/18/189
+f 45/18/189 10/69/188 46/99/190
+f 50/96/191 51/95/192 61/53/193
+f 61/53/193 51/95/192 55/56/194
+f 48/31/195 49/97/196 60/79/197
+f 60/79/197 49/97/196 54/52/198
+f 47/98/199 48/31/200 59/81/201
+f 59/81/201 48/31/200 60/79/202
+f 46/99/203 50/96/204 58/54/205
+f 58/54/205 50/96/204 61/53/206
+f 44/20/207 45/18/208 57/26/209
+f 57/26/209 45/18/208 53/47/210
+f 49/97/211 43/61/212 54/52/213
+f 54/52/213 43/61/212 52/57/214
+f 42/3/215 44/20/216 56/76/217
+f 56/76/217 44/20/216 57/26/218
+f 43/61/219 42/3/220 52/57/221
+f 52/57/221 42/3/220 56/76/222
+f 51/95/223 47/98/224 55/56/225
+f 55/56/225 47/98/224 59/81/226
+f 45/18/227 46/99/228 53/47/229
+f 53/47/229 46/99/228 58/54/230
+f 62/15/231 69/12/232 72/83/233
+f 72/83/233 69/12/232 79/62/234
+f 64/108/235 74/66/236 68/1/237
+f 68/1/237 74/66/236 78/16/238
+f 63/109/239 65/82/240 73/64/241
+f 73/64/241 65/82/240 75/65/242
+f 63/109/243 73/64/244 71/19/245
+f 71/19/245 73/64/244 81/13/246
+f 62/15/247 72/83/248 70/21/249
+f 70/21/249 72/83/248 80/11/250
+f 66/67/251 67/48/252 76/49/253
+f 76/49/253 67/48/252 77/14/254
+f 65/82/255 68/1/256 75/65/257
+f 75/65/257 68/1/256 78/16/258
+f 69/12/259 71/19/260 79/62/261
+f 79/62/261 71/19/260 81/13/262
+f 67/48/263 70/21/264 77/14/265
+f 77/14/265 70/21/264 80/11/266
+f 64/108/267 66/67/268 74/66/269
+f 74/66/269 66/67/268 76/49/270
+f 82/68/271 92/17/272 89/22/273
+f 89/22/273 92/17/272 99/63/274
+f 84/2/275 88/5/276 94/59/277
+f 94/59/277 88/5/276 98/37/278
+f 83/6/279 93/7/280 85/8/281
+f 85/8/281 93/7/280 95/86/282
+f 83/6/283 91/32/284 93/7/285
+f 93/7/285 91/32/284 101/33/286
+f 82/68/287 90/38/288 92/17/289
+f 92/17/289 90/38/288 100/10/290
+f 86/84/291 96/34/292 87/87/293
+f 87/87/293 96/34/292 97/89/294
+f 85/8/295 95/86/296 88/5/297
+f 88/5/297 95/86/296 98/37/298
+f 89/22/299 99/63/300 91/32/301
+f 91/32/301 99/63/300 101/33/302
+f 87/87/303 97/89/304 90/38/305
+f 90/38/305 97/89/304 100/10/306
+f 84/2/307 94/59/308 86/84/309
+f 86/84/309 94/59/308 96/34/310
+f 75/65/311 78/16/312 95/72/313
+f 95/72/313 78/16/312 98/73/314
+f 67/48/315 66/67/316 87/87/317
+f 87/87/317 66/67/316 86/84/318
+f 74/66/319 76/49/320 94/39/321
+f 94/39/321 76/49/320 96/60/322
+f 70/21/323 67/48/324 90/38/325
+f 90/38/325 67/48/324 87/87/326
+f 71/19/327 69/12/328 91/32/329
+f 91/32/329 69/12/328 89/22/330
+f 73/64/331 75/65/332 93/88/333
+f 93/88/333 75/65/332 95/72/334
+f 64/108/335 68/1/336 84/2/337
+f 84/2/337 68/1/336 88/5/338
+f 69/12/339 62/15/340 89/22/341
+f 89/22/341 62/15/340 82/68/342
+f 76/49/343 77/14/344 96/60/345
+f 96/60/345 77/14/344 97/85/346
+f 62/15/347 70/21/348 82/68/349
+f 82/68/349 70/21/348 90/38/350
+f 63/109/351 71/19/352 83/6/353
+f 83/6/353 71/19/352 91/32/354
+f 77/14/355 80/11/356 97/85/357
+f 97/85/357 80/11/356 100/9/358
+f 79/62/359 81/13/360 99/36/361
+f 99/36/361 81/13/360 101/35/362
+f 68/1/363 65/82/364 88/5/365
+f 88/5/365 65/82/364 85/8/366
+f 78/16/367 74/66/368 98/73/369
+f 98/73/369 74/66/368 94/39/370
+f 72/83/371 79/62/372 92/71/373
+f 92/71/373 79/62/372 99/36/374
+f 66/67/375 64/108/376 86/84/377
+f 86/84/377 64/108/376 84/2/378
+f 80/11/379 72/83/380 100/9/381
+f 100/9/381 72/83/380 92/71/382
+f 81/13/383 73/64/384 101/35/385
+f 101/35/385 73/64/384 93/88/386
+f 65/82/387 63/109/388 85/8/389
+f 85/8/389 63/109/388 83/6/390
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_joystick.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_joystick.obj
new file mode 100644
index 0000000..c4daae3
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_joystick.obj
@@ -0,0 +1,556 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v 0.013683 0.008414 0.043000
+v 0.013683 0.013400 0.058800
+v 0.013683 0.008414 0.058800
+v 0.013683 0.013400 0.043000
+v -0.013683 0.013400 0.043000
+v -0.015800 0.013400 0.050900
+v 0.007900 0.013400 0.064583
+v 0.015800 0.013400 0.050900
+v -0.013683 0.013400 0.058800
+v -0.007900 0.013400 0.064583
+v -0.000000 0.013400 0.066700
+v 0.007900 0.013400 0.037217
+v 0.000000 0.013400 0.035100
+v -0.007900 0.013400 0.037217
+v 0.013247 0.013400 0.058548
+v 0.013247 0.013400 0.043252
+v -0.013247 0.013400 0.043252
+v -0.015297 0.013400 0.050900
+v 0.007648 0.013400 0.064148
+v 0.015297 0.013400 0.050900
+v -0.013247 0.013400 0.058548
+v -0.007648 0.013400 0.064148
+v -0.000000 0.013400 0.066197
+v 0.007648 0.013400 0.037652
+v 0.000000 0.013400 0.035603
+v -0.007648 0.013400 0.037652
+v 0.000000 0.013400 0.050900
+v -0.013683 0.008414 0.043000
+v -0.015800 0.008414 0.050900
+v 0.007900 0.008414 0.064583
+v 0.015800 0.008414 0.050900
+v -0.013683 0.008414 0.058800
+v -0.007900 0.008414 0.064583
+v -0.000000 0.008414 0.066700
+v 0.007900 0.008414 0.037217
+v 0.000000 0.008414 0.035100
+v -0.007900 0.008414 0.037217
+v 0.015123 0.008414 0.042169
+v 0.015123 0.008414 0.059631
+v -0.015123 0.008414 0.042169
+v -0.017462 0.008414 0.050900
+v 0.008731 0.008414 0.066023
+v 0.017462 0.008414 0.050900
+v -0.015123 0.008414 0.059631
+v -0.008731 0.008414 0.066023
+v -0.000000 0.008414 0.068362
+v 0.008731 0.008414 0.035777
+v 0.000000 0.008414 0.033438
+v -0.008731 0.008414 0.035777
+v -0.007900 0.013082 0.037217
+v 0.000000 0.013082 0.035100
+v 0.007900 0.013082 0.037217
+v -0.000000 0.013082 0.066700
+v -0.007900 0.013082 0.064583
+v -0.013683 0.013082 0.058800
+v 0.013683 0.013082 0.043000
+v 0.015800 0.013082 0.050900
+v 0.013683 0.013082 0.058800
+v 0.007900 0.013082 0.064583
+v -0.015800 0.013082 0.050900
+v -0.013683 0.013082 0.043000
+v -0.007900 0.008785 0.064583
+v 0.015800 0.008785 0.050900
+v 0.007900 0.008785 0.064583
+v -0.013683 0.008785 0.043000
+v -0.007900 0.008785 0.037217
+v 0.000000 0.008785 0.035100
+v 0.007900 0.008785 0.037217
+v -0.000000 0.008785 0.066700
+v -0.013683 0.008785 0.058800
+v 0.013683 0.008785 0.043000
+v 0.013683 0.008785 0.058800
+v -0.015800 0.008785 0.050900
+vt 0.598611 0.792613
+vt 0.581181 0.842714
+vt 0.572095 0.840361
+vt 0.583337 0.843249
+vt 0.583900 0.891691
+vt 0.740650 0.932332
+vt 0.764677 0.890275
+vt 0.764114 0.841833
+vt 0.623873 0.817102
+vt 0.649036 0.776482
+vt 0.607278 0.849202
+vt 0.609406 0.849762
+vt 0.609695 0.884296
+vt 0.626984 0.914208
+vt 0.624971 0.818153
+vt 0.626666 0.819833
+vt 0.740622 0.848600
+vt 0.740736 0.884322
+vt 0.738608 0.883762
+vt 0.738319 0.849228
+vt 0.742081 0.848175
+vt 0.605933 0.885349
+vt 0.698978 0.957041
+vt 0.724141 0.916422
+vt 0.650543 0.957262
+vt 0.691947 0.798514
+vt 0.742213 0.884683
+vt 0.697472 0.776262
+vt 0.657036 0.931225
+vt 0.691585 0.931208
+vt 0.625435 0.915771
+vt 0.747828 0.791704
+vt 0.766832 0.890810
+vt 0.766241 0.841221
+vt 0.775920 0.838541
+vt 0.775919 0.893164
+vt 0.741246 0.798397
+vt 0.607392 0.884924
+vt 0.739706 0.799996
+vt 0.723631 0.816655
+vt 0.655425 0.798733
+vt 0.650009 0.959409
+vt 0.647491 0.969131
+vt 0.648421 0.774349
+vt 0.700524 0.764393
+vt 0.674007 0.866762
+vt 0.699593 0.959175
+vt 0.742243 0.933868
+vt 0.749403 0.940910
+vt 0.702098 0.968221
+vt 0.581773 0.892303
+vt 0.606768 0.935127
+vt 0.600187 0.941819
+vt 0.572095 0.894983
+vt 0.698005 0.774114
+vt 0.607364 0.801191
+vt 0.692589 0.934791
+vt 0.656067 0.935009
+vt 0.608309 0.933528
+vt 0.605801 0.848841
+vt 0.624384 0.916869
+vt 0.692164 0.933331
+vt 0.656428 0.933533
+vt 0.655850 0.800192
+vt 0.691586 0.799991
+vt 0.690978 0.802299
+vt 0.656429 0.802316
+vt 0.723043 0.915371
+vt 0.721348 0.913691
+vt 0.722579 0.817753
+vt 0.721030 0.819316
+vt 0.645916 0.765302
+vt 0.605771 0.799655
+vn 0.552274 0.770272 0.318864
+vn 0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 0.552274
+vn 0.000000 0.770266 0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.378558 0.653230 -0.655732
+vn -0.655715 0.653243 -0.378566
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.655715 0.653243 -0.378566
+vn 0.378558 0.653230 -0.655732
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.378558 0.653230 -0.655732
+vn 0.000000 0.653236 -0.757154
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.653236 -0.757154
+vn -0.378558 0.653230 -0.655732
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.655715 0.653243 -0.378566
+vn -0.757167 0.653221 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.378558 0.653230 0.655732
+vn 0.655715 0.653243 0.378566
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.655715 0.653243 0.378566
+vn -0.378558 0.653230 0.655732
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.757167 0.653221 0.000000
+vn 0.655715 0.653243 -0.378566
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.653236 0.757154
+vn 0.378558 0.653230 0.655732
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.378558 0.653230 0.655732
+vn 0.000000 0.653236 0.757154
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.655715 0.653243 0.378566
+vn 0.757167 0.653221 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.757167 0.653221 0.000000
+vn -0.655715 0.653243 0.378566
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.552274 0.770272 -0.318864
+vn 0.637723 0.770266 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 0.770266 -0.637723
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 0.637723
+vn -0.318864 0.770272 0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.318864 0.770272 -0.552274
+vn 0.552274 0.770272 -0.318864
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.318864 0.770272 0.552274
+vn -0.552274 0.770272 0.318864
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.552274 0.770272 0.318864
+vn -0.637723 0.770266 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.552274 0.770272 -0.318864
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.637723 0.770266 0.000000
+vn 0.552274 0.770272 0.318864
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn 0.000000 0.770266 -0.637723
+vn 0.318864 0.770272 -0.552274
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.637723 0.770266 0.000000
+vn -0.552274 0.770272 -0.318864
+vn 0.000000 1.000000 0.000000
+vn 0.000000 1.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn -0.499989 0.000000 -0.866032
+vn -0.552274 0.770272 -0.318864
+vn -0.318864 0.770272 -0.552274
+vn 1.000000 0.000000 0.000000
+vn 0.866032 -0.000000 0.499989
+vn 0.637723 0.770266 0.000000
+vn 0.552274 0.770272 0.318864
+vn 0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 0.318864 0.770272 0.552274
+vn 0.000000 0.770266 0.637723
+vn -0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn -0.318864 0.770272 -0.552274
+vn 0.000000 0.770266 -0.637723
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 0.000000 -0.866032
+vn 0.000000 0.770266 -0.637723
+vn 0.318864 0.770272 -0.552274
+vn 0.499989 0.000000 -0.866032
+vn 0.866032 0.000000 -0.499989
+vn 0.318864 0.770272 -0.552274
+vn 0.552274 0.770272 -0.318864
+vn -0.499989 -0.000000 0.866032
+vn -0.866032 -0.000000 0.499989
+vn -0.318864 0.770272 0.552274
+vn -0.552274 0.770272 0.318864
+vn 0.000000 -0.000000 1.000000
+vn -0.499989 -0.000000 0.866032
+vn 0.000000 0.770266 0.637723
+vn -0.318864 0.770272 0.552274
+vn -0.866032 -0.000000 0.499989
+vn -1.000000 0.000000 0.000000
+vn -0.552274 0.770272 0.318864
+vn -0.637723 0.770266 0.000000
+vn 0.866032 0.000000 -0.499989
+vn 1.000000 0.000000 0.000000
+vn 0.552274 0.770272 -0.318864
+vn 0.637723 0.770266 0.000000
+vn 0.866032 -0.000000 0.499989
+vn 0.499989 -0.000000 0.866032
+vn 0.552274 0.770272 0.318864
+vn 0.318864 0.770272 0.552274
+vn -1.000000 0.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn -0.637723 0.770266 0.000000
+vn -0.552274 0.770272 -0.318864
+vn -0.757167 0.653221 0.000000
+vn -0.655715 0.653243 -0.378566
+vn -1.000000 0.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn 0.655715 0.653243 0.378566
+vn 0.378558 0.653230 0.655732
+vn 0.866032 -0.000000 0.499989
+vn 0.499989 -0.000000 0.866032
+vn 0.655715 0.653243 -0.378566
+vn 0.757167 0.653221 0.000000
+vn 0.866032 0.000000 -0.499989
+vn 1.000000 0.000000 0.000000
+vn -0.655715 0.653243 0.378566
+vn -0.757167 0.653221 0.000000
+vn -0.866032 -0.000000 0.499989
+vn -1.000000 0.000000 0.000000
+vn 0.000000 0.653236 0.757154
+vn -0.378558 0.653230 0.655732
+vn 0.000000 -0.000000 1.000000
+vn -0.499989 -0.000000 0.866032
+vn -0.378558 0.653230 0.655732
+vn -0.655715 0.653243 0.378566
+vn -0.499989 -0.000000 0.866032
+vn -0.866032 -0.000000 0.499989
+vn 0.378558 0.653230 -0.655732
+vn 0.655715 0.653243 -0.378566
+vn 0.499989 0.000000 -0.866032
+vn 0.866032 0.000000 -0.499989
+vn 0.000000 0.653236 -0.757154
+vn 0.378558 0.653230 -0.655732
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 0.000000 -0.866032
+vn -0.378558 0.653230 -0.655732
+vn 0.000000 0.653236 -0.757154
+vn -0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn 0.378558 0.653230 0.655732
+vn 0.000000 0.653236 0.757154
+vn 0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 0.757167 0.653221 0.000000
+vn 0.655715 0.653243 0.378566
+vn 1.000000 0.000000 0.000000
+vn 0.866032 -0.000000 0.499989
+vn -0.655715 0.653243 -0.378566
+vn -0.378558 0.653230 -0.655732
+vn -0.866032 0.000000 -0.499989
+vn -0.499989 0.000000 -0.866032
+vn -1.000000 0.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn -1.000000 0.000000 0.000000
+vn -0.866032 0.000000 -0.499989
+vn 0.866032 -0.000000 0.499989
+vn 0.499989 -0.000000 0.866032
+vn 0.866032 -0.000000 0.499989
+vn 0.499989 -0.000000 0.866032
+vn 0.866032 0.000000 -0.499989
+vn 1.000000 0.000000 0.000000
+vn 0.866032 0.000000 -0.499989
+vn 1.000000 0.000000 0.000000
+vn -0.866032 -0.000000 0.499989
+vn -1.000000 0.000000 0.000000
+vn -0.866032 -0.000000 0.499989
+vn -1.000000 0.000000 0.000000
+vn 0.000000 -0.000000 1.000000
+vn -0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn -0.499989 -0.000000 0.866032
+vn -0.499989 -0.000000 0.866032
+vn -0.866032 -0.000000 0.499989
+vn -0.499989 -0.000000 0.866032
+vn -0.866032 -0.000000 0.499989
+vn 0.499989 0.000000 -0.866032
+vn 0.866032 0.000000 -0.499989
+vn 0.499989 0.000000 -0.866032
+vn 0.866032 0.000000 -0.499989
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 0.000000 -0.866032
+vn -0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn -0.499989 0.000000 -0.866032
+vn 0.000000 0.000000 -1.000000
+vn 0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 0.499989 -0.000000 0.866032
+vn 0.000000 -0.000000 1.000000
+vn 1.000000 0.000000 0.000000
+vn 0.866032 -0.000000 0.499989
+vn 1.000000 0.000000 0.000000
+vn 0.866032 -0.000000 0.499989
+vn -0.866032 0.000000 -0.499989
+vn -0.499989 0.000000 -0.866032
+vn -0.866032 0.000000 -0.499989
+vn -0.499989 0.000000 -0.866032
+f 3/33/1 30/34/2 39/36/3
+f 39/36/3 30/34/2 42/35/4
+f 30/34/5 34/37/6 42/35/7
+f 42/35/7 34/37/6 46/32/8
+f 14/38/9 5/11/10 26/13/11
+f 26/13/11 5/11/10 17/12/12
+f 4/62/13 12/63/14 16/30/15
+f 16/30/15 12/63/14 24/29/16
+f 12/63/17 13/31/18 24/29/19
+f 24/29/19 13/31/18 25/14/20
+f 13/31/21 14/38/22 25/14/23
+f 25/14/23 14/38/22 26/13/24
+f 5/11/25 6/15/26 17/12/27
+f 17/12/27 6/15/26 18/16/28
+f 7/17/29 2/18/30 19/20/31
+f 19/20/31 2/18/30 15/19/32
+f 9/64/33 10/65/34 21/67/35
+f 21/67/35 10/65/34 22/66/36
+f 8/68/37 4/62/38 20/69/39
+f 20/69/39 4/62/38 16/30/40
+f 11/70/41 7/17/42 23/71/43
+f 23/71/43 7/17/42 19/20/44
+f 10/65/45 11/70/46 22/66/47
+f 22/66/47 11/70/46 23/71/48
+f 2/18/49 8/68/50 15/19/51
+f 15/19/51 8/68/50 20/69/52
+f 6/15/53 9/64/54 18/16/55
+f 18/16/55 9/64/54 21/67/56
+f 20/69/57 16/30/58 27/46/59
+f 19/20/60 15/19/61 27/46/62
+f 16/30/63 24/29/64 27/46/65
+f 22/66/66 23/71/67 27/46/68
+f 21/67/69 22/66/70 27/46/71
+f 18/16/72 21/67/73 27/46/74
+f 23/71/75 19/20/76 27/46/77
+f 26/13/78 17/12/79 27/46/80
+f 24/29/81 25/14/82 27/46/83
+f 15/19/84 20/69/85 27/46/86
+f 17/12/87 18/16/88 27/46/89
+f 25/14/90 26/13/91 27/46/92
+f 1/47/93 31/48/94 38/50/95
+f 38/50/95 31/48/94 43/49/96
+f 37/51/97 36/52/98 49/54/99
+f 49/54/99 36/52/98 48/53/100
+f 34/37/101 33/55/102 46/32/103
+f 46/32/103 33/55/102 45/45/104
+f 35/42/105 1/47/106 47/43/107
+f 47/43/107 1/47/106 38/50/108
+f 33/55/109 32/44/110 45/45/111
+f 45/45/111 32/44/110 44/72/112
+f 32/44/113 29/73/114 44/72/115
+f 44/72/115 29/73/114 41/1/116
+f 28/2/117 37/51/118 40/3/119
+f 40/3/119 37/51/118 49/54/120
+f 31/48/121 3/33/122 43/49/123
+f 43/49/123 3/33/122 39/36/124
+f 36/52/125 35/42/126 48/53/127
+f 48/53/127 35/42/126 47/43/128
+f 29/73/129 28/2/130 41/1/131
+f 41/1/131 28/2/130 40/3/132
+f 65/4/133 66/5/134 28/2/135
+f 28/2/135 66/5/134 37/51/136
+f 63/6/137 72/7/138 31/48/139
+f 31/48/139 72/7/138 3/33/140
+f 64/8/141 69/39/142 30/34/143
+f 30/34/143 69/39/142 34/37/144
+f 66/5/145 67/59/146 37/51/147
+f 37/51/147 67/59/146 36/52/148
+f 67/59/149 68/25/150 36/52/151
+f 36/52/151 68/25/150 35/42/152
+f 68/25/153 71/23/154 35/42/155
+f 35/42/155 71/23/154 1/47/156
+f 62/28/157 70/10/158 33/55/159
+f 33/55/159 70/10/158 32/44/160
+f 69/39/161 62/28/162 34/37/163
+f 34/37/163 62/28/162 33/55/164
+f 70/10/165 73/56/166 32/44/167
+f 32/44/167 73/56/166 29/73/168
+f 71/23/169 63/6/170 1/47/171
+f 1/47/171 63/6/170 31/48/172
+f 72/7/173 64/8/174 3/33/175
+f 3/33/175 64/8/174 30/34/176
+f 73/56/177 65/4/178 29/73/179
+f 29/73/179 65/4/178 28/2/180
+f 6/15/181 5/11/182 60/9/183
+f 60/9/183 5/11/182 61/60/184
+f 2/18/185 7/17/186 58/27/187
+f 58/27/187 7/17/186 59/21/188
+f 4/62/189 8/68/190 56/57/191
+f 56/57/191 8/68/190 57/24/192
+f 9/64/193 6/15/194 55/41/195
+f 55/41/195 6/15/194 60/9/196
+f 11/70/197 10/65/198 53/40/199
+f 53/40/199 10/65/198 54/26/200
+f 10/65/201 9/64/202 54/26/203
+f 54/26/203 9/64/202 55/41/204
+f 12/63/205 4/62/206 52/58/207
+f 52/58/207 4/62/206 56/57/208
+f 13/31/209 12/63/210 51/61/211
+f 51/61/211 12/63/210 52/58/212
+f 14/38/213 13/31/214 50/22/215
+f 50/22/215 13/31/214 51/61/216
+f 7/17/217 11/70/218 59/21/219
+f 59/21/219 11/70/218 53/40/220
+f 8/68/221 2/18/222 57/24/223
+f 57/24/223 2/18/222 58/27/224
+f 5/11/225 14/38/226 61/60/227
+f 61/60/227 14/38/226 50/22/228
+f 60/9/229 61/60/230 73/56/231
+f 73/56/231 61/60/230 65/4/232
+f 58/27/233 59/21/234 72/7/235
+f 72/7/235 59/21/234 64/8/236
+f 56/57/237 57/24/238 71/23/239
+f 71/23/239 57/24/238 63/6/240
+f 55/41/241 60/9/242 70/10/243
+f 70/10/243 60/9/242 73/56/244
+f 53/40/245 54/26/246 69/39/247
+f 69/39/247 54/26/246 62/28/248
+f 54/26/249 55/41/250 62/28/251
+f 62/28/251 55/41/250 70/10/252
+f 52/58/253 56/57/254 68/25/255
+f 68/25/255 56/57/254 71/23/256
+f 51/61/257 52/58/258 67/59/259
+f 67/59/259 52/58/258 68/25/260
+f 50/22/261 51/61/262 66/5/263
+f 66/5/263 51/61/262 67/59/264
+f 59/21/265 53/40/266 64/8/267
+f 64/8/267 53/40/266 69/39/268
+f 57/24/269 58/27/270 63/6/271
+f 63/6/271 58/27/270 72/7/272
+f 61/60/273 50/22/274 65/4/275
+f 65/4/275 50/22/274 66/5/276
diff --git a/libs/vr/libdvrgraphics/assets/controller_proto2_trigger.obj b/libs/vr/libdvrgraphics/assets/controller_proto2_trigger.obj
new file mode 100644
index 0000000..7b87239
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/controller_proto2_trigger.obj
@@ -0,0 +1,1240 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v 0.012469 0.002342 0.063363
+v 0.009351 -0.007408 0.065472
+v 0.010658 -0.006647 0.065307
+v 0.006441 -0.008510 0.065710
+v 0.003284 -0.009190 0.065857
+v 0.012469 -0.001545 0.064204
+v 0.012469 -0.003489 0.064624
+v 0.012469 0.002342 0.037855
+v 0.012469 0.002342 0.035622
+v 0.012469 0.002342 0.040088
+v 0.012469 0.002342 0.042320
+v 0.010019 -0.002861 0.035742
+v 0.010019 -0.009748 0.045459
+v 0.008153 -0.014181 0.046940
+v 0.006671 -0.015226 0.047180
+v 0.010019 -0.012511 0.046558
+v 0.010019 -0.004041 0.040214
+v 0.010019 -0.007400 0.044006
+v 0.010019 -0.005473 0.042235
+v 0.010019 -0.003159 0.038022
+v 0.004895 -0.016332 0.047274
+v 0.002649 -0.002861 0.035742
+v 0.002649 -0.009748 0.045459
+v 0.002649 -0.014181 0.046940
+v 0.002649 -0.015226 0.047180
+v 0.002649 -0.012511 0.046558
+v 0.002649 -0.004041 0.040214
+v 0.002649 -0.007400 0.044006
+v 0.002649 -0.005473 0.042235
+v 0.002649 -0.003159 0.038022
+v 0.002649 -0.016789 0.047312
+v 0.012469 -0.001216 0.035704
+v 0.012469 -0.001420 0.037969
+v 0.012469 -0.002023 0.040174
+v 0.012469 -0.003002 0.042262
+v 0.010658 -0.013756 0.048907
+v 0.003284 -0.017321 0.049383
+v 0.006441 -0.016360 0.049298
+v 0.009351 -0.014801 0.049145
+v 0.012469 -0.009331 0.047481
+v 0.012469 -0.006949 0.046118
+v 0.012469 0.002342 0.046118
+v 0.012469 -0.001298 0.035706
+v 0.011089 -0.002861 0.035742
+v 0.012469 -0.001595 0.037974
+v 0.011089 -0.003159 0.038022
+v 0.012469 -0.002483 0.040183
+v 0.011089 -0.004041 0.040214
+v 0.012469 -0.003949 0.042252
+v 0.011089 -0.005473 0.042235
+v 0.012469 -0.007147 0.045190
+v 0.011089 -0.007400 0.044006
+v 0.012469 -0.009503 0.046649
+v 0.011089 -0.009748 0.045459
+v 0.011089 -0.012511 0.046558
+v 0.012469 -0.010955 0.047226
+v 0.011466 -0.013144 0.048119
+v 0.011466 -0.006105 0.065190
+v 0.012469 -0.004454 0.064833
+v 0.007971 -0.015226 0.047180
+v 0.009351 -0.014935 0.048529
+v 0.010658 -0.013889 0.048289
+v 0.009277 -0.014181 0.046940
+v 0.003284 -0.017462 0.048733
+v 0.002990 -0.017308 0.047351
+v 0.006441 -0.016497 0.048661
+v 0.005551 -0.016526 0.047290
+v 0.012469 -0.010781 0.048032
+v 0.011466 -0.013010 0.048737
+v 0.000000 -0.003159 0.038022
+v 0.000000 -0.009748 0.045459
+v 0.000000 -0.007400 0.044006
+v 0.000000 -0.002861 0.035742
+v 0.000000 -0.009419 0.065907
+v 0.000000 -0.005473 0.042235
+v -0.012469 0.002342 0.063363
+v 0.000000 -0.014181 0.046940
+v 0.000000 -0.015226 0.047180
+v 0.000000 -0.012511 0.046558
+v 0.000000 -0.004041 0.040214
+v 0.000000 -0.016789 0.047312
+v -0.009351 -0.007408 0.065472
+v -0.010658 -0.006647 0.065307
+v -0.006441 -0.008510 0.065710
+v -0.003284 -0.009190 0.065857
+v -0.012469 -0.001545 0.064204
+v -0.012469 -0.003489 0.064624
+v -0.012469 0.002342 0.037855
+v -0.012469 0.002342 0.035622
+v -0.012469 0.002342 0.040088
+v -0.012469 0.002342 0.042320
+v -0.010019 -0.002861 0.035742
+v -0.010019 -0.009748 0.045459
+v -0.008153 -0.014181 0.046940
+v -0.006671 -0.015226 0.047180
+v -0.010019 -0.012511 0.046558
+v -0.010019 -0.004041 0.040214
+v -0.010019 -0.007400 0.044006
+v -0.010019 -0.005473 0.042235
+v -0.010019 -0.003159 0.038022
+v -0.004895 -0.016332 0.047274
+v -0.002649 -0.002861 0.035742
+v -0.002649 -0.009748 0.045459
+v -0.002649 -0.014181 0.046940
+v -0.002649 -0.015226 0.047180
+v -0.002649 -0.012511 0.046558
+v -0.002649 -0.004041 0.040214
+v -0.002649 -0.007400 0.044006
+v -0.002649 -0.005473 0.042235
+v -0.002649 -0.003159 0.038022
+v -0.002649 -0.016789 0.047312
+v -0.012469 -0.001216 0.035704
+v -0.012469 -0.001420 0.037969
+v -0.012469 -0.002023 0.040174
+v -0.012469 -0.003002 0.042262
+v -0.010658 -0.013756 0.048907
+v 0.000000 -0.017646 0.049412
+v -0.003284 -0.017321 0.049383
+v -0.006441 -0.016360 0.049298
+v -0.009351 -0.014801 0.049145
+v -0.012469 -0.009331 0.047481
+v -0.012469 -0.006949 0.046118
+v -0.012469 0.002342 0.046118
+v -0.012469 -0.001298 0.035706
+v -0.011089 -0.002861 0.035742
+v -0.012469 -0.001595 0.037974
+v -0.011089 -0.003159 0.038022
+v -0.012469 -0.002483 0.040183
+v -0.011089 -0.004041 0.040214
+v -0.012469 -0.003949 0.042252
+v -0.011089 -0.005473 0.042235
+v -0.012469 -0.007147 0.045190
+v -0.011089 -0.007400 0.044006
+v -0.012469 -0.009503 0.046649
+v -0.011089 -0.009748 0.045459
+v -0.011089 -0.012511 0.046558
+v -0.012469 -0.010955 0.047226
+v -0.011466 -0.013144 0.048119
+v -0.011466 -0.006105 0.065190
+v -0.012469 -0.004454 0.064833
+v -0.007971 -0.015226 0.047180
+v -0.009351 -0.014935 0.048529
+v -0.010658 -0.013889 0.048289
+v -0.009277 -0.014181 0.046940
+v -0.003284 -0.017462 0.048733
+v -0.002990 -0.017308 0.047351
+v 0.000000 -0.017604 0.047373
+v 0.000000 -0.017787 0.048757
+v -0.006441 -0.016497 0.048661
+v -0.005551 -0.016526 0.047290
+v -0.012469 -0.010781 0.048032
+v -0.011466 -0.013010 0.048737
+v 0.000000 -0.016909 0.052819
+v 0.003284 -0.016586 0.052783
+v 0.006441 -0.015629 0.052675
+v 0.012469 -0.008622 0.050921
+v 0.012469 -0.006181 0.049710
+v 0.012469 0.002342 0.049484
+v 0.010658 -0.013032 0.052252
+v 0.009351 -0.014078 0.052488
+v 0.011466 -0.012286 0.052084
+v 0.012469 -0.010048 0.051420
+v -0.003284 -0.016586 0.052783
+v -0.006441 -0.015629 0.052675
+v -0.012469 -0.008622 0.050921
+v -0.012469 -0.006181 0.049710
+v -0.012469 0.002342 0.049484
+v -0.010658 -0.013032 0.052252
+v -0.009351 -0.014078 0.052488
+v -0.011466 -0.012286 0.052084
+v -0.012469 -0.010048 0.051420
+vt 0.389626 0.855156
+vt 0.390521 0.876103
+vt 0.497178 0.870536
+vt 0.484333 0.855130
+vt 0.412289 0.967976
+vt 0.478403 0.774806
+vt 0.463433 0.778867
+vt 0.412380 0.809901
+vt 0.409798 0.834762
+vt 0.396714 0.803798
+vt 0.445493 0.843045
+vt 0.390505 0.834206
+vt 0.408648 0.830875
+vt 0.389815 0.844760
+vt 0.330514 0.823054
+vt 0.432027 0.965825
+vt 0.391682 0.823360
+vt 0.437068 0.901524
+vt 0.402269 0.864596
+vt 0.493086 0.959935
+vt 0.480141 0.959822
+vt 0.407446 0.893095
+vt 0.404279 0.890129
+vt 0.494272 0.915752
+vt 0.405408 0.893816
+vt 0.445499 0.867230
+vt 0.458613 0.855135
+vt 0.434528 0.906466
+vt 0.478084 0.921347
+vt 0.433534 0.865962
+vt 0.458054 0.868399
+vt 0.392761 0.817328
+vt 0.404255 0.820169
+vt 0.336182 0.937153
+vt 0.405821 0.786943
+vt 0.413215 0.855147
+vt 0.416743 0.904914
+vt 0.413479 0.906889
+vt 0.389822 0.865551
+vt 0.328339 0.855183
+vt 0.406268 0.863663
+vt 0.405868 0.923348
+vt 0.393637 0.897111
+vt 0.478510 0.773673
+vt 0.409635 0.808495
+vt 0.403113 0.836120
+vt 0.402263 0.845706
+vt 0.407910 0.840728
+vt 0.497174 0.839720
+vt 0.478060 0.788901
+vt 0.329051 0.838946
+vt 0.334987 0.787175
+vt 0.401168 0.835782
+vt 0.448778 0.784859
+vt 0.447886 0.797316
+vt 0.462582 0.792299
+vt 0.412210 0.742297
+vt 0.422076 0.845553
+vt 0.416711 0.805374
+vt 0.464373 0.798874
+vt 0.431956 0.744434
+vt 0.450212 0.930782
+vt 0.427474 0.913081
+vt 0.423235 0.915539
+vt 0.399149 0.912516
+vt 0.335447 0.927703
+vt 0.335050 0.923173
+vt 0.412698 0.863505
+vt 0.416158 0.863975
+vt 0.411799 0.881077
+vt 0.409812 0.875534
+vt 0.493410 0.773030
+vt 0.413445 0.803401
+vt 0.402794 0.825572
+vt 0.437042 0.808751
+vt 0.400328 0.864755
+vt 0.408394 0.855149
+vt 0.333617 0.909517
+vt 0.332644 0.902621
+vt 0.403126 0.874181
+vt 0.422082 0.864736
+vt 0.416711 0.855146
+vt 0.402042 0.855151
+vt 0.422934 0.899416
+vt 0.334248 0.913876
+vt 0.450172 0.779477
+vt 0.493052 0.750284
+vt 0.458048 0.841870
+vt 0.433528 0.844320
+vt 0.434498 0.803810
+vt 0.494257 0.794492
+vt 0.410463 0.825689
+vt 0.415040 0.889351
+vt 0.471407 0.855132
+vt 0.446098 0.855138
+vt 0.479212 0.914296
+vt 0.493852 0.922939
+vt 0.410483 0.884606
+vt 0.447916 0.912950
+vt 0.434120 0.855141
+vt 0.470937 0.869361
+vt 0.406318 0.889536
+vt 0.493399 0.772491
+vt 0.332599 0.807735
+vt 0.467185 0.749518
+vt 0.413259 0.817561
+vt 0.393606 0.813194
+vt 0.423197 0.794742
+vt 0.479191 0.795954
+vt 0.406262 0.846637
+vt 0.406068 0.855150
+vt 0.407893 0.847406
+vt 0.406295 0.820760
+vt 0.406924 0.838997
+vt 0.399108 0.797782
+vt 0.422669 0.855144
+vt 0.407898 0.862892
+vt 0.391706 0.886948
+vt 0.401182 0.874520
+vt 0.404771 0.884253
+vt 0.483983 0.870072
+vt 0.400087 0.855152
+vt 0.329066 0.871418
+vt 0.330545 0.887307
+vt 0.497404 0.855128
+vt 0.416152 0.846318
+vt 0.454204 0.747884
+vt 0.404751 0.826046
+vt 0.411782 0.829217
+vt 0.400321 0.845549
+vt 0.463943 0.775990
+vt 0.337941 0.751811
+vt 0.333566 0.800837
+vt 0.412692 0.846790
+vt 0.483978 0.840187
+vt 0.334192 0.796476
+vt 0.493834 0.787302
+vt 0.450179 0.803181
+vt 0.408665 0.879421
+vt 0.406935 0.871302
+vt 0.407920 0.869569
+vt 0.467237 0.960717
+vt 0.454263 0.962359
+vt 0.450205 0.907085
+vt 0.464397 0.911384
+vt 0.462610 0.917958
+vt 0.402814 0.884728
+vt 0.425386 0.895350
+vt 0.336106 0.773189
+vt 0.422907 0.810868
+vt 0.338033 0.958524
+vt 0.412410 0.900390
+vt 0.409666 0.901797
+vt 0.396751 0.906503
+vt 0.463979 0.934259
+vt 0.478433 0.935435
+vt 0.493434 0.937204
+vt 0.493422 0.937742
+vt 0.478541 0.936568
+vt 0.463468 0.931384
+vt 0.448815 0.925403
+vt 0.427439 0.797198
+vt 0.480098 0.750404
+vt 0.425362 0.814933
+vt 0.335380 0.782643
+vt 0.470932 0.840902
+vt 0.405382 0.816481
+vt 0.415019 0.820940
+vt 0.407421 0.817201
+vt 0.392788 0.892978
+vt 0.413283 0.892731
+vn 0.941739 -0.326680 -0.080053
+vn 0.945242 -0.323626 -0.042239
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.933306 -0.319627 -0.163644
+vn 0.941739 -0.326680 -0.080053
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.919246 -0.303577 -0.250656
+vn 0.933306 -0.319627 -0.163644
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.911448 -0.243205 -0.331832
+vn 0.919246 -0.303577 -0.250656
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.911448 -0.243205 -0.331832
+vn 1.000000 0.000000 0.000000
+vn 0.916678 -0.176432 -0.358571
+vn 1.000000 0.000000 0.000000
+vn 0.916678 -0.176432 -0.358571
+vn 1.000000 0.000000 0.000000
+vn 0.910980 -0.276630 -0.305928
+vn 0.979394 -0.197399 0.042666
+vn 0.973672 -0.216165 0.072360
+vn 1.000000 0.000000 0.000000
+vn 0.978343 -0.189186 0.083988
+vn 1.000000 0.000000 0.000000
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.161752 -0.860157 0.483701
+vn 0.000000 -0.869145 0.494557
+vn 0.178204 -0.920500 0.347740
+vn 0.000000 -0.934880 0.354963
+vn 0.325059 -0.831219 0.451012
+vn 0.161752 -0.860157 0.483701
+vn 0.352896 -0.876974 0.326161
+vn 0.178204 -0.920500 0.347740
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn -0.000092 -0.079746 -0.996815
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.079380 -0.996844
+vn 0.596627 -0.720598 0.353234
+vn 0.491395 -0.774189 0.398951
+vn 0.629820 -0.732455 0.258526
+vn 0.520259 -0.802134 0.293106
+vn 0.352896 -0.876974 0.326161
+vn 0.520259 -0.802134 0.293106
+vn 0.325059 -0.831219 0.451012
+vn 0.491395 -0.774189 0.398951
+vn 0.382681 -0.903004 0.195293
+vn 0.195076 -0.958627 0.207314
+vn 0.395898 -0.918084 -0.019654
+vn 0.200818 -0.979187 0.029421
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.291820 -0.619012 -0.729154
+vn 0.221873 -0.646208 -0.730197
+vn 0.000000 -0.154215 -0.988037
+vn -0.000244 -0.081790 -0.996650
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.341206 -0.695262 -0.632605
+vn 0.383839 -0.804668 -0.452965
+vn 0.395898 -0.918084 -0.019654
+vn 0.552792 -0.829295 -0.081791
+vn 0.382681 -0.903004 0.195293
+vn 0.555564 -0.812685 0.175758
+vn 0.772999 -0.620157 -0.133706
+vn 0.819595 -0.559998 0.121101
+vn 0.635399 -0.761197 -0.129796
+vn 0.661102 -0.733340 0.158607
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.449678 -0.893191
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449678 -0.893191
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.154215 -0.988037
+vn -0.000244 -0.081790 -0.996650
+vn 0.000000 -0.154215 -0.988037
+vn -0.000092 -0.079746 -0.996815
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.195076 -0.958627 0.207314
+vn 0.000000 -0.977405 0.211375
+vn 0.200818 -0.979187 0.029421
+vn 0.000000 -0.999153 0.041139
+vn 0.221873 -0.646208 -0.730197
+vn 0.120368 -0.692302 -0.711498
+vn -0.000244 -0.081790 -0.996650
+vn -0.000092 -0.079746 -0.996815
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.333728 -0.575562 -0.746561
+vn 0.341206 -0.695262 -0.632605
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.412992 -0.879180 -0.237657
+vn 0.422817 -0.898585 -0.117347
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.383839 -0.804668 -0.452965
+vn 0.412992 -0.879180 -0.237657
+vn 0.457368 -0.496250 -0.737936
+vn 0.313953 -0.551668 -0.772720
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.457368 -0.496250 -0.737936
+vn 0.350609 -0.424894 -0.834589
+vn 0.635399 -0.761197 -0.129796
+vn 0.661102 -0.733340 0.158607
+vn 0.552792 -0.829295 -0.081791
+vn 0.555564 -0.812685 0.175758
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.291820 -0.619012 -0.729154
+vn 0.313953 -0.551668 -0.772720
+vn 0.120368 -0.692302 -0.711498
+vn 0.000000 -0.707900 -0.706313
+vn -0.000092 -0.079746 -0.996815
+vn 0.000000 -0.079380 -0.996844
+vn 0.788994 -0.557075 0.259142
+vn 0.596627 -0.720598 0.353234
+vn 0.791884 -0.577089 0.199718
+vn 0.629820 -0.732455 0.258526
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.350609 -0.424894 -0.834589
+vn 0.333728 -0.575562 -0.746561
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.457368 -0.496250 -0.737936
+vn 0.910980 -0.276630 -0.305928
+vn 0.772999 -0.620157 -0.133706
+vn 0.313953 -0.551668 -0.772720
+vn 0.457368 -0.496250 -0.737936
+vn 0.635399 -0.761197 -0.129796
+vn 0.772999 -0.620157 -0.133706
+vn 0.945242 -0.323626 -0.042239
+vn 0.941739 -0.326680 -0.080053
+vn 0.422817 -0.898585 -0.117347
+vn 0.412992 -0.879180 -0.237657
+vn 0.941739 -0.326680 -0.080053
+vn 0.933306 -0.319627 -0.163644
+vn 0.412992 -0.879180 -0.237657
+vn 0.383839 -0.804668 -0.452965
+vn 0.933306 -0.319627 -0.163644
+vn 0.919246 -0.303577 -0.250656
+vn 0.383839 -0.804668 -0.452965
+vn 0.341206 -0.695262 -0.632605
+vn 0.919246 -0.303577 -0.250656
+vn 0.911448 -0.243205 -0.331832
+vn 0.341206 -0.695262 -0.632605
+vn 0.333728 -0.575562 -0.746561
+vn 0.916678 -0.176432 -0.358571
+vn 0.350609 -0.424894 -0.834589
+vn 0.911448 -0.243205 -0.331832
+vn 0.333728 -0.575562 -0.746561
+vn 0.916678 -0.176432 -0.358571
+vn 0.910980 -0.276630 -0.305928
+vn 0.350609 -0.424894 -0.834589
+vn 0.457368 -0.496250 -0.737936
+vn 0.819595 -0.559998 0.121101
+vn 0.772999 -0.620157 -0.133706
+vn 0.979394 -0.197399 0.042666
+vn 0.910980 -0.276630 -0.305928
+vn 0.552792 -0.829295 -0.081791
+vn 0.395898 -0.918084 -0.019654
+vn 0.291820 -0.619012 -0.729154
+vn 0.221873 -0.646208 -0.730197
+vn 0.395898 -0.918084 -0.019654
+vn 0.200818 -0.979187 0.029421
+vn 0.221873 -0.646208 -0.730197
+vn 0.120368 -0.692302 -0.711498
+vn 0.200818 -0.979187 0.029421
+vn 0.000000 -0.999153 0.041139
+vn 0.120368 -0.692302 -0.711498
+vn 0.000000 -0.707900 -0.706313
+vn 0.291820 -0.619012 -0.729154
+vn 0.313953 -0.551668 -0.772720
+vn 0.552792 -0.829295 -0.081791
+vn 0.635399 -0.761197 -0.129796
+vn 0.791884 -0.577089 0.199718
+vn 0.819595 -0.559998 0.121101
+vn 0.973672 -0.216165 0.072360
+vn 0.979394 -0.197399 0.042666
+vn -0.941739 -0.326680 -0.080053
+vn -1.000000 0.000000 0.000000
+vn -0.945242 -0.323626 -0.042239
+vn -1.000000 0.000000 0.000000
+vn -0.933306 -0.319627 -0.163644
+vn -1.000000 0.000000 0.000000
+vn -0.941739 -0.326680 -0.080053
+vn -1.000000 0.000000 0.000000
+vn -0.919246 -0.303577 -0.250656
+vn -1.000000 0.000000 0.000000
+vn -0.933306 -0.319627 -0.163644
+vn -1.000000 0.000000 0.000000
+vn -0.911448 -0.243205 -0.331832
+vn -1.000000 0.000000 0.000000
+vn -0.919246 -0.303577 -0.250656
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.916678 -0.176432 -0.358571
+vn -0.911448 -0.243205 -0.331832
+vn -0.979394 -0.197399 0.042666
+vn -1.000000 0.000000 0.000000
+vn -0.910980 -0.276630 -0.305928
+vn -0.916678 -0.176432 -0.358571
+vn -0.973672 -0.216165 0.072360
+vn -0.978343 -0.189186 0.083988
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.934880 0.354963
+vn 0.000000 -0.869145 0.494557
+vn -0.178204 -0.920500 0.347740
+vn -0.161752 -0.860157 0.483701
+vn -0.178204 -0.920500 0.347740
+vn -0.161752 -0.860157 0.483701
+vn -0.352896 -0.876974 0.326161
+vn -0.325059 -0.831219 0.451012
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.154215 -0.988037
+vn 0.000092 -0.079746 -0.996815
+vn 0.000000 -0.079380 -0.996844
+vn -0.520259 -0.802134 0.293106
+vn -0.491395 -0.774189 0.398951
+vn -0.629820 -0.732455 0.258526
+vn -0.596627 -0.720598 0.353234
+vn -0.352896 -0.876974 0.326161
+vn -0.325059 -0.831219 0.451012
+vn -0.520259 -0.802134 0.293106
+vn -0.491395 -0.774189 0.398951
+vn -0.200818 -0.979187 0.029421
+vn -0.195076 -0.958627 0.207314
+vn -0.395898 -0.918084 -0.019654
+vn -0.382681 -0.903004 0.195293
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.291820 -0.619012 -0.729154
+vn 0.000000 -0.154215 -0.988037
+vn -0.221873 -0.646208 -0.730197
+vn 0.000244 -0.081790 -0.996650
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn 0.000000 -0.750417 -0.660964
+vn -0.341206 -0.695262 -0.632605
+vn 0.000000 -0.877817 -0.478995
+vn -0.383839 -0.804668 -0.452965
+vn -0.395898 -0.918084 -0.019654
+vn -0.382681 -0.903004 0.195293
+vn -0.552792 -0.829295 -0.081791
+vn -0.555564 -0.812685 0.175758
+vn -0.772999 -0.620157 -0.133706
+vn -0.635399 -0.761197 -0.129796
+vn -0.819595 -0.559998 0.121101
+vn -0.661102 -0.733340 0.158607
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.991581 -0.129491
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.967377 -0.253342
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.877817 -0.478995
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.750417 -0.660964
+vn 0.000000 -0.449678 -0.893191
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.604128 -0.796887
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.449678 -0.893191
+vn 0.000000 -0.449666 -0.893197
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.154215 -0.988037
+vn 0.000244 -0.081790 -0.996650
+vn 0.000092 -0.079746 -0.996815
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.297194 -0.954817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.154215 -0.988037
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.999153 0.041139
+vn 0.000000 -0.977405 0.211375
+vn -0.200818 -0.979187 0.029421
+vn -0.195076 -0.958627 0.207314
+vn -0.221873 -0.646208 -0.730197
+vn 0.000244 -0.081790 -0.996650
+vn -0.120368 -0.692302 -0.711498
+vn 0.000092 -0.079746 -0.996815
+vn 0.000000 -0.604128 -0.796887
+vn -0.333728 -0.575562 -0.746561
+vn 0.000000 -0.750417 -0.660964
+vn -0.341206 -0.695262 -0.632605
+vn 0.000000 -0.967377 -0.253342
+vn -0.412992 -0.879180 -0.237657
+vn 0.000000 -0.991581 -0.129491
+vn -0.422817 -0.898585 -0.117347
+vn 0.000000 -0.877817 -0.478995
+vn -0.383839 -0.804668 -0.452965
+vn 0.000000 -0.967377 -0.253342
+vn -0.412992 -0.879180 -0.237657
+vn -0.457368 -0.496250 -0.737936
+vn 0.000000 -0.297194 -0.954817
+vn -0.313953 -0.551668 -0.772720
+vn 0.000000 -0.223005 -0.974817
+vn 0.000000 -0.297194 -0.954817
+vn -0.457368 -0.496250 -0.737936
+vn 0.000000 -0.449666 -0.893197
+vn -0.350609 -0.424894 -0.834589
+vn -0.635399 -0.761197 -0.129796
+vn -0.552792 -0.829295 -0.081791
+vn -0.661102 -0.733340 0.158607
+vn -0.555564 -0.812685 0.175758
+vn 0.000000 -0.154215 -0.988037
+vn -0.291820 -0.619012 -0.729154
+vn 0.000000 -0.223005 -0.974817
+vn -0.313953 -0.551668 -0.772720
+vn -0.120368 -0.692302 -0.711498
+vn 0.000092 -0.079746 -0.996815
+vn 0.000000 -0.707900 -0.706313
+vn 0.000000 -0.079380 -0.996844
+vn -0.629820 -0.732455 0.258526
+vn -0.596627 -0.720598 0.353234
+vn -0.791884 -0.577089 0.199718
+vn -0.788994 -0.557075 0.259142
+vn 0.000000 -0.449666 -0.893197
+vn -0.350609 -0.424894 -0.834589
+vn 0.000000 -0.604128 -0.796887
+vn -0.333728 -0.575562 -0.746561
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.457368 -0.496250 -0.737936
+vn -0.772999 -0.620157 -0.133706
+vn -0.910980 -0.276630 -0.305928
+vn -0.313953 -0.551668 -0.772720
+vn -0.635399 -0.761197 -0.129796
+vn -0.457368 -0.496250 -0.737936
+vn -0.772999 -0.620157 -0.133706
+vn -0.945242 -0.323626 -0.042239
+vn -0.422817 -0.898585 -0.117347
+vn -0.941739 -0.326680 -0.080053
+vn -0.412992 -0.879180 -0.237657
+vn -0.941739 -0.326680 -0.080053
+vn -0.412992 -0.879180 -0.237657
+vn -0.933306 -0.319627 -0.163644
+vn -0.383839 -0.804668 -0.452965
+vn -0.933306 -0.319627 -0.163644
+vn -0.383839 -0.804668 -0.452965
+vn -0.919246 -0.303577 -0.250656
+vn -0.341206 -0.695262 -0.632605
+vn -0.919246 -0.303577 -0.250656
+vn -0.341206 -0.695262 -0.632605
+vn -0.911448 -0.243205 -0.331832
+vn -0.333728 -0.575562 -0.746561
+vn -0.333728 -0.575562 -0.746561
+vn -0.350609 -0.424894 -0.834589
+vn -0.911448 -0.243205 -0.331832
+vn -0.916678 -0.176432 -0.358571
+vn -0.916678 -0.176432 -0.358571
+vn -0.350609 -0.424894 -0.834589
+vn -0.910980 -0.276630 -0.305928
+vn -0.457368 -0.496250 -0.737936
+vn -0.819595 -0.559998 0.121101
+vn -0.979394 -0.197399 0.042666
+vn -0.772999 -0.620157 -0.133706
+vn -0.910980 -0.276630 -0.305928
+vn -0.552792 -0.829295 -0.081791
+vn -0.291820 -0.619012 -0.729154
+vn -0.395898 -0.918084 -0.019654
+vn -0.221873 -0.646208 -0.730197
+vn -0.395898 -0.918084 -0.019654
+vn -0.221873 -0.646208 -0.730197
+vn -0.200818 -0.979187 0.029421
+vn -0.120368 -0.692302 -0.711498
+vn -0.200818 -0.979187 0.029421
+vn -0.120368 -0.692302 -0.711498
+vn 0.000000 -0.999153 0.041139
+vn 0.000000 -0.707900 -0.706313
+vn -0.291820 -0.619012 -0.729154
+vn -0.552792 -0.829295 -0.081791
+vn -0.313953 -0.551668 -0.772720
+vn -0.635399 -0.761197 -0.129796
+vn -0.791884 -0.577089 0.199718
+vn -0.973672 -0.216165 0.072360
+vn -0.819595 -0.559998 0.121101
+vn -0.979394 -0.197399 0.042666
+vn -0.978343 -0.189186 0.083988
+vn -0.973672 -0.216165 0.072360
+vn -0.788994 -0.557075 0.259142
+vn -0.791884 -0.577089 0.199718
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.819595 -0.559998 0.121101
+vn -0.661102 -0.733340 0.158607
+vn -0.791884 -0.577089 0.199718
+vn -0.629820 -0.732455 0.258526
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -0.382681 -0.903004 0.195293
+vn -0.352896 -0.876974 0.326161
+vn -0.555564 -0.812685 0.175758
+vn -0.520259 -0.802134 0.293106
+vn -0.661102 -0.733340 0.158607
+vn -0.555564 -0.812685 0.175758
+vn -0.629820 -0.732455 0.258526
+vn -0.520259 -0.802134 0.293106
+vn -0.195076 -0.958627 0.207314
+vn -0.178204 -0.920500 0.347740
+vn -0.382681 -0.903004 0.195293
+vn -0.352896 -0.876974 0.326161
+vn 0.000000 -0.977405 0.211375
+vn 0.000000 -0.934880 0.354963
+vn -0.195076 -0.958627 0.207314
+vn -0.178204 -0.920500 0.347740
+vn -0.973672 -0.216165 0.072360
+vn -1.000000 0.000000 0.000000
+vn -0.979394 -0.197399 0.042666
+vn -1.000000 0.000000 0.000000
+vn 0.791884 -0.577089 0.199718
+vn 0.973672 -0.216165 0.072360
+vn 0.788994 -0.557075 0.259142
+vn 0.978343 -0.189186 0.083988
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.819595 -0.559998 0.121101
+vn 0.791884 -0.577089 0.199718
+vn 0.661102 -0.733340 0.158607
+vn 0.629820 -0.732455 0.258526
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.382681 -0.903004 0.195293
+vn 0.555564 -0.812685 0.175758
+vn 0.352896 -0.876974 0.326161
+vn 0.520259 -0.802134 0.293106
+vn 0.661102 -0.733340 0.158607
+vn 0.629820 -0.732455 0.258526
+vn 0.555564 -0.812685 0.175758
+vn 0.520259 -0.802134 0.293106
+vn 0.352896 -0.876974 0.326161
+vn 0.178204 -0.920500 0.347740
+vn 0.382681 -0.903004 0.195293
+vn 0.195076 -0.958627 0.207314
+vn 0.178204 -0.920500 0.347740
+vn 0.000000 -0.934880 0.354963
+vn 0.195076 -0.958627 0.207314
+vn 0.000000 -0.977405 0.211375
+vn 1.000000 0.000000 0.000000
+vn 1.000000 0.000000 0.000000
+vn 0.979394 -0.197399 0.042666
+vn 0.973672 -0.216165 0.072360
+f 45/156/1 43/157/2 33/159/3
+f 33/159/3 43/157/2 32/158/4
+f 47/160/5 45/156/6 34/155/7
+f 34/155/7 45/156/6 33/159/8
+f 49/161/9 47/160/10 35/62/11
+f 35/62/11 47/160/10 34/155/12
+f 51/63/13 49/161/14 41/64/15
+f 41/64/15 49/161/14 35/62/16
+f 51/63/17 41/64/18 53/37/19
+f 53/37/19 41/64/18 40/38/20
+f 53/37/21 40/38/22 56/152/23
+f 56/152/23 40/38/22 68/153/24
+f 162/154/25 156/65/26 59/67/27
+f 59/67/27 156/65/26 7/66/28
+f 25/68/29 24/69/30 15/71/31
+f 15/71/31 24/69/30 14/70/32
+f 5/123/33 74/40/34 154/39/35
+f 154/39/35 74/40/34 153/1/36
+f 4/124/37 5/123/38 155/2/39
+f 155/2/39 5/123/38 154/39/40
+f 26/81/41 24/69/42 79/116/43
+f 79/116/43 24/69/42 77/82/44
+f 25/68/45 31/117/46 78/36/47
+f 78/36/47 31/117/46 81/77/48
+f 3/78/49 2/79/50 159/170/51
+f 159/170/51 2/79/50 160/118/52
+f 155/2/53 160/118/54 4/124/55
+f 4/124/55 160/118/54 2/79/56
+f 38/119/57 37/76/58 66/80/59
+f 66/80/59 37/76/58 64/19/60
+f 33/159/61 32/158/62 8/21/63
+f 8/21/63 32/158/62 9/20/64
+f 157/42/65 6/34/66 156/65/67
+f 156/65/67 6/34/66 7/66/68
+f 60/139/69 67/140/70 15/71/71
+f 15/71/71 67/140/70 21/141/72
+f 35/62/73 34/155/74 11/143/75
+f 11/143/75 34/155/74 10/142/76
+f 34/155/77 33/159/78 10/142/79
+f 10/142/79 33/159/78 8/21/80
+f 19/144/81 17/145/82 50/99/83
+f 50/99/83 17/145/82 48/146/84
+f 66/80/85 61/120/86 38/119/87
+f 38/119/87 61/120/86 39/147/88
+f 57/22/89 69/25/90 62/102/91
+f 62/102/91 69/25/90 36/23/92
+f 26/81/93 23/30/94 16/93/95
+f 16/93/95 23/30/94 13/148/96
+f 23/30/97 28/26/98 13/148/99
+f 13/148/99 28/26/98 18/18/100
+f 28/26/101 29/31/102 18/18/103
+f 18/18/103 29/31/102 19/144/104
+f 29/31/105 27/101/106 19/144/107
+f 19/144/107 27/101/106 17/145/108
+f 27/101/109 30/121/110 17/145/111
+f 17/145/111 30/121/110 20/96/112
+f 30/121/113 22/3/114 20/96/115
+f 20/96/115 22/3/114 12/24/116
+f 70/4/117 73/125/118 30/121/119
+f 30/121/119 73/125/118 22/3/120
+f 80/94/121 70/4/122 27/101/123
+f 27/101/123 70/4/122 30/121/124
+f 75/27/125 80/94/126 29/31/127
+f 29/31/127 80/94/126 27/101/128
+f 72/95/129 75/27/130 28/26/131
+f 28/26/131 75/27/130 29/31/132
+f 71/100/133 72/95/134 23/30/135
+f 23/30/135 72/95/134 28/26/136
+f 79/116/137 71/100/138 26/81/139
+f 26/81/139 71/100/138 23/30/140
+f 15/71/141 21/141/142 25/68/143
+f 25/68/143 21/141/142 31/117/144
+f 16/93/145 14/70/146 26/81/147
+f 26/81/147 14/70/146 24/69/148
+f 78/36/149 77/82/150 25/68/151
+f 25/68/151 77/82/150 24/69/152
+f 37/76/153 117/122/154 64/19/155
+f 64/19/155 117/122/154 148/83/156
+f 67/140/157 65/41/158 21/141/159
+f 21/141/159 65/41/158 31/117/160
+f 18/18/161 19/144/162 52/28/163
+f 52/28/163 19/144/162 50/99/164
+f 20/96/165 12/24/166 46/29/167
+f 46/29/167 12/24/166 44/97/168
+f 17/145/169 20/96/170 48/146/171
+f 48/146/171 20/96/170 46/29/172
+f 55/171/173 63/98/174 16/93/175
+f 16/93/175 63/98/174 14/70/176
+f 16/93/177 13/148/178 55/171/179
+f 55/171/179 13/148/178 54/84/180
+f 62/102/181 36/23/182 61/120/183
+f 61/120/183 36/23/182 39/147/184
+f 15/71/185 14/70/186 60/139/187
+f 60/139/187 14/70/186 63/98/188
+f 65/41/189 147/111/190 31/117/191
+f 31/117/191 147/111/190 81/77/192
+f 58/85/193 3/78/194 161/43/195
+f 161/43/195 3/78/194 159/170/196
+f 13/148/197 18/18/198 54/84/199
+f 54/84/199 18/18/198 52/28/200
+f 11/143/201 42/16/202 35/62/203
+f 35/62/203 42/16/202 41/64/204
+f 41/64/205 42/16/206 157/42/207
+f 157/42/207 42/16/206 158/5/208
+f 55/171/209 56/152/210 57/22/211
+f 63/98/212 55/171/213 62/102/214
+f 62/102/214 55/171/213 57/22/215
+f 43/157/216 45/156/217 44/97/218
+f 44/97/218 45/156/217 46/29/219
+f 45/156/220 47/160/221 46/29/222
+f 46/29/222 47/160/221 48/146/223
+f 47/160/224 49/161/225 48/146/226
+f 48/146/226 49/161/225 50/99/227
+f 49/161/228 51/63/229 50/99/230
+f 50/99/230 51/63/229 52/28/231
+f 53/37/232 54/84/233 51/63/234
+f 51/63/234 54/84/233 52/28/235
+f 53/37/236 56/152/237 54/84/238
+f 54/84/238 56/152/237 55/171/239
+f 69/25/240 57/22/241 68/153/242
+f 68/153/242 57/22/241 56/152/243
+f 61/120/244 66/80/245 60/139/246
+f 60/139/246 66/80/245 67/140/247
+f 66/80/248 64/19/249 67/140/250
+f 67/140/250 64/19/249 65/41/251
+f 64/19/252 148/83/253 65/41/254
+f 65/41/254 148/83/253 147/111/255
+f 60/139/256 63/98/257 61/120/258
+f 61/120/258 63/98/257 62/102/259
+f 161/43/260 69/25/261 162/154/262
+f 162/154/262 69/25/261 68/153/263
+f 126/6/264 113/44/265 124/72/266
+f 124/72/266 113/44/265 112/103/267
+f 128/7/268 114/131/269 126/6/270
+f 126/6/270 114/131/269 113/44/271
+f 130/54/272 115/86/273 128/7/274
+f 128/7/274 115/86/273 114/131/275
+f 132/162/276 122/108/277 130/54/278
+f 130/54/278 122/108/277 115/86/279
+f 121/73/280 122/108/281 134/59/282
+f 134/59/282 122/108/281 132/162/283
+f 151/45/284 121/73/285 137/8/286
+f 137/8/286 121/73/285 134/59/287
+f 171/10/288 140/52/289 165/115/290
+f 165/115/290 140/52/289 87/165/291
+f 105/134/292 95/9/293 104/126/294
+f 104/126/294 95/9/293 94/129/295
+f 153/1/296 74/40/297 163/14/298
+f 163/14/298 74/40/297 85/51/299
+f 163/14/300 85/51/301 164/12/302
+f 164/12/302 85/51/301 84/15/303
+f 106/58/304 79/116/305 104/126/306
+f 104/126/306 79/116/305 77/82/307
+f 105/134/308 78/36/309 111/112/310
+f 111/112/310 78/36/309 81/77/311
+f 169/17/312 82/104/313 168/32/314
+f 168/32/314 82/104/313 83/133/315
+f 164/12/316 84/15/317 169/17/318
+f 169/17/318 84/15/317 82/104/319
+f 145/47/320 118/130/321 149/46/322
+f 149/46/322 118/130/321 119/53/323
+f 113/44/324 88/163/325 112/103/326
+f 112/103/326 88/163/325 89/87/327
+f 87/165/328 86/149/329 165/115/330
+f 165/115/330 86/149/329 166/35/331
+f 141/13/332 95/9/333 150/114/334
+f 150/114/334 95/9/333 101/48/335
+f 115/86/336 91/127/337 114/131/338
+f 114/131/338 91/127/337 90/105/339
+f 114/131/340 90/105/341 113/44/342
+f 113/44/342 90/105/341 88/163/343
+f 99/138/344 131/55/345 97/60/346
+f 97/60/346 131/55/345 129/56/347
+f 149/46/348 119/53/349 142/128/350
+f 142/128/350 119/53/349 120/74/351
+f 138/169/352 143/113/353 152/167/354
+f 152/167/354 143/113/353 116/33/355
+f 106/58/356 96/168/357 103/89/358
+f 103/89/358 96/168/357 93/164/359
+f 103/89/360 93/164/361 108/11/362
+f 108/11/362 93/164/361 98/75/363
+f 108/11/364 98/75/365 109/88/366
+f 109/88/366 98/75/365 99/138/367
+f 109/88/368 99/138/369 107/166/370
+f 107/166/370 99/138/369 97/60/371
+f 107/166/372 97/60/373 110/135/374
+f 110/135/374 97/60/373 100/109/375
+f 110/135/376 100/109/377 102/49/378
+f 102/49/378 100/109/377 92/91/379
+f 70/4/380 110/135/381 73/125/382
+f 73/125/382 110/135/381 102/49/383
+f 80/94/384 107/166/385 70/4/386
+f 70/4/386 107/166/385 110/135/387
+f 75/27/388 109/88/389 80/94/390
+f 80/94/390 109/88/389 107/166/391
+f 72/95/392 108/11/393 75/27/394
+f 75/27/394 108/11/393 109/88/395
+f 71/100/396 103/89/397 72/95/398
+f 72/95/398 103/89/397 108/11/399
+f 79/116/400 106/58/401 71/100/402
+f 71/100/402 106/58/401 103/89/403
+f 95/9/404 105/134/405 101/48/406
+f 101/48/406 105/134/405 111/112/407
+f 96/168/408 106/58/409 94/129/410
+f 94/129/410 106/58/409 104/126/411
+f 78/36/412 105/134/413 77/82/414
+f 77/82/414 105/134/413 104/126/415
+f 148/83/416 117/122/417 145/47/418
+f 145/47/418 117/122/417 118/130/419
+f 150/114/420 101/48/421 146/110/422
+f 146/110/422 101/48/421 111/112/423
+f 98/75/424 133/90/425 99/138/426
+f 99/138/426 133/90/425 131/55/427
+f 100/109/428 127/50/429 92/91/430
+f 92/91/430 127/50/429 125/137/431
+f 97/60/432 129/56/433 100/109/434
+f 100/109/434 129/56/433 127/50/435
+f 136/106/436 96/168/437 144/92/438
+f 144/92/438 96/168/437 94/129/439
+f 96/168/440 136/106/441 93/164/442
+f 93/164/442 136/106/441 135/150/443
+f 143/113/444 142/128/445 116/33/446
+f 116/33/446 142/128/445 120/74/447
+f 95/9/448 141/13/449 94/129/450
+f 94/129/450 141/13/449 144/92/451
+f 146/110/452 111/112/453 147/111/454
+f 147/111/454 111/112/453 81/77/455
+f 168/32/456 83/133/457 170/107/458
+f 170/107/458 83/133/457 139/136/459
+f 93/164/460 135/150/461 98/75/462
+f 98/75/462 135/150/461 133/90/463
+f 91/127/464 115/86/465 123/61/466
+f 123/61/466 115/86/465 122/108/467
+f 167/57/468 123/61/469 166/35/470
+f 166/35/470 123/61/469 122/108/471
+f 136/106/472 138/169/473 137/8/474
+f 144/92/475 143/113/476 136/106/477
+f 136/106/477 143/113/476 138/169/478
+f 124/72/479 125/137/480 126/6/481
+f 126/6/481 125/137/480 127/50/482
+f 126/6/483 127/50/484 128/7/485
+f 128/7/485 127/50/484 129/56/486
+f 128/7/487 129/56/488 130/54/489
+f 130/54/489 129/56/488 131/55/490
+f 130/54/491 131/55/492 132/162/493
+f 132/162/493 131/55/492 133/90/494
+f 133/90/495 135/150/496 132/162/497
+f 132/162/497 135/150/496 134/59/498
+f 134/59/499 135/150/500 137/8/501
+f 137/8/501 135/150/500 136/106/502
+f 152/167/503 151/45/504 138/169/505
+f 138/169/505 151/45/504 137/8/506
+f 142/128/507 141/13/508 149/46/509
+f 149/46/509 141/13/508 150/114/510
+f 149/46/511 150/114/512 145/47/513
+f 145/47/513 150/114/512 146/110/514
+f 145/47/515 146/110/516 148/83/517
+f 148/83/517 146/110/516 147/111/518
+f 141/13/519 142/128/520 144/92/521
+f 144/92/521 142/128/520 143/113/522
+f 170/107/523 171/10/524 152/167/525
+f 152/167/525 171/10/524 151/45/526
+f 140/52/527 171/10/528 139/136/529
+f 139/136/529 171/10/528 170/107/530
+f 76/132/531 167/57/532 86/149/533
+f 86/149/533 167/57/532 166/35/534
+f 152/167/535 116/33/536 170/107/537
+f 170/107/537 116/33/536 168/32/538
+f 165/115/539 166/35/540 121/73/541
+f 121/73/541 166/35/540 122/108/542
+f 119/53/543 164/12/544 120/74/545
+f 120/74/545 164/12/544 169/17/546
+f 116/33/547 120/74/548 168/32/549
+f 168/32/549 120/74/548 169/17/550
+f 118/130/551 163/14/552 119/53/553
+f 119/53/553 163/14/552 164/12/554
+f 117/122/555 153/1/556 118/130/557
+f 118/130/557 153/1/556 163/14/558
+f 171/10/559 165/115/560 151/45/561
+f 151/45/561 165/115/560 121/73/562
+f 161/43/563 162/154/564 58/85/565
+f 58/85/565 162/154/564 59/67/566
+f 157/42/567 158/5/568 6/34/569
+f 6/34/569 158/5/568 1/151/570
+f 69/25/571 161/43/572 36/23/573
+f 36/23/573 161/43/572 159/170/574
+f 41/64/575 157/42/576 40/38/577
+f 40/38/577 157/42/576 156/65/578
+f 38/119/579 39/147/580 155/2/581
+f 155/2/581 39/147/580 160/118/582
+f 36/23/583 159/170/584 39/147/585
+f 39/147/585 159/170/584 160/118/586
+f 155/2/587 154/39/588 38/119/589
+f 38/119/589 154/39/588 37/76/590
+f 154/39/591 153/1/592 37/76/593
+f 37/76/593 153/1/592 117/122/594
+f 40/38/595 156/65/596 68/153/597
+f 68/153/597 156/65/596 162/154/598
diff --git a/libs/vr/libdvrgraphics/assets/laser.obj b/libs/vr/libdvrgraphics/assets/laser.obj
new file mode 100644
index 0000000..32737e4
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/laser.obj
@@ -0,0 +1,28 @@
+# This file uses centimeters as units for non-parametric coordinates.
+
+v -0.010000 -0.000000 -1.000000
+v 0.010000 -0.000000 -1.000000
+v -0.010000 -0.000000 0.000000
+v 0.010000 -0.000000 0.000000
+v 0.000000 0.010000 -1.000000
+v 0.000000 -0.010000 -1.000000
+v 0.000000 0.010000 -0.000000
+v 0.000000 -0.010000 0.000000
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 0.000000 1.000000
+vt 1.000000 1.000000
+vt 0.000000 0.000000
+vt 1.000000 0.000000
+vt 1.000000 1.000000
+vt 0.000000 1.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.000000 -1.000000 -0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+vn -1.000000 0.000000 0.000000
+f 1/1/1 2/2/2 4/4/3 3/3/4
+f 5/5/5 6/6/6 8/7/7 7/8/8
diff --git a/libs/vr/libdvrgraphics/assets/laser.png b/libs/vr/libdvrgraphics/assets/laser.png
new file mode 100644
index 0000000..a96c68d
--- /dev/null
+++ b/libs/vr/libdvrgraphics/assets/laser.png
Binary files differ
diff --git a/libs/vr/libdvrgraphics/blur.cpp b/libs/vr/libdvrgraphics/blur.cpp
new file mode 100644
index 0000000..90e271e
--- /dev/null
+++ b/libs/vr/libdvrgraphics/blur.cpp
@@ -0,0 +1,246 @@
+#include "include/private/dvr/graphics/blur.h"
+
+// clang-format off
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+// clang-format on
+#include <hardware/gralloc.h>
+
+#include <string>
+
+#include <log/log.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/egl_image.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+#define POSITION_ATTR 0
+#define OFFSET_BINDING 0
+#define SAMPLER_BINDING 1
+
+namespace {
+
+std::string screen_space_vert_shader = SHADER0([]() {  // NOLINT
+  layout(location = 0) in vec4 position_uv;
+  out vec2 texCoords;
+
+  void main() {
+    gl_Position = vec4(position_uv.xy, 0.0, 1.0);
+    texCoords = position_uv.zw;
+  }
+});
+
+std::string kawase_blur_frag_shader = SHADER0([]() {  // NOLINT
+  precision mediump float;
+  layout(location = 0) uniform vec2 uSampleOffsets[4];
+  layout(binding = 1) uniform APP_SAMPLER_2D uTexture;
+  in vec2 texCoords;
+  out vec4 color;
+
+  void main() {
+    vec2 tc = texCoords;
+    color = texture(uTexture, tc + uSampleOffsets[0]);
+    color += texture(uTexture, tc + uSampleOffsets[1]);
+    color += texture(uTexture, tc + uSampleOffsets[2]);
+    color += texture(uTexture, tc + uSampleOffsets[3]);
+    color *= (1.0 / 4.0);
+  }
+});
+
+constexpr int g_num_samples = 4;
+
+// Modified kernel patterns originally based on:
+// https://software.intel.com/en-us/blogs/2014/07/15/an-investigation-of-fast-real-time-gpu-based-image-blur-algorithms
+// The modification is left and right rotations of the 3rd and 4th patterns.
+const android::dvr::vec2 g_blur_samples[][g_num_samples] = {
+    {{0.5f, 0.5f}, {-0.5f, 0.5f}, {0.5f, -0.5f}, {-0.5f, -0.5f}},
+    {{1.5f, 1.5f}, {-1.5f, 1.5f}, {1.5f, -1.5f}, {-1.5f, -1.5f}},
+    {{2.5f, 1.5f}, {-1.5f, 2.5f}, {1.5f, -2.5f}, {-2.5f, -1.5f}},
+    {{2.5f, 3.5f}, {-3.5f, 2.5f}, {3.5f, -2.5f}, {-2.5f, -3.5f}},
+    // Last pass disabled, because it is more blur than we need.
+    // {{3.5f, 3.5f}, {-3.5f, 3.5f}, {3.5f, -3.5f}, {-3.5f, -3.5f}},
+};
+
+}  // namespace
+
+namespace android {
+namespace dvr {
+
+Blur::Blur(int w, int h, GLuint source_texture, GLint source_texture_target,
+           GLint target_texture_target, bool is_external, EGLDisplay display,
+           int num_blur_outputs)
+    : display_(display),
+      target_texture_target_(target_texture_target),
+      width_(w),
+      height_(h),
+      fbo_q_free_(1 + num_blur_outputs) {
+  LOG_ALWAYS_FATAL_IF(num_blur_outputs <= 0);
+  source_fbo_ =
+      CreateFbo(w, h, source_texture, source_texture_target, is_external);
+  fbo_half_ = CreateFbo(w / 2, h / 2, 0, target_texture_target, is_external);
+  // Create the quarter res fbos.
+  for (size_t i = 0; i < fbo_q_free_.GetCapacity(); ++i)
+    fbo_q_.push_back(
+        CreateFbo(w / 4, h / 4, 0, target_texture_target, is_external));
+  scale_ = 1.0f;
+}
+
+Blur::~Blur() {
+  glFinish();
+  glDeleteFramebuffers(1, &source_fbo_.fbo);
+  glDeleteFramebuffers(1, &fbo_half_.fbo);
+  // Note: source_fbo_.texture is not deleted because it was created externally.
+  glDeleteTextures(1, &fbo_half_.texture);
+  if (fbo_half_.egl_image)
+    eglDestroyImageKHR(display_, fbo_half_.egl_image);
+  for (const auto& fbo : fbo_q_) {
+    glDeleteFramebuffers(1, &fbo.fbo);
+    glDeleteTextures(1, &fbo.texture);
+    if (fbo.egl_image)
+      eglDestroyImageKHR(display_, fbo.egl_image);
+  }
+  CHECK_GL();
+}
+
+void Blur::StartFrame() {
+  fbo_q_free_.Clear();
+  for (const auto& fbo : fbo_q_)
+    fbo_q_free_.Append(fbo);
+}
+
+GLuint Blur::DrawBlur(GLuint source_texture) {
+  LOG_ALWAYS_FATAL_IF(fbo_q_free_.GetSize() < 2);
+
+  // Downsample to half w x half h.
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, source_fbo_.fbo);
+  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_half_.fbo);
+  glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                         target_texture_target_, source_texture, 0);
+  glBlitFramebuffer(0, 0, width_, height_, 0, 0, width_ / 2, height_ / 2,
+                    GL_COLOR_BUFFER_BIT, GL_LINEAR);
+  CHECK_GL();
+
+  // Downsample to quarter w x quarter h.
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_half_.fbo);
+  Fbo fbo_out = fbo_q_free_.Front();
+  fbo_q_free_.PopFront();
+  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_out.fbo);
+  glBlitFramebuffer(0, 0, width_ / 2, height_ / 2, 0, 0, width_ / 4,
+                    height_ / 4, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+  CHECK_GL();
+
+  // Blur shader is initialized statically to share between multiple blur
+  // instances.
+  static ShaderProgram kawase_prog[2];
+  int prog_index = (target_texture_target_ == GL_TEXTURE_EXTERNAL_OES) ? 1 : 0;
+  if (!kawase_prog[prog_index].IsUsable()) {
+    std::string prefix = "#version 310 es\n";
+    if (target_texture_target_ == GL_TEXTURE_EXTERNAL_OES) {
+      prefix += "#extension GL_OES_EGL_image_external_essl3 : require\n";
+      prefix += "#define APP_SAMPLER_2D samplerExternalOES\n";
+    } else {
+      prefix += "#define APP_SAMPLER_2D sampler2D\n";
+    }
+    std::string vert = prefix + screen_space_vert_shader;
+    std::string frag = prefix + kawase_blur_frag_shader;
+    kawase_prog[prog_index].Link(vert, frag);
+    CHECK_GL();
+  }
+
+  int blur_w = width_ / 4;
+  int blur_h = height_ / 4;
+  float pix_w = 1.0f / static_cast<float>(blur_w);
+  float pix_h = 1.0f / static_cast<float>(blur_h);
+  vec2 pixel_size(pix_w, pix_h);
+  constexpr int num_passes = sizeof(g_blur_samples) / sizeof(g_blur_samples[0]);
+  vec2 blur_offsets[num_passes][g_num_samples];
+  for (int i = 0; i < num_passes; ++i) {
+    for (int dir = 0; dir < g_num_samples; ++dir) {
+      blur_offsets[i][dir] = pixel_size.array() *
+          g_blur_samples[i][dir].array() * scale_;
+    }
+  }
+
+  kawase_prog[prog_index].Use();
+
+  vec4 screen_tri_strip[4] = {vec4(-1, 1, 0, 1), vec4(-1, -1, 0, 0),
+                              vec4(1, 1, 1, 1), vec4(1, -1, 1, 0)};
+
+  glViewport(0, 0, blur_w, blur_h);
+  glVertexAttribPointer(POSITION_ATTR, 4, GL_FLOAT, GL_FALSE, sizeof(vec4),
+                        screen_tri_strip);
+  glEnableVertexAttribArray(POSITION_ATTR);
+  CHECK_GL();
+
+  // Ping-pong between fbos from fbo_q_free_ to compute the passes.
+  Fbo fbo_in = fbo_out;
+  for (int i = 0; i < num_passes; ++i) {
+    fbo_out = fbo_q_free_.Front();
+    fbo_q_free_.PopFront();
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_out.fbo);
+    glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING);
+    glBindTexture(target_texture_target_, fbo_in.texture);
+    glUniform2fv(OFFSET_BINDING, 4, &blur_offsets[i][0][0]);
+    glClear(GL_COLOR_BUFFER_BIT);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    CHECK_GL();
+    // Put fbo_in back into the free fbo pool.
+    fbo_q_free_.Append(fbo_in);
+    // Next iteration's in buffer is this iteration's out buffer.
+    fbo_in = fbo_out;
+  }
+  glDisableVertexAttribArray(POSITION_ATTR);
+  glBindTexture(target_texture_target_, 0);
+  glUseProgram(0);
+  glActiveTexture(GL_TEXTURE0);
+  CHECK_GL();
+  // fbo_out remains out of the fbo_q_free_ list, since the application will be
+  // using it as a texture.
+  return fbo_out.texture;
+}
+
+Blur::Fbo Blur::CreateFbo(int w, int h, GLuint source_texture, GLint tex_target,
+                          bool is_external) {
+  Fbo fbo;
+  glGenFramebuffers(1, &fbo.fbo);
+  if (source_texture) {
+    fbo.texture = source_texture;
+  } else {
+    glGenTextures(1, &fbo.texture);
+  }
+
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
+  CHECK_GL();
+
+  if (!source_texture) {
+    glBindTexture(tex_target, fbo.texture);
+    glTexParameteri(tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    if (is_external) {
+      fbo.egl_image =
+          CreateEglImage(display_, w, h, HAL_PIXEL_FORMAT_RGBA_8888,
+                         GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER);
+      glEGLImageTargetTexture2DOES(tex_target, fbo.egl_image);
+    } else {
+      glTexImage2D(tex_target, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                   nullptr);
+    }
+  }
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_target,
+                         fbo.texture, 0);
+  CHECK_GL();
+  CHECK_GL_FBO();
+
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  return fbo;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/debug_text.cpp b/libs/vr/libdvrgraphics/debug_text.cpp
new file mode 100644
index 0000000..1875b14
--- /dev/null
+++ b/libs/vr/libdvrgraphics/debug_text.cpp
@@ -0,0 +1,186 @@
+#include "include/private/dvr/graphics/debug_text.h"
+
+#include <algorithm>
+
+#include <private/dvr/debug.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+// 658x11 alpha texture with ascii characters starting with !: "!"#$%&'("...
+// Each character is 7x11 pixels, monospace.
+// clang-format off
+const uint8_t ascii_texture[] = {
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0x92,0x49,0x92,0x00,0x00,0x24,0xdb,0xff,0xff,0xdb,0x00,0x49,0xff,0x49,0x00,0x24,0xb6,0x00,0x00,0x6d,0xff,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0xff,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0x24,0xb6,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x6d,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0x24,0xdb,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0x6d,0x00,0x00,0x00,0x6d,0xff,0xff,0x92,0x00,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x24,0xb6,0xff,0xff,0xb6,0x00,0x00,0xff,0xff,0xdb,0x6d,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x24,0xb6,0xff,0xff,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x6d,0x6d,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x49,0xff,0x00,0x00,0xff,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x24,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x24,0xb6,0xff,0xff,0xdb,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x92,0x6d,0x00,0x00,0xdb,0x6d,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0xff,0x24,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xb6,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x24,0xb6,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0xff,0x49,0x00,0x49,0xff,0x00,0x00,0xb6,0x6d,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x92,0x00,0x24,0xff,0x00,0x00,0x24,0xdb,0x24,0x00,0xdb,0x6d,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x49,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xb6,0x00,0x92,0xff,0x00,0x00,0xff,0xdb,0x00,0x00,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xdb,0x24,0x00,0x00,0xdb,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0x6d,0x92,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0xb6,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x49,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x24,0xb6,0x00,0x00,0x00,0xb6,0x49,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0x92,0xb6,0x00,0x00,0x00,0x00,0xdb,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x24,0xb6,0xb6,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x24,0xb6,0xb6,0x24,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0xb6,0x6d,0x6d,0xff,0x92,0xdb,0x00,0x00,0x00,0xb6,0x92,0xb6,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x6d,0x6d,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xb6,0x49,0xb6,0xff,0x00,0x00,0xff,0xb6,0x49,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x92,0x00,0x92,0x6d,0x00,0x00,0x92,0x24,0x00,0x00,0xdb,0x00,0x00,0x00,0xb6,0x6d,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0xff,0xff,0xdb,0x49,0x00,0x00,0xff,0x6d,0xff,0xdb,0x24,0x00,0x00,0x00,0x92,0xff,0xff,0xdb,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xb6,0xff,0xdb,0x24,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x49,0xff,0xdb,0xb6,0xff,0x00,0x00,0xff,0x00,0x92,0xb6,0x24,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xb6,0x6d,0xdb,0x49,0x00,0x00,0xff,0x00,0x92,0xb6,0x24,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0xff,0x6d,0xff,0xdb,0x24,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xff,0x49,0xff,0xdb,0x00,0x00,0x49,0xdb,0xff,0xff,0xdb,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x24,0xdb,0xff,0x49,0x00,0x00,0x49,0xff,0x49,0xb6,0x24,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0x49,0xff,0x49,0xb6,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0xb6,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xdb,0x24,0x00,0x00,0xff,0x92,0xff,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xdb,0xff,0xdb,0x00,0x00,0x00,0xdb,0x6d,0x00,0x92,0xff,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xff,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0xdb,0x00,0xdb,0xdb,0x49,0xff,0x00,0x00,0x00,0xff,0x24,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x24,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x6d,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x6d,0xff,0x6d,0xff,0x00,0x00,0xff,0x24,0xdb,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0x92,0x00,0x00,0x00,0x24,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0xdb,0x24,0x00,0x00,0x6d,0x6d,0xff,0x24,0xb6,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x92,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0xff,0x92,0x00,0x49,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x24,0xff,0x00,0x00,0x00,0xff,0xb6,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x49,0xff,0x49,0xdb,0x00,0x00,0xff,0xb6,0x00,0x49,0xdb,0x00,0x00,0xb6,0x92,0x00,0x92,0xb6,0x00,0x00,0xff,0x6d,0x00,0x92,0xb6,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0xb6,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x6d,0x92,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0x00,0xdb,0x24,0xdb,0x00,0x00,0x00,0x6d,0x6d,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xb6,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0xdb,0x49,0x00,0x00,0x00,0x24,0xb6,0x49,0xff,0x49,0x49,0xdb,0x49,0xdb,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0xff,0x00,0xb6,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x24,0x92,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0xff,0x92,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0xb6,0x6d,0x00,0x6d,0xb6,0x00,0x00,0x24,0xdb,0xff,0x92,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0xb6,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x24,0xb6,0xdb,0x49,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0xff,0x00,0xff,0xdb,0x24,0xff,0x00,0x00,0x24,0xdb,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x92,0xb6,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x6d,0xb6,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0xff,0x24,0xff,0x00,0x00,0xff,0x00,0xb6,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x24,0xff,0x00,0x00,0x00,0x49,0xb6,0xff,0x6d,0x92,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x92,0xdb,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x24,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0xdb,0x24,0x00,0x00,0xb6,0x49,0x92,0x49,0xb6,0x00,0x00,0x00,0x49,0xff,0x49,0x00,0x00,0x00,0x00,0xdb,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x49,0xb6,0x92,0x24,0x00,0x92,0x00
+  ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x24,0xb6,0xff,0x00,0x00,0x24,0xb6,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0xff,0x6d,0xb6,0x00,0x00,0x6d,0xff,0xff,0xff,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x49,0xb6,0xff,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xb6,0xb6,0x00,0x00,0x00,0x00,0xdb,0xdb,0xdb,0x6d,0x00,0x00,0x00,0xb6,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x92,0x24,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x92,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x6d,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x49,0xdb,0x00,0x00,0x00,0x92,0x92,0xff,0xdb,0x92,0x00,0x00,0x00,0x49,0xff,0x49,0x00,0x00,0x00,0x00,0xb6,0x49,0xdb,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x92,0x00,0x24,0x92,0xb6,0x49,0x00
+  ,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x6d,0xdb,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0xb6,0xdb,0x6d,0x00,0x24,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x6d,0xb6,0x00,0x00,0x6d,0x92,0x00,0x24,0xdb,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x24,0xdb,0x49,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xdb,0x49,0xb6,0xdb,0xdb,0x00,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xff,0x00,0x00,0x92,0xdb,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x49,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0xdb,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0xb6,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x6d,0xdb,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x00,0xff,0x92,0xff,0x49,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x92,0xff,0x00,0x00,0xff,0x92,0x00,0x49,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x6d,0xb6,0x00,0x00,0xff,0x6d,0x00,0x92,0xb6,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0xff,0x49,0x00,0xb6,0xff,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x6d,0xff,0xb6,0xff,0x49,0x00,0x00,0x00,0xdb,0x24,0xdb,0x00,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0xb6,0x24,0x00,0x49,0xff,0x49,0x24,0xb6,0xff,0xdb,0x49,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xdb,0x00,0x00,0x00,0x00,0xdb,0xdb,0xff,0x49,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0xff,0xff,0xff,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xff,0xff,0xff,0xb6,0x00,0x00,0x00,0x24,0xb6,0xff,0xff,0xb6,0x00,0x00,0xff,0xff,0xdb,0x6d,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0x92,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0xdb,0xff,0xdb,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x49,0x92,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0xdb,0xff,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0x6d,0x92,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0xdb,0x49,0xff,0x49,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0xdb,0x6d,0xff,0x00,0x00,0xff,0x6d,0xff,0xdb,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0xb6,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0x6d,0xff,0xdb,0x00,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0x00,0x00,0x49,0xb6,0x92,0x24,0xff,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0xff,0x24,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x92,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};
+// clang-format on
+
+constexpr char kTextureFirstChar = '!';
+constexpr char kTextureLastChar = '~';
+constexpr int kTextureTotalChars = kTextureLastChar - kTextureFirstChar + 1;
+constexpr int kCharWidth = 7;
+constexpr int kCharHeight = 11;
+constexpr int kTextureWidth = kTextureTotalChars * kCharWidth;
+constexpr int kTextureHeight = kCharHeight;
+
+std::string vert_shader = SHADER0([]() {  // NOLINT
+  layout(location = 0) in vec2 position;
+  layout(location = 1) in vec2 uv;
+
+  out vec2 texCoords;
+
+  void main() {
+    gl_Position = vec4(position, 0.0, 1.0f);
+    texCoords = vec2(uv.x, 1.0 - uv.y);
+  }
+});
+
+// Uniform locations used in the following shader
+#define UNIFORM_LOCATION_DIGITS 0
+#define UNIFORM_LOCATION_COLOR 1
+
+std::string frag_shader = SHADER0([]() {  // NOLINT
+  precision mediump float;
+
+  out vec4 color;
+  in vec2 texCoords;
+  layout(location = 0) uniform sampler2D digitsTexture;
+  layout(location = 1) uniform vec4 mixColor;
+
+  void main() {
+    float alpha = texture(digitsTexture, texCoords).r;
+    color = vec4(mixColor.rgb, alpha * mixColor.a);
+  }
+});
+
+}  // anonymous namespace
+
+void DebugText::SetViewportSize(int viewport_width, int viewport_height) {
+  pixel_size_screen_space_ = vec2(2.0f / static_cast<float>(viewport_width),
+                                  2.0f / static_cast<float>(viewport_height));
+}
+
+DebugText::DebugText(int max_digits, int viewport_width, int viewport_height) {
+  max_digits_ = max_digits;
+  SetViewportSize(viewport_width, viewport_height);
+
+  shader_.Link(vert_shader, frag_shader);
+  shader_.Use();
+  glUniform1i(UNIFORM_LOCATION_DIGITS, 0);
+
+  // Num quads * 6 vertices per quad.
+  mesh_.SetVertices(max_digits * 6, nullptr, GL_TRIANGLES, GL_DYNAMIC_DRAW);
+
+  glGenTextures(1, &texture_);
+  glBindTexture(GL_TEXTURE_2D, texture_);
+  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, kTextureWidth, kTextureHeight, 0,
+               GL_RED, GL_UNSIGNED_BYTE, ascii_texture);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  glBindTexture(GL_TEXTURE_2D, 0);
+  CHECK_GL();
+}
+
+DebugText::~DebugText() {}
+
+void DebugText::Draw(float x, float y, float scale, float r, float g, float b,
+                     float a, const char* str, float stereo_offset,
+                     uint8_t axis) {
+  assert(axis < 2);
+  shader_.Use();
+  glUniform4f(UNIFORM_LOCATION_COLOR, r, g, b, a);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, texture_);
+  CHECK_GL();
+
+  float px = x;
+  float py = y;
+  float x_advance = scale * static_cast<float>(kCharWidth);
+  float y_advance = 0.0f;
+  float x_height = 0.0f;
+  float y_height = scale * static_cast<float>(kCharHeight);
+  if (axis) {
+    std::swap(x_advance, y_advance);
+    std::swap(x_height, y_height);
+  }
+  x_advance *= pixel_size_screen_space_[0];
+  x_height *= pixel_size_screen_space_[0];
+  y_advance *= pixel_size_screen_space_[1];
+  y_height *= pixel_size_screen_space_[1];
+  int max_digits = stereo_offset != 0.0f ? max_digits_ / 2 : max_digits_;
+  int len = std::min(max_digits, static_cast<int>(strlen(str)));
+
+  int num_quads = stereo_offset != 0.0f ? len * 2 : len;
+  std::tuple<vec2, vec2>* vbo =
+      mesh_.Map(GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT, num_quads * 6);
+
+  int v = 0;
+  for (int i = 0; i < len; ++i) {
+    char digit = str[i];
+    if (digit == '\n') {
+      if (axis == 0) {
+        py += y_height;
+        px = x;
+      } else {
+        px -= x_height;
+        py = y;
+      }
+      continue;
+    }
+    if (digit < kTextureFirstChar || digit > kTextureLastChar) {
+      px += x_advance;
+      py += y_advance;
+      continue;
+    }
+
+    int tex_digit = digit - kTextureFirstChar;
+
+    // Add screenspace tri vertices in CCW order starting with bottom left.
+    float tx =
+        static_cast<float>(tex_digit) / static_cast<float>(kTextureTotalChars);
+    float tx2 = tx + 1.0f / static_cast<float>(kTextureTotalChars);
+    vbo[v * 6 + 0] = {vec2(px, py), vec2(tx, 0.0f)};
+    vbo[v * 6 + 1] = {vec2(px + x_advance, py + y_advance), vec2(tx2, 0.0f)};
+    vbo[v * 6 + 2] = {
+        vec2(px + x_advance + x_height, py + y_advance + y_height),
+        vec2(tx2, 1.0f)};
+    vbo[v * 6 + 3] = vbo[v * 6 + 0];
+    vbo[v * 6 + 4] = vbo[v * 6 + 2];
+    vbo[v * 6 + 5] = {vec2(px + x_height, py + y_height), vec2(tx, 1.0f)};
+    px += x_advance;
+    py += y_advance;
+    ++v;
+  }
+
+  if (stereo_offset != 0.0f) {
+    int num_chars = v;
+    for (int i = 0; i < num_chars; ++i) {
+      for (int j = 0; j < 6; ++j) {
+        vbo[v * 6 + j] = vbo[i * 6 + j];
+        // The 0th tuple element is the vertex position vec2.
+        std::get<0>(vbo[v * 6 + j])[axis] += stereo_offset;
+      }
+      ++v;
+    }
+  }
+
+  mesh_.Unmap();
+
+  mesh_.Draw(v * 6);
+  glBindTexture(GL_TEXTURE_2D, texture_);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/egl_image.cpp b/libs/vr/libdvrgraphics/egl_image.cpp
new file mode 100644
index 0000000..26d68cd
--- /dev/null
+++ b/libs/vr/libdvrgraphics/egl_image.cpp
@@ -0,0 +1,22 @@
+#include "include/private/dvr/graphics/egl_image.h"
+
+#include <hardware/gralloc.h>
+
+#include <memory>
+
+#include <private/dvr/native_buffer.h>
+
+namespace android {
+namespace dvr {
+
+EGLImageKHR CreateEglImage(EGLDisplay dpy, int width, int height, int format,
+                           int usage) {
+  auto image = std::make_shared<IonBuffer>(width, height, format, usage);
+
+  return eglCreateImageKHR(
+      dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(new NativeBuffer(image)), nullptr);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/gpu_profiler.cpp b/libs/vr/libdvrgraphics/gpu_profiler.cpp
new file mode 100644
index 0000000..c8c978d
--- /dev/null
+++ b/libs/vr/libdvrgraphics/gpu_profiler.cpp
@@ -0,0 +1,296 @@
+#include "include/private/dvr/graphics/gpu_profiler.h"
+
+#include <log/log.h>
+
+#include <private/dvr/clock_ns.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr int kMaxPendingQueries = 32;
+
+}  // anonynmous namespace
+
+static int64_t AdjustTimerQueryToNs(int64_t gpu_time) { return gpu_time; }
+
+void GpuProfiler::TimerData::reset() {
+  total_elapsed_ns = 0;
+  num_events = 0;
+}
+
+void GpuProfiler::TimerData::print(const char* name) const {
+  ALOGI("GPU_TIME[%s]: %f ms", name,
+        (float)((double)total_elapsed_ns / 1000000.0 / (double)num_events));
+}
+
+// Enter a scope, records the timestamp for later matching with leave.
+void GpuProfiler::TimerData::enter(int64_t timestamp_ns) {
+  entered = true;
+  enter_timestamp_ns = timestamp_ns;
+}
+
+// Compute the elapsed time for the scope.
+void GpuProfiler::TimerData::leave(int64_t timestamp_ns, const char* name,
+                                   std::weak_ptr<int64_t> duration_ns,
+                                   int print_period) {
+  if (!entered) {
+    // We got the leave event but are missing the enter. This can happen if
+    // OnPendingQueryOverflow() is called, or if the calls to enter()/leave()
+    // aren't properly balanced. Ignore the call but print a warning.
+    ALOGW("Ignoring GpuProfiler::TimerData::leave event with no enter event");
+    return;
+  }
+  entered = false;
+
+  int64_t elapsed = timestamp_ns - enter_timestamp_ns;
+  if (elapsed > 1000 * 1000 * 1000) {
+    // More than one second, drop it as invalid data.
+    return;
+  }
+  if (auto out_ns = duration_ns.lock()) {
+    *out_ns = elapsed;
+  }
+  total_elapsed_ns += elapsed;
+  if (print_period > 0 && ++num_events >= print_period) {
+    print(name);
+    reset();
+  }
+}
+
+GpuProfiler* GpuProfiler::Get() {
+  static GpuProfiler* profiler = new GpuProfiler();
+  return profiler;
+}
+
+GpuProfiler::GpuProfiler()
+    : enable_gpu_tracing_(true),
+      has_gl_context_(false),
+      sync_with_cpu_time_(false),
+      gl_timer_offset_ns_(0) {
+}
+
+GpuProfiler::~GpuProfiler() { Clear(); }
+
+bool GpuProfiler::IsGpuProfilingSupported() const {
+  // TODO(jbates) check for GL_EXT_disjoint_timer_query
+  return true;
+}
+
+GLuint GpuProfiler::TryAllocateGlQueryId() {
+  if (pending_gpu_queries_.size() >= kMaxPendingQueries)
+    OnPendingQueryOverflow();
+
+  GLuint query_id = 0;
+  if (gl_timer_query_id_pool_.empty()) {
+    glGenQueries(1, &query_id);
+  } else {
+    query_id = gl_timer_query_id_pool_.top();
+    gl_timer_query_id_pool_.pop();
+  }
+  return query_id;
+}
+
+void GpuProfiler::EnterGlScope(const char* scope_name) {
+  GLuint query_id = TryAllocateGlQueryId();
+  if (query_id != 0) {
+    glQueryCounter(query_id, GL_TIMESTAMP_EXT);
+    pending_gpu_queries_.push_back(
+        GpuTimerQuery(GetSystemClockNs(), scope_name, std::weak_ptr<int64_t>(),
+                      -1, query_id, GpuTimerQuery::kQueryBeginScope));
+  }
+}
+
+void GpuProfiler::LeaveGlScope(const char* scope_name,
+                               std::weak_ptr<int64_t> duration_ns,
+                               int print_period) {
+  GLuint query_id = TryAllocateGlQueryId();
+  if (query_id != 0) {
+    glQueryCounter(query_id, GL_TIMESTAMP_EXT);
+    pending_gpu_queries_.push_back(
+        GpuTimerQuery(GetSystemClockNs(), scope_name, duration_ns, print_period,
+                      query_id, GpuTimerQuery::kQueryEndScope));
+  }
+}
+
+void GpuProfiler::OnGlContextCreated() {
+  has_gl_context_ = true;
+  gl_timer_offset_ns_ = 0;
+  SyncGlTimebase();
+}
+
+void GpuProfiler::OnGlContextDestroyed() {
+  has_gl_context_ = false;
+  Clear();
+}
+
+void GpuProfiler::Clear() {
+  events_.clear();
+  for (auto& query : pending_gpu_queries_)
+    glDeleteQueries(1, &query.query_id);
+  pending_gpu_queries_.clear();
+  while (!gl_timer_query_id_pool_.empty()) {
+    GLuint id = gl_timer_query_id_pool_.top();
+    gl_timer_query_id_pool_.pop();
+    glDeleteQueries(1, &id);
+  }
+}
+
+void GpuProfiler::OnPendingQueryOverflow() {
+  ALOGW("Reached limit of %d pending queries in GpuProfiler."
+        " Clearing all queries.", kMaxPendingQueries);
+  Clear();
+}
+
+void GpuProfiler::SyncGlTimebase() {
+  if (!sync_with_cpu_time_) {
+    return;
+  }
+
+  // Clear disjoint error status.
+  // This error status indicates that we need to ignore the result of the
+  // timer query because of some kind of disjoint GPU event such as heat
+  // throttling.
+  GLint disjoint = 0;
+  glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
+
+  // Try to get the current GL timestamp. Since the GPU can supposedly fail to
+  // produce a timestamp occasionally we try a few times before giving up.
+  int attempts_remaining = 3;
+  do {
+    GLint64 gl_timestamp = 0;
+    glGetInteger64v(GL_TIMESTAMP_EXT, &gl_timestamp);
+    gl_timestamp = AdjustTimerQueryToNs(gl_timestamp);
+
+    // Now get the CPU timebase.
+    int64_t cpu_timebase_ns = static_cast<int64_t>(GetSystemClockNs());
+
+    disjoint = 0;
+    glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
+    if (!disjoint) {
+      gl_timer_offset_ns_ = cpu_timebase_ns - gl_timestamp;
+      break;
+    }
+    ALOGW("WARNING: Skipping disjoint GPU timestamp");
+  } while (--attempts_remaining > 0);
+
+  if (attempts_remaining == 0) {
+    ALOGE("ERROR: Failed to sync GL timebase due to disjoint results\n");
+    gl_timer_offset_ns_ = 0;
+  }
+}
+
+void GpuProfiler::QueryFrameBegin() {
+  GLuint begin_frame_id = TryAllocateGlQueryId();
+  if (begin_frame_id != 0) {
+    glQueryCounter(begin_frame_id, GL_TIMESTAMP_EXT);
+    pending_gpu_queries_.push_back(
+        GpuTimerQuery(GetSystemClockNs(), 0, std::weak_ptr<int64_t>(), -1,
+                      begin_frame_id, GpuTimerQuery::kQueryBeginFrame));
+  }
+}
+
+void GpuProfiler::PollGlTimerQueries() {
+  if (!enabled()) {
+    return;
+  }
+
+#ifdef ENABLE_DISJOINT_TIMER_IGNORING
+  bool has_checked_disjoint = false;
+  bool was_disjoint = false;
+#endif
+  for (;;) {
+    if (pending_gpu_queries_.empty()) {
+      // No queries pending.
+      return;
+    }
+
+    GpuTimerQuery query = pending_gpu_queries_.front();
+
+    GLint available = 0;
+    glGetQueryObjectiv(query.query_id, GL_QUERY_RESULT_AVAILABLE_EXT,
+                       &available);
+    if (!available) {
+      // No queries available.
+      return;
+    }
+
+    // Found an available query, remove it from pending queue.
+    pending_gpu_queries_.pop_front();
+    gl_timer_query_id_pool_.push(query.query_id);
+
+#ifdef ENABLE_DISJOINT_TIMER_IGNORING
+    if (!has_checked_disjoint) {
+      // Check if we need to ignore the result of the timer query because
+      // of some kind of disjoint GPU event such as heat throttling.
+      // If so, we ignore all events that are available during this loop.
+      has_checked_disjoint = true;
+      GLint disjoint_occurred = 0;
+      glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_occurred);
+      was_disjoint = !!disjoint_occurred;
+      if (was_disjoint) {
+        ALOGW("Skipping disjoint GPU events");
+      }
+    }
+
+    if (was_disjoint) {
+      continue;
+    }
+#endif
+
+    GLint64 timestamp_ns = 0;
+    glGetQueryObjecti64v(query.query_id, GL_QUERY_RESULT_EXT, &timestamp_ns);
+    timestamp_ns = AdjustTimerQueryToNs(timestamp_ns);
+
+    int64_t adjusted_timestamp_ns;
+
+    if (sync_with_cpu_time_) {
+      adjusted_timestamp_ns = timestamp_ns + gl_timer_offset_ns_;
+
+      if (query.type == GpuTimerQuery::kQueryBeginFrame ||
+          query.type == GpuTimerQuery::kQueryBeginScope) {
+        if (adjusted_timestamp_ns < query.timestamp_ns) {
+          // GPU clock is behind, adjust our offset to correct it.
+          gl_timer_offset_ns_ += query.timestamp_ns - adjusted_timestamp_ns;
+          adjusted_timestamp_ns = query.timestamp_ns;
+        }
+      }
+    } else {
+      adjusted_timestamp_ns = timestamp_ns;
+    }
+
+    switch (query.type) {
+      case GpuTimerQuery::kQueryBeginFrame:
+        break;
+      case GpuTimerQuery::kQueryBeginScope:
+        events_[query.scope_name].enter(adjusted_timestamp_ns);
+        break;
+      case GpuTimerQuery::kQueryEndScope:
+        events_[query.scope_name].leave(adjusted_timestamp_ns, query.scope_name,
+                                        query.duration_ns, query.print_period);
+        break;
+    }
+  }
+}
+
+void GpuProfiler::FinishGlTimerQueries() {
+  if (!enabled()) {
+    return;
+  }
+
+  glFlush();
+  PollGlTimerQueries();
+  int max_iterations = 100;
+  while (!pending_gpu_queries_.empty()) {
+    if (--max_iterations <= 0) {
+      ALOGE("Error: GL timer queries failed to finish.");
+      break;
+    }
+    PollGlTimerQueries();
+    usleep(1000);
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/blur.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/blur.h
new file mode 100644
index 0000000..c1c2b91
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/blur.h
@@ -0,0 +1,86 @@
+#ifndef ANDROID_DVR_GRAPHICS_BLUR_H_
+#define ANDROID_DVR_GRAPHICS_BLUR_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <private/dvr/ring_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class Blur {
+ public:
+  // Construct a blur kernel for GL that works on source textures of the given
+  // size. The given source_texture is configured for linear filtering.
+  // |source_texture_target| is for |source_texture| while
+  // |target_texture_target| is used for all the intermediate and output
+  // buffers.
+  // |num_blur_outputs| determines how many blurs this instance can be used for
+  // in a single frame.
+  Blur(int w, int h, GLuint source_texture, GLint source_texture_target,
+       GLint target_texture_target, bool is_external, EGLDisplay display,
+       int num_blur_outputs);
+  ~Blur();
+
+  // Place all output textures back into the FBO pool for a new frame.
+  // Call this at the start of each frame before doing one or more blurs.
+  void StartFrame();
+
+  // Draw a multipass blur from the given source_texture. The resulting texture
+  // is returned. The given source_texture is configured for linear filtering.
+  // A segfault will occur if the application calls DrawBlur more times than
+  // |num_blur_outputs| without calling StartFrame.
+  // It is up to the calling code to change the framebuffer after this method.
+  GLuint DrawBlur(GLuint source_texture);
+
+  float width() const { return width_; }
+  float height() const { return height_; }
+  float scale() const { return scale_; }
+
+  // Set the scale of the blur, usually between 0 and 1. This is only useful for
+  // animation.
+  // At the steady state, the scale should be set to 1. To change the steady
+  // state blur appearance, the kernel patterns in DrawBlur should be modified
+  // instead of using scale.
+  void set_scale(float scale) { scale_ = scale; }
+
+  // Animate the blur by |delta|. Clamp the result between |low| and |high|.
+  // Recommended range is between 0 and 1, but other values will also work.
+  void animate(float delta, float low, float high) {
+    scale_ += delta;
+    scale_ = std::min(high, std::max(low, scale_));
+  }
+
+ private:
+  struct Fbo {
+    Fbo() : fbo(0), renderbuffer(0), texture(0), egl_image(0) {}
+    GLuint fbo;
+    GLuint renderbuffer;
+    GLuint texture;
+    EGLImageKHR egl_image;
+  };
+
+  Fbo CreateFbo(int w, int h, GLuint source_texture, GLint tex_target,
+                bool is_external);
+
+  // EGL display for when target texture format is EGL image.
+  EGLDisplay display_;
+  GLint target_texture_target_;
+  int width_;
+  int height_;
+  Fbo source_fbo_;
+  Fbo fbo_half_;
+  std::vector<Fbo> fbo_q_;
+  RingBuffer<Fbo> fbo_q_free_;
+  float scale_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GRAPHICS_BLUR_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/debug_text.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/debug_text.h
new file mode 100644
index 0000000..bbe891b
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/debug_text.h
@@ -0,0 +1,44 @@
+#ifndef ANDROID_DVR_GRAPHICS_FPS_GRAPH_H
+#define ANDROID_DVR_GRAPHICS_FPS_GRAPH_H
+
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+
+namespace android {
+namespace dvr {
+
+// Debug text class that draws small text with Open GL.
+class DebugText {
+ public:
+  DebugText(int max_digits, int viewport_width, int viewport_height);
+  ~DebugText();
+
+  void SetViewportSize(int viewport_width, int viewport_height);
+
+  // Draw text at given screen-space location, scale and color.
+  // A |scale| of 1.0 means 1:1 pixel mapping with current viewport size.
+  // If |stereo_offset| is not zero, the string will be rendered again
+  // with the given offset for stereo rendering. The stereo axis can be on
+  // screenspace x or y axis, which is given by |axis| as 0 or 1,
+  // respectively. |axis| also determines the direction that text is rendered.
+  void Draw(float x, float y, float scale, float r, float g, float b, float a,
+            const char* str, float stereo_offset, uint8_t axis);
+
+  // Helper that draws green text at render target resolution.
+  void Draw(float x, float y, const char* str, float stereo_offset,
+            uint8_t axis) {
+    Draw(x, y, 1.0f, 0, 1, 0, 1, str, stereo_offset, axis);
+  }
+
+ private:
+  int max_digits_;
+  vec2 pixel_size_screen_space_;
+  ShaderProgram shader_;
+  GLuint texture_;
+  Mesh<vec2, vec2> mesh_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GRAPHICS_FPS_GRAPH_H
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/egl_image.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/egl_image.h
new file mode 100644
index 0000000..59de61e
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/egl_image.h
@@ -0,0 +1,21 @@
+#ifndef ANDROID_DVR_GRAPHICS_EGL_IMAGE_H_
+#define ANDROID_DVR_GRAPHICS_EGL_IMAGE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+namespace android {
+namespace dvr {
+
+// Create an EGLImage with texture storage defined by the given format and
+// usage flags.
+// For example, to create an RGBA texture for rendering to, specify:
+//   format = HAL_PIXEL_FORMAT_RGBA_8888;
+//   usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER;
+EGLImageKHR CreateEglImage(EGLDisplay dpy, int width, int height, int format,
+                           int usage);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GRAPHICS_EGL_IMAGE_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/gpu_profiler.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/gpu_profiler.h
new file mode 100644
index 0000000..c6e752b
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/gpu_profiler.h
@@ -0,0 +1,236 @@
+#ifndef ANDROID_DVR_GPU_PROFILER_H_
+#define ANDROID_DVR_GPU_PROFILER_H_
+
+// This file contains classes and macros related to run-time performance
+// profiling of GPU processing.
+
+#include <deque>
+#include <map>
+#include <memory>
+#include <stack>
+#include <vector>
+
+#include <private/dvr/graphics/vr_gl_extensions.h>
+
+namespace android {
+namespace dvr {
+
+// While enabled, GL commands will be submitted each frame to query timestamps
+// of GPU workloads that have been traced using the ION_PROFILE_GPU macro
+// defined below.
+//
+// Basic workflow:
+//  - have the app framework call PollGlTimerQueries at the start of each frame.
+//  - place ION_PROFILE_GPU("MyGlWorkload") at the start of code scopes where
+//    GL draw commands are performed that you want to trace.
+class GpuProfiler {
+ public:
+  // Gets the GpuProfiler singleton instance.
+  static GpuProfiler* Get();
+
+  GpuProfiler();
+  ~GpuProfiler();
+
+  bool IsGpuProfilingSupported() const;
+
+  // Enables runtime GPU tracing. While enabled, GL commands will be submitted
+  // each frame to query timestamps of GPU workloads that have been traced using
+  // one of the TRACE_GPU* macros defined below.
+  void SetEnableGpuTracing(bool enabled) { enable_gpu_tracing_ = enabled; }
+
+  bool enabled() const { return enable_gpu_tracing_ && has_gl_context_; }
+
+  // Attempt to keep the GPU times in sync with CPU times.
+  void SetEnableSyncCpuTime(bool enabled) { sync_with_cpu_time_ = enabled; }
+
+  // When sync cpu time is enabled because of mobile GPU timer query issues,
+  // it can sometimes help to put a beginning timer query at the start of the
+  // frame to sync the CPU time when GPU work begins.
+  void QueryFrameBegin();
+
+  // Polls (non-blocking) for completed GL timer query data and adds events into
+  // the trace buffer. Must call once close to the start of each frame.
+  void PollGlTimerQueries();
+
+  // Call glFinish and process all pending timer queries.
+  void FinishGlTimerQueries();
+
+  // Records the beginning of a scoped GL trace event.
+  void EnterGlScope(const char* scope_name);
+
+  // Records the end of a scoped GL trace event.
+  void LeaveGlScope(const char* scope_name, std::weak_ptr<int64_t> duration_ns,
+                    int print_period);
+
+  // Must be called when the GL context is created. The GpuProfiler will be
+  // inactive until this is called.
+  void OnGlContextCreated();
+
+  // Must be called before the GL context is destroyed. The GpuProfiler will be
+  // inactive until a call to OnGlContextCreated().
+  void OnGlContextDestroyed();
+
+ private:
+  // Data to queue the pending GPU timer queries that need to be polled
+  // for completion.
+  struct GpuTimerQuery {
+    enum QueryType {
+      kQueryBeginFrame,
+      kQueryBeginScope,
+      kQueryEndScope,
+    };
+
+    // scope_id is only required for kQueryBeginScope query types.
+    GpuTimerQuery(int64_t timestamp_ns, const char* scope_name,
+                  std::weak_ptr<int64_t> duration_ns, int print_period,
+                  GLuint query_id, QueryType type)
+        : timestamp_ns(timestamp_ns),
+          scope_name(scope_name),
+          duration_ns(duration_ns),
+          print_period(print_period),
+          query_id(query_id),
+          type(type) {}
+
+    int64_t timestamp_ns;
+    const char* scope_name;
+    std::weak_ptr<int64_t> duration_ns;
+    int print_period;
+    GLuint query_id;
+    QueryType type;
+  };
+
+  // Struct that tracks timing data for a particular trace scope.
+  struct TimerData {
+    void reset();
+
+    // Print the profiling data.
+    void print(const char* name) const;
+
+    // Enter a scope, records the timestamp for later matching with leave.
+    void enter(int64_t timestamp_ns);
+
+    // Compute the elapsed time for the scope.
+    void leave(int64_t timestamp_ns, const char* name,
+               std::weak_ptr<int64_t> duration_ns, int print_period);
+
+    bool entered = false;
+    int64_t total_elapsed_ns = 0;
+    int64_t enter_timestamp_ns = 0;
+    int num_events = 0;
+  };
+
+  // Clear out events and free GL resources.
+  void Clear();
+
+  // Called when we detect that we've overflowed the pending query queue. This
+  // shouldn't occur in practice, and probably indicates some internal
+  // mismanagement of the gl query objects.
+  void OnPendingQueryOverflow();
+
+  // Synchronises the GL timebase with the CallTraceManager timebase.
+  void SyncGlTimebase();
+
+  // Returns a GL timer query ID if possible. Otherwise returns 0.
+  GLuint TryAllocateGlQueryId();
+
+  // Setting for enabling GPU tracing.
+  bool enable_gpu_tracing_;
+
+  // True if we have a GL context, false otherwise. When the GpuProfiler is
+  // first created we assume no GL context.
+  bool has_gl_context_;
+
+  // Setting for synchronizing GPU timestamps with CPU time.
+  bool sync_with_cpu_time_;
+
+  // Nanosecond offset to the GL timebase to compute the CallTraceManager time.
+  int64_t gl_timer_offset_ns_;
+
+  std::map<const char*, TimerData> events_;
+
+  // For GPU event TraceRecords, this tracks the pending queries that will
+  // be asynchronously polled (in order) and then added to the TraceRecorder
+  // buffer with the GPU timestamps.
+  std::deque<GpuTimerQuery> pending_gpu_queries_;
+
+  // Available ids for use with GLTimerQuery as needed. This will generally
+  // reach a steady state after a few frames. Always push and pop from the back
+  // to avoid shifting the vector.
+  std::stack<GLuint, std::vector<GLuint> > gl_timer_query_id_pool_;
+};
+
+// Traces the GPU start and end times of the GL commands submitted in the
+// same scope. Typically used via the TRACE_GPU macro.
+class ScopedGlTracer {
+ public:
+  ScopedGlTracer(const char* name, std::weak_ptr<int64_t> duration_ns,
+                 int print_period, bool finish)
+      : name_(name),
+        duration_ns_(duration_ns),
+        print_period_(print_period),
+        is_finish_(finish) {
+    GpuProfiler* profiler = GpuProfiler::Get();
+    if (profiler->enabled()) {
+      profiler->EnterGlScope(name);
+    }
+  }
+
+  ~ScopedGlTracer() {
+    GpuProfiler* profiler = GpuProfiler::Get();
+    if (profiler->enabled()) {
+      profiler->LeaveGlScope(name_, duration_ns_, print_period_);
+      if (is_finish_) {
+        GpuProfiler::Get()->FinishGlTimerQueries();
+      }
+    }
+  }
+
+ private:
+  const char* name_;
+  std::weak_ptr<int64_t> duration_ns_;
+  int print_period_;
+  bool is_finish_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#define PROFILING_PASTE1(x, y) x##y
+#define PROFILING_PASTE2(x, y) PROFILING_PASTE1(x, y)
+#define PROFILING_PASTE3(x) PROFILING_PASTE2(x, __LINE__)
+
+// This macro can be used in any GL operation scope to trace the resulting
+// GPU work. The argument must be a literal string. Specify the number of frames
+// to wait before printing an average result in the num_frames_period argument.
+#define TRACE_GPU_PRINT(group_name, num_frames_period)        \
+  (void)group_name " must be a literal string.";              \
+  android::dvr::ScopedGlTracer PROFILING_PASTE3(gpu_tracer_)( \
+      group_name, std::weak_ptr<int64_t>(), num_frames_period, false)
+
+// This macro can be used in any GL operation scope to trace the resulting
+// GPU work. The argument must be a literal string. The duration parameter
+// is a weak_ptr to a int64_t that will receive duration values asynchronously
+// during calls to PollGlTimerQueries.
+#define TRACE_GPU(group_name, duration_ns_weak_ptr)           \
+  (void)group_name " must be a literal string.";              \
+  android::dvr::ScopedGlTracer PROFILING_PASTE3(gpu_tracer_)( \
+      group_name, duration_ns_weak_ptr, -1, false)
+
+// This macro can be used in any GL operation scope to trace the resulting
+// GPU work. The argument must be a literal string. Specify the number of frames
+// to wait before printing an average result in the num_frames_period argument.
+#define TRACE_GPU_PRINT_FINISH(group_name)                    \
+  (void)group_name " must be a literal string.";              \
+  android::dvr::ScopedGlTracer PROFILING_PASTE3(gpu_tracer_)( \
+      group_name, std::weak_ptr<int64_t>(), 1, true)
+
+// This macro can be used in any GL operation scope to trace the resulting
+// GPU work. The argument must be a literal string. The duration parameter
+// is a weak_ptr to a int64_t that will receive duration values asynchronously
+// during calls to PollGlTimerQueries.
+#define TRACE_GPU_FINISH(group_name, duration_ns_weak_ptr)    \
+  (void)group_name " must be a literal string.";              \
+  android::dvr::ScopedGlTracer PROFILING_PASTE3(gpu_tracer_)( \
+      group_name, duration_ns_weak_ptr, -1, true)
+
+#endif  // ANDROID_DVR_GPU_PROFILER_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/indexed_mesh.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/indexed_mesh.h
new file mode 100644
index 0000000..7e74a75
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/indexed_mesh.h
@@ -0,0 +1,154 @@
+#ifndef ANDROID_DVR_GRAPHICS_INDEXED_MESH_H_
+#define ANDROID_DVR_GRAPHICS_INDEXED_MESH_H_
+
+#include <private/dvr/graphics/vertex_attributes.h>
+#include <private/dvr/types.h>
+
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <tuple>
+
+namespace android {
+namespace dvr {
+
+namespace Details {
+
+// We can have 16 and 32bit indices.
+template <typename T>
+GLenum GetIndexType();
+template <>
+inline GLenum GetIndexType<uint16_t>() {
+  return GL_UNSIGNED_SHORT;
+}
+template <>
+inline GLenum GetIndexType<uint32_t>() {
+  return GL_UNSIGNED_INT;
+}
+
+}  // namespace Details
+
+template <typename INDEX_TYPE, typename... Attributes>
+class IndexedMesh {
+ public:
+  static const int attribute_size = sizeof(std::tuple<Attributes...>);
+
+  IndexedMesh() {}
+  IndexedMesh(INDEX_TYPE number_of_vertices, const void* vertices,
+              INDEX_TYPE number_of_indices, const void* indices) {
+    SetVertices(number_of_vertices, vertices, number_of_indices, indices);
+  }
+
+  IndexedMesh(INDEX_TYPE number_of_vertices, const void* vertices,
+              INDEX_TYPE number_of_indices, const void* indices,
+              GLenum element_type) {
+    SetVertices(number_of_vertices, vertices, number_of_indices, indices,
+                element_type);
+  }
+
+  IndexedMesh(IndexedMesh&& to_move) { Swap(to_move); }
+
+  ~IndexedMesh() { DeleteGLData(); }
+
+  IndexedMesh& operator=(IndexedMesh&& to_move) {
+    Swap(to_move);
+    return *this;
+  }
+
+  operator bool() const { return mesh_vbo_ != 0; }
+
+  void Swap(IndexedMesh& to_swap) {
+    std::swap(mesh_vbo_, to_swap.mesh_vbo_);
+    std::swap(mesh_vao_, to_swap.mesh_vao_);
+    std::swap(mesh_ibo_, to_swap.mesh_ibo_);
+    std::swap(number_of_indices_, to_swap.number_of_indices_);
+    std::swap(element_type_, to_swap.element_type_);
+  }
+
+  void Draw() {
+    if (!mesh_vbo_)
+      return;
+
+    glBindVertexArray(mesh_vao_);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_);
+
+    glDrawElements(element_type_, number_of_indices_,
+                   Details::GetIndexType<INDEX_TYPE>(), nullptr);
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    glBindVertexArray(0);
+  }
+
+  void SetVertices(INDEX_TYPE number_of_vertices, const void* vertices,
+                   INDEX_TYPE number_of_indices, const void* indices,
+                   GLenum element_type) {
+    element_type_ = element_type;
+    SetVertices(number_of_vertices, vertices, number_of_indices, indices);
+  }
+
+  void SetVertices(INDEX_TYPE number_of_vertices, const void* vertices,
+                   INDEX_TYPE number_of_indices, const void* indices) {
+    DeleteGLData();
+    number_of_indices_ = number_of_indices;
+    glGenBuffers(1, &mesh_vbo_);
+    glGenVertexArrays(1, &mesh_vao_);
+    glGenBuffers(1, &mesh_ibo_);
+    glBindVertexArray(mesh_vao_);
+
+    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_);
+    glBufferData(GL_ARRAY_BUFFER, attribute_size * number_of_vertices, vertices,
+                 GL_STATIC_DRAW);
+
+    SetupAttributes();
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+                 sizeof(INDEX_TYPE) * number_of_indices_, indices,
+                 GL_STATIC_DRAW);
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindVertexArray(0);
+  }
+
+  size_t GetAttributesSize() const { return attribute_size; }
+
+ private:
+  IndexedMesh(const IndexedMesh&) = delete;
+  IndexedMesh& operator=(const IndexedMesh&) = delete;
+
+  void DeleteGLData() {
+    if (mesh_vbo_) {
+      glDeleteBuffers(1, &mesh_vbo_);
+      glDeleteVertexArrays(1, &mesh_vao_);
+      glDeleteBuffers(1, &mesh_ibo_);
+      mesh_vbo_ = 0;
+      mesh_vao_ = 0;
+      mesh_ibo_ = 0;
+      number_of_indices_ = 0;
+    }
+  }
+
+  void SetupAttributes() {
+    const auto size = std::tuple_size<std::tuple<Attributes...>>::value;
+    Details::VertexAttribHelper<size - 1, Attributes...>{}();
+  }
+
+ private:
+  GLuint mesh_vbo_ = 0;
+  GLuint mesh_vao_ = 0;
+  GLuint mesh_ibo_ = 0;
+  INDEX_TYPE number_of_indices_ = 0;
+
+  GLenum element_type_ = GL_TRIANGLES;
+};
+
+template <typename... Attributes>
+using Indexed16Mesh = IndexedMesh<uint16_t, Attributes...>;
+
+template <typename... Attributes>
+using Indexed32Mesh = IndexedMesh<uint32_t, Attributes...>;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GRAPHICS_INDEXED_MESH_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/mesh.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/mesh.h
new file mode 100644
index 0000000..45bc108
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/mesh.h
@@ -0,0 +1,128 @@
+#ifndef ANDROID_DVR_GRAPHICS_MESH_H_
+#define ANDROID_DVR_GRAPHICS_MESH_H_
+
+#include <private/dvr/graphics/vertex_attributes.h>
+#include <private/dvr/types.h>
+
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <tuple>
+
+namespace android {
+namespace dvr {
+
+template <typename... Attributes>
+class Mesh {
+ public:
+  static const int attribute_size = sizeof(std::tuple<Attributes...>);
+
+  Mesh() {}
+
+  Mesh(uint32_t number_of_vertices, const void* vertices) {
+    SetVertices(number_of_vertices, vertices);
+  }
+
+  Mesh(uint32_t number_of_vertices, const void* vertices, GLenum element_type) {
+    SetVertices(number_of_vertices, vertices, element_type);
+  }
+
+  Mesh(Mesh&& to_move) { Swap(to_move); }
+
+  ~Mesh() { DeleteGLData(); }
+
+  Mesh& operator=(const Mesh&& to_move) {
+    Swap(to_move);
+    return *this;
+  }
+
+  operator bool() const { return mesh_vbo_ != 0; }
+
+  void Swap(Mesh& to_swap) {
+    std::swap(mesh_vbo_, to_swap.mesh_vbo_);
+    std::swap(mesh_vao_, to_swap.mesh_vao_);
+    std::swap(number_of_vertices_, to_swap.number_of_vertices_);
+    std::swap(element_type_, to_swap.element_type_);
+  }
+
+  void Draw(uint32_t number_of_vertices) {
+    if (!mesh_vbo_)
+      return;
+
+    glBindVertexArray(mesh_vao_);
+    glDrawArrays(element_type_, 0, number_of_vertices);
+    glBindVertexArray(0);
+  }
+
+  void Draw() { Draw(number_of_vertices_); }
+
+  void SetVertices(uint32_t number_of_vertices, const void* vertices,
+                   GLenum element_type, GLenum usage) {
+    DeleteGLData();
+    element_type_ = element_type;
+    number_of_vertices_ = number_of_vertices;
+    glGenBuffers(1, &mesh_vbo_);
+    glGenVertexArrays(1, &mesh_vao_);
+    glBindVertexArray(mesh_vao_);
+
+    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_);
+    glBufferData(GL_ARRAY_BUFFER, attribute_size * number_of_vertices, vertices,
+                 usage);
+
+    SetupAttributes();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindVertexArray(0);
+  }
+
+  void SetVertices(uint32_t number_of_vertices, const void* vertices) {
+    SetVertices(number_of_vertices, vertices, element_type_, GL_STATIC_DRAW);
+  }
+
+  void SetVertices(uint32_t number_of_vertices, const void* vertices,
+                   GLenum element_type) {
+    SetVertices(number_of_vertices, vertices, element_type, GL_STATIC_DRAW);
+  }
+
+  std::tuple<Attributes...>* Map(GLbitfield access, int num_vertices) {
+    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_);
+    void* ptr = glMapBufferRange(GL_ARRAY_BUFFER, 0,
+                                 attribute_size * num_vertices, access);
+    return static_cast<std::tuple<Attributes...>*>(ptr);
+  }
+
+  void Unmap() {
+    glUnmapBuffer(GL_ARRAY_BUFFER);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+  }
+
+ private:
+  Mesh(const Mesh&) = delete;
+  Mesh& operator=(const Mesh&) = delete;
+
+  void DeleteGLData() {
+    if (mesh_vbo_) {
+      glDeleteBuffers(1, &mesh_vbo_);
+      glDeleteVertexArrays(1, &mesh_vao_);
+      mesh_vbo_ = 0;
+      mesh_vao_ = 0;
+      number_of_vertices_ = 0;
+    }
+  }
+
+  void SetupAttributes() {
+    const auto size = std::tuple_size<std::tuple<Attributes...>>::value;
+    Details::VertexAttribHelper<size - 1, Attributes...>{}();
+  }
+
+ private:
+  GLuint mesh_vbo_ = 0;
+  GLuint mesh_vao_ = 0;
+  uint32_t number_of_vertices_ = 0;
+
+  GLenum element_type_ = GL_TRIANGLES;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GRAPHICS_MESH_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/shader_program.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/shader_program.h
new file mode 100644
index 0000000..4218a73
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/shader_program.h
@@ -0,0 +1,75 @@
+#ifndef ANDROID_DVR_SHADER_PROGRAM_H_
+#define ANDROID_DVR_SHADER_PROGRAM_H_
+
+#include <EGL/egl.h>
+#include <GLES3/gl31.h>
+#include <sys/cdefs.h>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+// Helper function that allows you to write a shader as a Lambda.  This allows
+// an IDE to syntax highlight the contents of a shader, as well as preventing
+// quotations on each line. Usage: std::string vs = SHADER0([]() { ... });
+template <size_t size>
+std::string StripLambda(const char (&shader)[size]) {
+  return std::string(shader + 6, shader + size - 2);
+}
+
+#define SHADER0(Src) ::android::dvr::StripLambda(#Src)
+
+// Helper function that takes a shader source string containing %0, %1, %n,
+// tokens and replaces them with replacements[0], replacements[1],
+// replacements[n].  For example:
+// shader = "{
+//   uniform vec2 %0;
+//   %1
+//   ...
+//     %0.x = 1.0; ...
+//     %1(%0);
+// }"
+// -> %0 = "myVarName", %1 = "void f(vec2 v) { ... }"
+std::string ComposeShader(const std::string& shader_code,
+                          const std::vector<std::string>& replacements);
+
+class ShaderProgram {
+ public:
+  ShaderProgram();
+  ShaderProgram(const std::string& vertext_source,
+                const std::string& fragment_source);
+  ShaderProgram(ShaderProgram&&);
+  ~ShaderProgram();
+
+  ShaderProgram& operator=(ShaderProgram&&);
+
+  void Link(const std::string& vertext_source,
+            const std::string& fragment_source);
+
+  void Link(const std::string& compute_source);
+
+  void Use() const;
+
+  GLuint GetProgram() const { return program_; }
+  GLuint GetUniformLocation(const GLchar* name) const {
+    return glGetUniformLocation(program_, name);
+  }
+  GLuint GetAttribLocation(const GLchar* name) const {
+    return glGetAttribLocation(program_, name);
+  }
+
+  bool IsUsable() const { return program_ != 0; }
+  explicit operator bool() const { return IsUsable(); }
+
+ private:
+  ShaderProgram(const ShaderProgram&) = delete;
+  ShaderProgram& operator=(const ShaderProgram&) = delete;
+
+  GLuint program_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SHADER_PROGRAM_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/timer_query.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/timer_query.h
new file mode 100644
index 0000000..11d4d01
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/timer_query.h
@@ -0,0 +1,52 @@
+#ifndef ANDROID_DVR_GRAPHICS_TIMER_QUERY_H_
+#define ANDROID_DVR_GRAPHICS_TIMER_QUERY_H_
+
+#include <GLES3/gl3.h>
+
+namespace android {
+namespace dvr {
+
+// Class used to asynchronously query time between draw calls on gpu.
+class TimerQuery {
+ public:
+  TimerQuery();
+  ~TimerQuery();
+
+  // Marks the start of the timer on gpu.
+  void Begin();
+
+  // Marks the end of the timer on gpu.
+  void End();
+
+  // Gets the time that has passed from call to Begin to End.
+  // Should be called only after the frame has been presented (after the call to
+  // swapbuffers).
+  double GetTimeInMS();
+
+ private:
+  // Generates OpenGL query object.
+  void Init();
+  // Deletes OpenGL query object.
+  void Delete();
+
+  GLuint query_ = 0;
+
+  friend class SyncTimerQuery;
+};
+
+// Simplification of TimerQuery that allows to synchronously query time used
+// for draw calls on gpu by doing glFlush and stalling cpu.
+class SyncTimerQuery {
+ public:
+  SyncTimerQuery();
+
+  double FlushAndGetTimeInMS();  // Note: This WILL cause a glFlush()
+
+ private:
+  TimerQuery timer_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GRAPHICS_TIMER_QUERY_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/vertex_attributes.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/vertex_attributes.h
new file mode 100644
index 0000000..dac5b64
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/vertex_attributes.h
@@ -0,0 +1,64 @@
+#ifndef ANDROID_DVR_GRAPHICS_VERTEX_ATTRIBUTES_H_
+#define ANDROID_DVR_GRAPHICS_VERTEX_ATTRIBUTES_H_
+
+#include <private/dvr/types.h>
+
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <tuple>
+
+namespace android {
+namespace dvr {
+
+namespace Details {
+
+// Set up the vertex attributes by iterating over the variadic template
+// parameters.  The supported attributes are the GetSize and GetType
+// specializations.
+// clang-format off
+template<typename T> GLint GetSize();
+template<> inline GLint GetSize<vec2>() { return 2; }
+template<> inline GLint GetSize<vec3>() { return 3; }
+template<> inline GLint GetSize<vec4>() { return 4; }
+
+template<typename T> GLenum GetType();
+template<> inline GLenum GetType<vec2>() { return GL_FLOAT; }
+template<> inline GLenum GetType<vec3>() { return GL_FLOAT; }
+template<> inline GLenum GetType<vec4>() { return GL_FLOAT; }
+// clang-format on
+
+template <typename T>
+void VertexAttrib(GLuint index, GLsizei stride, const GLvoid* pointer) {
+  glVertexAttribPointer(index, GetSize<T>(), GetType<T>(), GL_FALSE, stride,
+                        pointer);
+  glEnableVertexAttribArray(index);
+}
+
+// Recursion variadic template parameter iterator.
+template <int index, typename... Ts>
+struct VertexAttribHelper {
+  using tuple = std::tuple<Ts...>;
+  size_t operator()() {
+    size_t offset = VertexAttribHelper<index - 1, Ts...>{}();
+    using type = typename std::tuple_element<index, tuple>::type;
+    VertexAttrib<type>(index, sizeof(tuple), reinterpret_cast<void*>(offset));
+    return offset + sizeof(type);
+  }
+};
+
+// Recursion stop point.
+template <typename... Ts>
+struct VertexAttribHelper<0, Ts...> {
+  using tuple = std::tuple<Ts...>;
+  size_t operator()() {
+    using type = typename std::tuple_element<0, tuple>::type;
+    VertexAttrib<type>(0, sizeof(tuple), nullptr);
+    return sizeof(type);
+  }
+};
+}  // namespace Details
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GRAPHICS_VERTEX_ATTRIBUTES_H_
diff --git a/libs/vr/libdvrgraphics/include/private/dvr/graphics/vr_gl_extensions.h b/libs/vr/libdvrgraphics/include/private/dvr/graphics/vr_gl_extensions.h
new file mode 100644
index 0000000..9635dbb
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/vr_gl_extensions.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_VR_GL_EXTENSIONS_H_
+#define ANDROID_DVR_VR_GL_EXTENSIONS_H_
+
+// clang-format off
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl31.h>
+#include <GLES3/gl3ext.h>
+// clang-format on
+
+// GL_EXT_disjoint_timer_query API function declarations
+extern PFNGLGETQUERYOBJECTI64VEXTPROC glGetQueryObjecti64v;
+extern PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectiv;
+extern PFNGLQUERYCOUNTEREXTPROC glQueryCounter;
+
+// EXT_buffer_storage:
+extern PFNGLBUFFERSTORAGEEXTPROC glBufferStorage;
+
+typedef void(GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR)(
+    GLenum target, GLenum attachment, GLuint texture, GLint level,
+    GLint baseViewIndex, GLsizei numViews);
+typedef void(GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR)(
+    GLenum target, GLenum attachement, GLuint texture, GLint level,
+    GLsizei samples, GLint baseViewIndex, GLsizei numViews);
+
+extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR glFramebufferTextureMultiview;
+extern PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR
+    glFramebufferTextureMultisampleMultiview;
+
+// QCOM_gralloc_buffer_data and QCOM_shared_buffer
+typedef void(GL_APIENTRY* PFNGLGRALLOCBUFFERDATAQCOM)(GLenum target,
+                                                      GLsizeiptr sizeInBytes,
+                                                      GLvoid* hostPtr,
+                                                      GLint fd);
+typedef void(GL_APIENTRY* PFNGLSHAREDBUFFERCREATEQCOM)(GLsizeiptr sizeInBytes,
+                                                       GLint* outFd);
+typedef void(GL_APIENTRY* PFNGLSHAREDBUFFERDESTROYQCOM)(GLint fd);
+typedef void(GL_APIENTRY* PFNGLSHAREDBUFFERBINDQCOM)(GLenum target,
+                                                     GLsizeiptr sizeInBytes,
+                                                     GLint fd);
+
+extern PFNGLGRALLOCBUFFERDATAQCOM glGrallocBufferDataQCOM;
+extern PFNGLSHAREDBUFFERCREATEQCOM glCreateSharedBufferQCOM;
+extern PFNGLSHAREDBUFFERDESTROYQCOM glDestroySharedBufferQCOM;
+extern PFNGLSHAREDBUFFERBINDQCOM glBindSharedBufferQCOM;
+
+extern "C" void load_gl_extensions();
+
+#endif  // ANDROID_DVR_VR_GL_EXTENSIONS_H_
diff --git a/libs/vr/libdvrgraphics/shader_program.cpp b/libs/vr/libdvrgraphics/shader_program.cpp
new file mode 100644
index 0000000..2d36600
--- /dev/null
+++ b/libs/vr/libdvrgraphics/shader_program.cpp
@@ -0,0 +1,163 @@
+#include "include/private/dvr/graphics/shader_program.h"
+
+#include <regex>
+#include <sstream>
+
+#include <log/log.h>
+
+namespace {
+
+static bool CompileShader(GLuint shader, const std::string& shader_string) {
+  std::string prefix;
+  const std::string kVersion = "#version";
+  if (shader_string.substr(0, kVersion.size()) != kVersion) {
+    prefix = "#version 310 es\n";
+  }
+  std::string string_with_prefix = prefix + shader_string;
+  const char* shader_str[] = {string_with_prefix.data()};
+  glShaderSource(shader, 1, shader_str, nullptr);
+  glCompileShader(shader);
+
+  GLint success;
+  glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
+  if (!success) {
+    GLchar infoLog[512];
+    glGetShaderInfoLog(shader, 512, nullptr, infoLog);
+    ALOGE("Shader Failed to compile: %s -- %s", *shader_str, infoLog);
+    return false;
+  }
+  return true;
+}
+
+static bool LinkProgram(GLuint program, GLuint vertex_shader,
+                        GLuint fragment_shader) {
+  glAttachShader(program, vertex_shader);
+  glAttachShader(program, fragment_shader);
+  glLinkProgram(program);
+
+  // Check for linking errors
+  GLint success;
+  glGetProgramiv(program, GL_LINK_STATUS, &success);
+  if (!success) {
+    GLchar infoLog[512];
+    glGetProgramInfoLog(program, 512, nullptr, infoLog);
+    ALOGE("Shader failed to link: %s", infoLog);
+    return false;
+  }
+
+  return true;
+}
+
+static bool LinkProgram(GLuint program, GLuint compute_shader) {
+  glAttachShader(program, compute_shader);
+  glLinkProgram(program);
+
+  // Check for linking errors
+  GLint success;
+  glGetProgramiv(program, GL_LINK_STATUS, &success);
+  if (!success) {
+    GLchar infoLog[512];
+    glGetProgramInfoLog(program, 512, nullptr, infoLog);
+    ALOGE("Shader failed to link: %s", infoLog);
+    return false;
+  }
+
+  return true;
+}
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+ShaderProgram::ShaderProgram() : program_(0) {}
+
+ShaderProgram::ShaderProgram(const std::string& vertext_source,
+                             const std::string& fragment_source)
+    : program_(0) {
+  Link(vertext_source, fragment_source);
+}
+
+ShaderProgram::ShaderProgram(ShaderProgram&& to_move) {
+  std::swap(program_, to_move.program_);
+}
+
+ShaderProgram::~ShaderProgram() {
+  if (program_)
+    glDeleteProgram(program_);
+}
+
+ShaderProgram& ShaderProgram::operator=(ShaderProgram&& to_move) {
+  std::swap(program_, to_move.program_);
+  return *this;
+}
+
+void ShaderProgram::Link(const std::string& vertext_source,
+                         const std::string& fragment_source) {
+  if (program_)
+    glDeleteProgram(program_);
+  program_ = glCreateProgram();
+  GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+  GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+
+  bool success = CompileShader(vertex_shader, vertext_source) &&
+                 CompileShader(fragment_shader, fragment_source) &&
+                 LinkProgram(program_, vertex_shader, fragment_shader);
+
+  glDeleteShader(vertex_shader);
+  glDeleteShader(fragment_shader);
+
+  if (!success) {
+    glDeleteProgram(program_);
+    program_ = 0;
+  }
+}
+
+void ShaderProgram::Link(const std::string& compute_source) {
+  if (program_)
+    glDeleteProgram(program_);
+  program_ = glCreateProgram();
+  GLuint shader = glCreateShader(GL_COMPUTE_SHADER);
+
+  bool success =
+      CompileShader(shader, compute_source) && LinkProgram(program_, shader);
+
+  glDeleteShader(shader);
+
+  if (!success) {
+    glDeleteProgram(program_);
+    program_ = 0;
+  }
+}
+
+void ShaderProgram::Use() const { glUseProgram(program_); }
+
+std::string ComposeShader(const std::string& shader_code,
+                          const std::vector<std::string>& variables) {
+  std::stringstream result_stream;
+  std::regex expression("%([0-9]*)");
+  using reg_iter = std::regex_token_iterator<std::string::const_iterator>;
+  reg_iter rend;
+  // match the string and number (drop the %)
+  std::vector<int> submatches = {-1, 1};
+  reg_iter reg(shader_code.begin(), shader_code.end(), expression, submatches);
+  bool is_even = true;
+  while (reg != rend) {
+    if (is_even) {
+      // even entries is the code between the %n's
+      result_stream << *reg;
+    } else {
+      // odd entries are the index into the passed in variables.
+      size_t i = static_cast<size_t>(std::stoi(*reg));
+      if (i < variables.size()) {
+        result_stream << variables[i];
+      }
+    }
+    is_even = !is_even;
+    ++reg;
+  }
+  return result_stream.str();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/timer_query.cpp b/libs/vr/libdvrgraphics/timer_query.cpp
new file mode 100644
index 0000000..23d2b7c
--- /dev/null
+++ b/libs/vr/libdvrgraphics/timer_query.cpp
@@ -0,0 +1,65 @@
+#include "include/private/dvr/graphics/timer_query.h"
+
+#include <GLES2/gl2ext.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+TimerQuery::TimerQuery() {}
+
+TimerQuery::~TimerQuery() { Delete(); }
+
+void TimerQuery::Init() { glGenQueriesEXT(1, &query_); }
+
+void TimerQuery::Delete() {
+  if (query_) {
+    glDeleteQueriesEXT(1, &query_);
+    query_ = 0;
+  }
+}
+
+void TimerQuery::Begin() {
+  if (query_ == 0) {
+    Init();
+  }
+  glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query_);
+}
+
+void TimerQuery::End() { glEndQueryEXT(GL_TIME_ELAPSED_EXT); }
+
+double TimerQuery::GetTimeInMS() {
+  GLuint64 elapsed_time = 0;
+  glGetQueryObjectui64vEXT(query_, GL_QUERY_RESULT, &elapsed_time);
+  return static_cast<double>(elapsed_time) / 1000000.0;
+}
+
+SyncTimerQuery::SyncTimerQuery() { timer_.Begin(); }
+
+double SyncTimerQuery::FlushAndGetTimeInMS() {
+  if (timer_.query_ == 0) {
+    ALOGE("Error: Only call FlushAndGetTimeInMS() once.");
+    return 0.0;
+  }
+  timer_.End();
+  glFlush();
+  GLint done = 0;
+  while (!done) {
+    glGetQueryObjectivEXT(timer_.query_, GL_QUERY_RESULT_AVAILABLE, &done);
+  }
+
+  GLint disjoint_occurred = 0;
+  glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_occurred);
+  if (disjoint_occurred) {
+    ALOGE("Disjoint occurred.");
+    timer_.Delete();
+    return 0.0;
+  }
+
+  double elapsed_time = timer_.GetTimeInMS();
+  timer_.Delete();
+  return elapsed_time;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/vr_gl_extensions.cpp b/libs/vr/libdvrgraphics/vr_gl_extensions.cpp
new file mode 100644
index 0000000..2c5a698
--- /dev/null
+++ b/libs/vr/libdvrgraphics/vr_gl_extensions.cpp
@@ -0,0 +1,44 @@
+#include "include/private/dvr/graphics/vr_gl_extensions.h"
+
+PFNGLGETQUERYOBJECTI64VEXTPROC glGetQueryObjecti64v = NULL;
+PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectiv = NULL;
+PFNGLQUERYCOUNTEREXTPROC glQueryCounter = NULL;
+PFNGLBUFFERSTORAGEEXTPROC glBufferStorage = NULL;
+PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR glFramebufferTextureMultiview = NULL;
+PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR
+glFramebufferTextureMultisampleMultiview = NULL;
+
+PFNGLSHAREDBUFFERCREATEQCOM glCreateSharedBufferQCOM = NULL;
+PFNGLSHAREDBUFFERDESTROYQCOM glDestroySharedBufferQCOM = NULL;
+PFNGLSHAREDBUFFERBINDQCOM glBindSharedBufferQCOM = NULL;
+PFNGLGRALLOCBUFFERDATAQCOM glGrallocBufferDataQCOM = NULL;
+
+extern "C" void load_gl_extensions() {
+  if (glGetQueryObjecti64v) {
+    return;
+  }
+  glGetQueryObjecti64v = reinterpret_cast<PFNGLGETQUERYOBJECTI64VEXTPROC>(
+      eglGetProcAddress("glGetQueryObjecti64vEXT"));
+  glGetQueryObjectiv = reinterpret_cast<PFNGLGETQUERYOBJECTIVEXTPROC>(
+      eglGetProcAddress("glGetQueryObjectivEXT"));
+  glQueryCounter = reinterpret_cast<PFNGLQUERYCOUNTEREXTPROC>(
+      eglGetProcAddress("glQueryCounterEXT"));
+  glBufferStorage = reinterpret_cast<PFNGLBUFFERSTORAGEEXTPROC>(
+      eglGetProcAddress("glBufferStorageEXT"));
+
+  glFramebufferTextureMultiview =
+      reinterpret_cast<PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR>(
+          eglGetProcAddress("glFramebufferTextureMultiviewOVR"));
+  glFramebufferTextureMultisampleMultiview =
+      reinterpret_cast<PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR>(
+          eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"));
+
+  glGrallocBufferDataQCOM = reinterpret_cast<PFNGLGRALLOCBUFFERDATAQCOM>(
+      eglGetProcAddress("glGrallocBufferDataQCOM"));
+  glCreateSharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERCREATEQCOM>(
+      eglGetProcAddress("glCreateSharedBufferQCOM"));
+  glBindSharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERBINDQCOM>(
+      eglGetProcAddress("glBindSharedBufferQCOM"));
+  glDestroySharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERDESTROYQCOM>(
+      eglGetProcAddress("glDestroySharedBufferQCOM"));
+}
diff --git a/libs/vr/libeds/Android.bp b/libs/vr/libeds/Android.bp
new file mode 100644
index 0000000..a149853
--- /dev/null
+++ b/libs/vr/libeds/Android.bp
@@ -0,0 +1,85 @@
+// 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 = [
+    "eds.cpp",
+    "eds_mesh.cpp",
+    "composite_hmd.cpp",
+    "display_metrics.cpp",
+    "distortion_renderer.cpp",
+    "device_metrics.cpp",
+    "polynomial_radial_distortion.cpp",
+]
+
+localIncludeFiles = [
+    "include",
+]
+
+sharedLibraries = [
+    "libbase",
+    "libcutils",
+    "liblog",
+    "libEGL",
+    "libGLESv1_CM",
+    "libGLESv2",
+    "libui",
+    "libutils",
+    "libvulkan",
+]
+
+staticLibraries = [
+    "libdisplay",
+    "libdvrcommon",
+    "libdvrgraphics",
+    "libvrsensor",
+    "libpdx_default_transport",
+]
+
+cc_library_static {
+    srcs: sourceFiles,
+    cflags: [
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+        "-Wno-unused-parameter"],
+    // Enable debug options below to show GL errors and use gdb.
+    // + ["-UNDEBUG", "-DDEBUG", "-O0", "-g", ]
+    export_include_dirs: localIncludeFiles,
+    shared_libs: sharedLibraries,
+    static_libs: staticLibraries,
+    name: "libeds",
+}
+
+testFiles = ["tests/eds_app_tests.cpp"]
+
+cc_test {
+    name: "eds_app_tests",
+    tags: ["optional"],
+
+    srcs: testFiles,
+
+    shared_libs: [
+        "libhardware",
+        "libsync",
+    ] + sharedLibraries,
+
+    static_libs: [
+        "libgmock_main",
+        "libgmock",
+        "libeds",
+    ] + staticLibraries + [
+        "libbufferhub",
+        "libbufferhubqueue",
+    ],
+
+}
diff --git a/libs/vr/libeds/composite_hmd.cpp b/libs/vr/libeds/composite_hmd.cpp
new file mode 100644
index 0000000..d6bf164
--- /dev/null
+++ b/libs/vr/libeds/composite_hmd.cpp
@@ -0,0 +1,257 @@
+#include "include/private/dvr/composite_hmd.h"
+
+#include <log/log.h>
+
+#include <private/dvr/numeric.h>
+
+namespace android {
+namespace dvr {
+
+CompositeHmd::CompositeHmd(const HeadMountMetrics& head_mount_metrics,
+                           const DisplayMetrics& display_metrics)
+    : head_mount_metrics_(head_mount_metrics),
+      display_metrics_(display_metrics) {
+  MetricsChanged();
+}
+
+float CompositeHmd::GetTargetFrameDuration() const {
+  return display_metrics_.GetFrameDurationSeconds();
+}
+
+vec2 CompositeHmd::ComputeDistortedPoint(EyeType eye, vec2 position,
+                                         RgbColorChannel channel) const {
+  position = TransformPoint(eye_tan_angle_from_norm_screen_matrix_[eye], position);
+  vec2 distorted =
+      head_mount_metrics_.GetColorChannelDistortion(channel).Distort(position);
+  return TransformPoint(eye_norm_texture_from_tan_angle_matrix_[eye], distorted);
+}
+
+vec2 CompositeHmd::ComputeInverseDistortedPoint(EyeType eye, vec2 position,
+                                                RgbColorChannel channel) const {
+  position = TransformPoint(eye_norm_texture_from_tan_angle_inv_matrix_[eye], position);
+  vec2 distorted =
+      head_mount_metrics_.GetColorChannelDistortion(channel).DistortInverse(
+          position);
+  return TransformPoint(eye_tan_angle_from_norm_screen_inv_matrix_[eye], distorted);
+}
+
+void CompositeHmd::ComputeDistortedVertex(EyeType eye, vec2 uv_in,
+                                          vec2* vertex_out,
+                                          vec2* uv_out) const {
+  // The mesh vertices holds the shape of the distortion.
+  vec2 vertex_position = ComputeInverseDistortedPoint(eye, uv_in, kRed);
+  *vertex_out = vec2(vertex_position.x() - 0.5f, vertex_position.y() - 0.5f);
+
+  if (uv_out) {
+    // Compute the texture coordinate for each vertex coordinate.
+    // Red's is the inverse of the inverse, skip the calculation and use uv_in.
+    uv_out[kRed] = uv_in;
+    uv_out[kGreen] = ComputeDistortedPoint(eye, vertex_position, kGreen);
+    uv_out[kBlue] = ComputeDistortedPoint(eye, vertex_position, kBlue);
+  }
+}
+
+vec2i CompositeHmd::GetRecommendedRenderTargetSize() const {
+  return recommended_render_target_size_;
+}
+
+Range2i CompositeHmd::GetDisplayRange() const { return display_range_; }
+
+mat4 CompositeHmd::GetEyeFromHeadMatrix(EyeType eye) const {
+  return eye_from_head_matrix_[eye];
+}
+
+FieldOfView CompositeHmd::GetEyeFov(EyeType eye) const { return eye_fov_[eye]; }
+
+Range2i CompositeHmd::GetEyeViewportBounds(EyeType eye) const {
+  return eye_viewport_range_[eye];
+}
+
+void CompositeHmd::SetHeadMountMetrics(
+    const HeadMountMetrics& head_mount_metrics) {
+  // Use the assignement operator to do memberwise copy.
+  head_mount_metrics_ = head_mount_metrics;
+  MetricsChanged();
+}
+
+const HeadMountMetrics& CompositeHmd::GetHeadMountMetrics() const {
+  return head_mount_metrics_;
+}
+
+void CompositeHmd::SetDisplayMetrics(const DisplayMetrics& display_metrics) {
+  // Use the assignment operator to do memberwise copy.
+  display_metrics_ = display_metrics;
+  MetricsChanged();
+}
+
+const DisplayMetrics& CompositeHmd::GetDisplayMetrics() const {
+  return display_metrics_;
+}
+
+void CompositeHmd::MetricsChanged() {
+  // Abbreviations in variable names:
+  //   "vp": viewport
+  //   "ta": tan-angle
+  const HeadMountMetrics& mount = head_mount_metrics_;
+  DisplayMetrics display = display_metrics_;
+
+  if (display.IsPortrait()) {
+    // If we're in portrait mode, toggle the orientation so that all
+    // calculations are done in landscape mode.
+    display.ToggleOrientation();
+  }
+
+  float display_width_meters = display.GetSizeMeters()[0];
+  float display_height_meters = display.GetSizeMeters()[1];
+
+  vec2 pixels_per_meter = vec2(1.0f / display.GetMetersPerPixel()[0],
+                               1.0f / display.GetMetersPerPixel()[1]);
+
+  // virtual_eye_to_screen_dist is the distance from the screen to the eye
+  // after it has been projected through the lens.  This would normally be
+  // slightly different from the distance to the actual eye.
+  float virtual_eye_to_screen_dist = mount.GetVirtualEyeToScreenDistance();
+  float meters_per_tan_angle = virtual_eye_to_screen_dist;
+  vec2 pixels_per_tan_angle = pixels_per_meter * meters_per_tan_angle;
+
+  LOG_ALWAYS_FATAL_IF(0.0f == display_width_meters);
+  LOG_ALWAYS_FATAL_IF(0.0f == display_height_meters);
+  LOG_ALWAYS_FATAL_IF(0.0f == virtual_eye_to_screen_dist);
+
+  // Height of lenses from the bottom of the screen.
+  float lens_y_center = 0;
+  float bottom_dist = 0;
+  float top_dist = 0;
+
+  // bottom_display_dist and top_display_dist represent the distance from the
+  // lens center to the edge of the display.
+  float bottom_display_dist = 0;
+  float top_display_dist = 0;
+  switch (mount.GetVerticalAlignment()) {
+    case HeadMountMetrics::kBottom:
+      lens_y_center =
+          mount.GetTrayToLensDistance() - display.GetBorderSizeMeters();
+      bottom_dist = lens_y_center;
+      top_dist = lens_y_center;
+      bottom_display_dist = lens_y_center;
+      top_display_dist = display_height_meters - lens_y_center;
+      break;
+    case HeadMountMetrics::kCenter:
+      // TODO(hendrikw): This should respect the border size, but since we
+      //                 currently hard code the border size, it would break
+      //                 the distortion on some devices.  Revisit when border
+      //                 size is fixed.
+      lens_y_center = display_height_meters * 0.5f;
+      bottom_dist = lens_y_center;
+      top_dist = lens_y_center;
+      bottom_display_dist = lens_y_center;
+      top_display_dist = lens_y_center;
+      break;
+    case HeadMountMetrics::kTop:
+      lens_y_center = display_height_meters - (mount.GetTrayToLensDistance() -
+                                               display.GetBorderSizeMeters());
+      bottom_dist =
+          mount.GetTrayToLensDistance() - display.GetBorderSizeMeters();
+      top_dist = bottom_dist;
+      bottom_display_dist = lens_y_center;
+      top_display_dist = display_height_meters - lens_y_center;
+      break;
+  }
+
+  float inner_dist = mount.GetScreenCenterToLensDistance();
+  float outer_dist = display_width_meters * 0.5f - inner_dist;
+
+  // We don't take chromatic aberration into account yet for computing FOV,
+  // viewport, etc, so we only use the green channel for now. Note the actual
+  // Distort function *does* implement chromatic aberration.
+  const ColorChannelDistortion& distortion =
+      mount.GetColorChannelDistortion(kGreen);
+
+  vec2 outer_point(outer_dist / virtual_eye_to_screen_dist, 0.0f);
+  vec2 inner_point(inner_dist / virtual_eye_to_screen_dist, 0.0f);
+  vec2 bottom_point(0.0f, bottom_dist / virtual_eye_to_screen_dist);
+  vec2 top_point(0.0f, top_dist / virtual_eye_to_screen_dist);
+
+  float outer_angle = atanf(distortion.Distort(outer_point)[0]);
+  float inner_angle = atanf(distortion.Distort(inner_point)[0]);
+  float bottom_angle = atanf(distortion.Distort(bottom_point)[1]);
+  float top_angle = atanf(distortion.Distort(top_point)[1]);
+
+  for (EyeType eye : {kLeftEye, kRightEye}) {
+    const FieldOfView max_fov = mount.GetEyeMaxFov(eye);
+    float left_angle = (eye == kLeftEye) ? outer_angle : inner_angle;
+    float right_angle = (eye == kLeftEye) ? inner_angle : outer_angle;
+
+    eye_fov_[eye] = FieldOfView(std::min(left_angle, max_fov.GetLeft()),
+                                std::min(right_angle, max_fov.GetRight()),
+                                std::min(bottom_angle, max_fov.GetBottom()),
+                                std::min(top_angle, max_fov.GetTop()));
+
+    vec2 texture_vp_ta_p1 =
+        vec2(-tanf(eye_fov_[eye].GetLeft()), -tanf(eye_fov_[eye].GetBottom()));
+    vec2 texture_vp_ta_p2 =
+        vec2(tanf(eye_fov_[eye].GetRight()), tanf(eye_fov_[eye].GetTop()));
+    vec2 texture_vp_size_ta = texture_vp_ta_p2 - texture_vp_ta_p1;
+
+    vec2 texture_vp_sizef_pixels =
+        texture_vp_size_ta.array() * pixels_per_tan_angle.array();
+
+    vec2i texture_vp_size_pixels =
+        vec2i(static_cast<int32_t>(roundf(texture_vp_sizef_pixels[0])),
+              static_cast<int32_t>(roundf(texture_vp_sizef_pixels[1])));
+    int vp_start_x =
+        (eye == kLeftEye) ? 0 : eye_viewport_range_[kLeftEye].p2[0];
+
+    eye_viewport_range_[eye] =
+        Range2i::FromSize(vec2i(vp_start_x, 0), texture_vp_size_pixels);
+    float left_dist = (eye == kLeftEye) ? outer_dist : inner_dist;
+    float right_dist = (eye == kLeftEye) ? inner_dist : outer_dist;
+    vec2 screen_ta_p1(-left_dist / virtual_eye_to_screen_dist,
+                      -bottom_display_dist / virtual_eye_to_screen_dist);
+    vec2 screen_ta_p2(right_dist / virtual_eye_to_screen_dist,
+                      top_display_dist / virtual_eye_to_screen_dist);
+    vec2 screen_ta_size = screen_ta_p2 - screen_ta_p1;
+
+    // Align the tan angle coordinates to the nearest pixel.  This will ensure
+    // that the optical center doesn't straddle multiple pixels.
+    // TODO(hendrikw): verify that this works correctly for Daydream View.
+    vec2 tan_angle_per_pixel(screen_ta_size.array() /
+                             texture_vp_size_pixels.cast<float>().array());
+    vec2 pixel_p1(screen_ta_p1.array() / tan_angle_per_pixel.array());
+    vec2 pixel_shift(roundf(pixel_p1.x()) - pixel_p1.x(),
+                     roundf(pixel_p1.y()) - pixel_p1.y());
+    screen_ta_p1 +=
+        (tan_angle_per_pixel.array() * pixel_shift.array()).matrix();
+    screen_ta_p2 +=
+        (tan_angle_per_pixel.array() * pixel_shift.array()).matrix();
+
+    // Calculate the transformations needed for the distortions.
+    eye_tan_angle_from_norm_screen_matrix_[eye] =
+        TranslationMatrix(vec2(screen_ta_p1)) *
+        ScaleMatrix(screen_ta_size);
+    eye_tan_angle_from_norm_screen_inv_matrix_[eye] =
+        eye_tan_angle_from_norm_screen_matrix_[eye].inverse();
+
+    eye_norm_texture_from_tan_angle_inv_matrix_[eye] =
+        TranslationMatrix(texture_vp_ta_p1) *
+        ScaleMatrix(texture_vp_size_ta);
+    eye_norm_texture_from_tan_angle_matrix_[eye] =
+        eye_norm_texture_from_tan_angle_inv_matrix_[eye].inverse();
+  }
+  vec2i left_vp_size = eye_viewport_range_[kLeftEye].GetSize();
+  vec2i right_vp_size = eye_viewport_range_[kRightEye].GetSize();
+
+  recommended_render_target_size_ =
+      vec2i(left_vp_size[0] + right_vp_size[0],
+            std::max(left_vp_size[1], right_vp_size[1]));
+
+  display_range_ = Range2i::FromSize(vec2i(0, 0), display.GetSizePixels());
+
+  eye_from_head_matrix_[kLeftEye] = Eigen::Translation3f(
+      vec3(mount.GetScreenCenterToLensDistance(), 0.0f, 0.0f));
+  eye_from_head_matrix_[kRightEye] = Eigen::Translation3f(
+      vec3(-mount.GetScreenCenterToLensDistance(), 0.0f, 0.0f));
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/device_metrics.cpp b/libs/vr/libeds/device_metrics.cpp
new file mode 100644
index 0000000..68ee186
--- /dev/null
+++ b/libs/vr/libeds/device_metrics.cpp
@@ -0,0 +1,172 @@
+#include <private/dvr/device_metrics.h>
+
+#include <cutils/properties.h>
+#include <private/dvr/head_mount_metrics.h>
+#include <private/dvr/identity_distortion.h>
+#include <private/dvr/polynomial_radial_distortion.h>
+#include <private/dvr/types.h>
+#include "include/private/dvr/display_metrics.h"
+
+namespace {
+
+static constexpr char kRPolynomial[] = "persist.dvr.r_poly";
+static constexpr char kGPolynomial[] = "persist.dvr.g_poly";
+static constexpr char kBPolynomial[] = "persist.dvr.b_poly";
+static constexpr char kLensDistance[] = "persist.dvr.lens_distance";
+static constexpr char kDisplayGap[] = "persist.dvr.display_gap";
+static constexpr char kVEyeToDisplay[] = "persist.dvr.v_eye_to_display";
+static constexpr char kFovIOBT[] = "persist.dvr.fov_iobt";
+static constexpr char kScreenSize[] = "persist.dvr.screen_size";
+
+bool StringToFloat(const char* str, float* result) {
+  char* endptr = nullptr;
+  *result = std::strtof(str, &endptr);
+  return !(str == endptr || !endptr);
+}
+
+std::vector<std::string> SplitString(const std::string& string_to_split,
+                                     char deliminator) {
+  std::vector<std::string> result;
+  std::string sub_string;
+  std::stringstream ss(string_to_split);
+  while (std::getline(ss, sub_string, deliminator))
+    result.push_back(sub_string);
+  return result;
+}
+
+std::vector<float> GetProperty(const char* name,
+                               const std::vector<float>& default_values) {
+  char prop[PROPERTY_VALUE_MAX + 1] = {};
+  property_get(name, prop, "");
+  std::vector<std::string> values = SplitString(prop, ',');
+  std::vector<float> results;
+  for (const auto& value : values) {
+    float result = 0.0f;
+    if (StringToFloat(value.c_str(), &result)) {
+      results.push_back(static_cast<float>(result));
+    }
+  }
+  if (results.empty()) {
+    return default_values;
+  }
+  return results;
+}
+
+float GetProperty(const char* name, float default_value) {
+  char prop[PROPERTY_VALUE_MAX + 1] = {};
+  property_get(name, prop, "");
+  float result = 0.0f;
+  if (StringToFloat(prop, &result)) {
+    return static_cast<float>(result);
+  }
+  return default_value;
+}
+
+float GetInterLensDistance() { return GetProperty(kLensDistance, 0.064f); }
+
+float GetDisplayGap() { return GetProperty(kDisplayGap, 0.0f); }
+
+float GetTrayToLensDistance() { return 0.035f; }
+
+float GetVEyeToDisplay() { return GetProperty(kVEyeToDisplay, 0.042f); }
+
+android::dvr::vec2 GetDisplaySize() {
+  static const std::vector<float> default_size = {0.0742177f, 0.131943f};
+  std::vector<float> sizes = GetProperty(kScreenSize, default_size);
+  if (sizes.size() != 0)
+    sizes = default_size;
+  return android::dvr::vec2(sizes[0], sizes[1]);
+}
+
+std::vector<float> GetMaxFOVs() {
+  static const std::vector<float> defaults = {43.7f, 47.8f, 54.2f, 54.2f};
+  std::vector<float> fovs = GetProperty(kFovIOBT, defaults);
+  if (fovs.size() != 4)
+    fovs = defaults;
+  for (auto& value : fovs) {
+    value = value * M_PI / 180.0f;
+  }
+  return fovs;
+}
+
+static const android::dvr::HeadMountMetrics::VerticalAlignment
+    kDefaultVerticalAlignment = android::dvr::HeadMountMetrics::kCenter;
+
+// Default border size in meters.
+static const float kScreenBorderSize = 0.004f;
+
+// Refresh rate.
+static const float kScreenRefreshRate = 60.0f;
+
+// Default display orientation is portrait.
+static const android::dvr::DisplayOrientation kDisplayOrientation =
+    android::dvr::DisplayOrientation::kPortrait;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+HeadMountMetrics CreateHeadMountMetrics(const FieldOfView& l_fov,
+                                        const FieldOfView& r_fov) {
+  static const std::vector<float> default_r = {
+      0.00103f, 2.63917f, -7.14427f, 8.98036f, -4.10586f, 0.83705f, 0.00130f};
+  static const std::vector<float> default_g = {
+      0.08944f, 2.26005f, -6.30924f, 7.94561f, -3.22788f, 0.45577f, 0.07300f};
+  static const std::vector<float> default_b = {
+      0.16364f, 1.94083f, -5.55033f, 6.89578f, -2.19053f, -0.04050f, 0.17380f};
+  std::vector<float> poly_r = GetProperty(kRPolynomial, default_r);
+  std::vector<float> poly_g = GetProperty(kGPolynomial, default_g);
+  std::vector<float> poly_b = GetProperty(kBPolynomial, default_b);
+
+  std::shared_ptr<ColorChannelDistortion> distortion_r(
+      new PolynomialRadialDistortion(poly_r));
+  std::shared_ptr<ColorChannelDistortion> distortion_g(
+      new PolynomialRadialDistortion(poly_g));
+  std::shared_ptr<ColorChannelDistortion> distortion_b(
+      new PolynomialRadialDistortion(poly_b));
+
+  return HeadMountMetrics(GetInterLensDistance(), GetTrayToLensDistance(),
+                          GetVEyeToDisplay(), kDefaultVerticalAlignment, l_fov,
+                          r_fov, distortion_r, distortion_g, distortion_b,
+                          HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+                          HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+                          (GetInterLensDistance() - GetDisplayGap()) / 2.0f);
+}
+
+HeadMountMetrics CreateHeadMountMetrics() {
+  std::vector<float> fovs = GetMaxFOVs();
+  FieldOfView l_fov(fovs[1], fovs[0], fovs[2], fovs[3]);
+  FieldOfView r_fov(fovs[0], fovs[1], fovs[2], fovs[3]);
+  return CreateHeadMountMetrics(l_fov, r_fov);
+}
+
+DisplayMetrics CreateDisplayMetrics(vec2i screen_size) {
+  android::dvr::vec2 size_in_meters = GetDisplaySize();
+  vec2 meters_per_pixel(size_in_meters[0] / static_cast<float>(screen_size[0]),
+                        size_in_meters[1] / static_cast<float>(screen_size[1]));
+  return DisplayMetrics(screen_size, meters_per_pixel, kScreenBorderSize,
+                        1000.0f / kScreenRefreshRate, kDisplayOrientation);
+}
+
+HeadMountMetrics CreateUndistortedHeadMountMetrics() {
+  std::vector<float> fovs = GetMaxFOVs();
+  FieldOfView l_fov(fovs[1], fovs[0], fovs[2], fovs[3]);
+  FieldOfView r_fov(fovs[0], fovs[1], fovs[2], fovs[3]);
+  return CreateUndistortedHeadMountMetrics(l_fov, r_fov);
+}
+
+HeadMountMetrics CreateUndistortedHeadMountMetrics(const FieldOfView& l_fov,
+                                                   const FieldOfView& r_fov) {
+  auto distortion_all = std::make_shared<IdentityDistortion>();
+
+  return HeadMountMetrics(GetInterLensDistance(), GetVEyeToDisplay(),
+                          GetVEyeToDisplay(), kDefaultVerticalAlignment, l_fov,
+                          r_fov, distortion_all, distortion_all, distortion_all,
+                          HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+                          HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+                          (GetInterLensDistance() - GetDisplayGap()) / 2.0f);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/display_metrics.cpp b/libs/vr/libeds/display_metrics.cpp
new file mode 100644
index 0000000..e129395
--- /dev/null
+++ b/libs/vr/libeds/display_metrics.cpp
@@ -0,0 +1,30 @@
+#include "include/private/dvr/display_metrics.h"
+
+namespace android {
+namespace dvr {
+
+DisplayMetrics::DisplayMetrics(vec2i size_pixels, vec2 meters_per_pixel,
+                               float border_size_meters,
+                               float frame_duration_seconds,
+                               DisplayOrientation orientation)
+    : size_pixels_(size_pixels),
+      meters_per_pixel_(meters_per_pixel),
+      border_size_meters_(border_size_meters),
+      frame_duration_seconds_(frame_duration_seconds),
+      orientation_(orientation) {}
+
+void DisplayMetrics::ToggleOrientation() {
+  std::swap(size_pixels_[0], size_pixels_[1]);
+  std::swap(meters_per_pixel_[0], meters_per_pixel_[1]);
+  if (orientation_ == DisplayOrientation::kPortrait)
+    orientation_ = DisplayOrientation::kLandscape;
+  else
+    orientation_ = DisplayOrientation::kPortrait;
+}
+
+DisplayMetrics::DisplayMetrics()
+    : DisplayMetrics(vec2i(0, 0), vec2(0.0f, 0.0f), 0.0f, 0.0f,
+                     DisplayOrientation::kLandscape) {}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/distortion_renderer.cpp b/libs/vr/libeds/distortion_renderer.cpp
new file mode 100644
index 0000000..13090ca
--- /dev/null
+++ b/libs/vr/libeds/distortion_renderer.cpp
@@ -0,0 +1,792 @@
+#include "include/private/dvr/distortion_renderer.h"
+
+#include <float.h>
+
+#include <string>
+
+#include <utils/Log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <log/log.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/ortho.h>
+#include <private/dvr/sensor_constants.h>
+
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+
+#define POSITION_ATTR 0
+#define VIEWPORT_COORD_R_ATTR 1
+#define VIEWPORT_COORD_G_ATTR 2
+#define VIEWPORT_COORD_B_ATTR 3
+
+// Pose data uniform buffer bindings. Must be sequential.
+#define POSE_BINDING 0
+#define POSE_BINDING2 1
+
+// Texture unit bindings. Must be sequential.
+// Things break if we start at binding 0 (samples come back black).
+#define SAMPLER_BINDING 1
+#define SAMPLER_BINDING2 2
+
+#define GLSL_VIGNETTE_FUNC                                       \
+  "float vignette(vec2 texCoords) {\n"                           \
+  "  const float fadeDist = 0.01;\n"                             \
+  "  const float fadeDistInv = 1.0 / fadeDist;\n"                \
+  "  const float inset = 0.02;\n"                                \
+  "  vec2 lowEdge = vec2(inset - fadeDist);\n"                   \
+  "  vec2 highEdge = vec2(1.0 - inset + fadeDist);\n"            \
+  "  vec2 vignetteMin = "                                        \
+  "    clamp(-fadeDistInv * (lowEdge - texCoords), 0.0, 1.0);\n" \
+  "  vec2 vignetteMax = "                                        \
+  "    clamp(fadeDistInv * (highEdge - texCoords), 0.0, 1.0);\n" \
+  "  vec2 vignette = vignetteMin * vignetteMax;\n"               \
+  "  return vignette.x * vignette.y;\n"                          \
+  "}\n"
+
+namespace {
+
+// If enabled, the pixel shader will blend by reading back the current pixel
+// from the framebuffer.
+// TODO(jbates) With framebuffer read coherency disabled, this seems to perform
+//   well enough. That requires a GL extension, so for now we disable this path.
+constexpr bool kUseFramebufferReadback = false;
+
+static const char* kVertexShaderChromaticAberrationString =
+    "uniform mat4 uProjectionMatrix;\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+    "uniform LateLatchData {\n"
+    "  mat4 uTexFromRecommendedViewportMatrix;\n"
+    "};\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING2) ", std140)\n"
+    "uniform LateLatchData2 {\n"
+    "  mat4 uTexFromRecommendedViewportMatrix2;\n"
+    "};\n"
+    "#endif\n"
+    "uniform vec4 uTexXMinMax;\n"
+    "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_R_ATTR)
+           ") in vec2 aViewportCoordsR;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+           ") in vec2 aViewportCoordsG;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_B_ATTR)
+           ") in vec2 aViewportCoordsB;\n"
+    "mediump out vec4 vTexCoordsRG;\n"
+    "mediump out vec2 vTexCoordsB;\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "mediump out vec4 vTexCoordsRG2;\n"
+    "mediump out vec2 vTexCoordsB2;\n"
+    "#endif\n"
+    "mediump out vec3 vVignette;\n"
+    "\n" GLSL_VIGNETTE_FUNC
+    "void main(void) {\n"
+    "  vVignette.r = vignette(aViewportCoordsR);\n"
+    "  vVignette.g = vignette(aViewportCoordsG);\n"
+    "  vVignette.b = vignette(aViewportCoordsB);\n"
+    "  vec4 redTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+    "                       vec4(aViewportCoordsR, 0., 1.));\n"
+    "  vec4 greenTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+    "                         vec4(aViewportCoordsG, 0., 1.));\n"
+    "  vec4 blueTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+    "                        vec4(aViewportCoordsB, 0., 1.));\n"
+    "  vTexCoordsRG.xy = redTexCoords.xy / redTexCoords.w;\n"
+    "  vTexCoordsRG.zw = greenTexCoords.xy / greenTexCoords.w;\n"
+    "  vTexCoordsB = blueTexCoords.xy / blueTexCoords.w;\n"
+    "  vTexCoordsRG.x = clamp(vTexCoordsRG.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+    "  vTexCoordsRG.z = clamp(vTexCoordsRG.z, uTexXMinMax.x, uTexXMinMax.y);\n"
+    "  vTexCoordsB.x = clamp(vTexCoordsB.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "  redTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+    "                  vec4(aViewportCoordsR, 0., 1.));\n"
+    "  greenTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+    "                    vec4(aViewportCoordsG, 0., 1.));\n"
+    "  blueTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+    "                   vec4(aViewportCoordsB, 0., 1.));\n"
+    "  vTexCoordsRG2.xy = redTexCoords.xy / redTexCoords.w;\n"
+    "  vTexCoordsRG2.zw = greenTexCoords.xy / greenTexCoords.w;\n"
+    "  vTexCoordsB2 = blueTexCoords.xy / blueTexCoords.w;\n"
+    "  vTexCoordsRG2.x = clamp(vTexCoordsRG2.x,\n"
+    "                          uTexXMinMax.z, uTexXMinMax.w);\n"
+    "  vTexCoordsRG2.z = clamp(vTexCoordsRG2.z, uTexXMinMax.z,\n"
+    "                          uTexXMinMax.w);\n"
+    "  vTexCoordsB2.x = clamp(vTexCoordsB2.x, uTexXMinMax.z, uTexXMinMax.w);\n"
+    "#endif\n"
+    "  gl_Position = uProjectionMatrix * vec4(aPosition, 0., 1.);\n"
+    "}\n";
+
+static const char* kFragmentShaderChromaticAberrationString =
+    "#ifdef GL_ES\n"
+    "precision mediump float;\n"
+    "#endif\n"
+    " \n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+    "uniform sampler2D uDistortionTexture; \n"
+    "mediump in vec4 vTexCoordsRG;\n"
+    "mediump in vec2 vTexCoordsB;\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING2) ")\n"
+    "uniform sampler2D uDistortionTexture2; \n"
+    "mediump in vec4 vTexCoordsRG2;\n"
+    "mediump in vec2 vTexCoordsB2;\n"
+    "#endif\n"
+    "mediump in vec3 vVignette;\n"
+    "#ifdef BLEND_WITH_PREVIOUS_LAYER \n"
+    "inout vec4 fragColor; \n"
+    "#else \n"
+    "out vec4 fragColor; \n"
+    "#endif \n"
+    " \n"
+    "void main(void) { \n"
+    "  vec4 ra = texture(uDistortionTexture, vTexCoordsRG.xy); \n"
+    "  vec4 ga = texture(uDistortionTexture, vTexCoordsRG.zw); \n"
+    "  vec4 ba = texture(uDistortionTexture, vTexCoordsB); \n"
+    "#ifdef BLEND_WITH_PREVIOUS_LAYER \n"
+    "  vec3 alpha1 = vec3(ra.a, ga.a, ba.a); \n"
+    "  vec3 color = (vec3(1.0) - alpha1) * fragColor.rgb + \n"
+    "               alpha1 * vec3(ra.r, ga.g, ba.b); \n"
+    "#else // BLEND_WITH_PREVIOUS_LAYER \n"
+    "  vec3 color = vec3(ra.r, ga.g, ba.b); \n"
+    "#endif // BLEND_WITH_PREVIOUS_LAYER \n"
+    "#ifdef COMPOSITE_LAYER_2 \n"
+    "  // Alpha blend layer 2 onto layer 1. \n"
+    "  vec4 ra2 = texture(uDistortionTexture2, vTexCoordsRG2.xy); \n"
+    "  vec4 ga2 = texture(uDistortionTexture2, vTexCoordsRG2.zw); \n"
+    "  vec4 ba2 = texture(uDistortionTexture2, vTexCoordsB2); \n"
+    "  vec3 color2 = vec3(ra2.r, ga2.g, ba2.b); \n"
+    "  vec3 alpha2 = vec3(ra2.a, ga2.a, ba2.a); \n"
+    "  color = (vec3(1.0) - alpha2) * color + alpha2 * color2; \n"
+    "#endif \n"
+    "#ifdef ALPHA_VIGNETTE\n"
+    "  fragColor = vec4(color, vVignette.b * ga.a); \n"
+    "#else // ALPHA_VIGNETTE\n"
+    "  fragColor = vec4(vVignette.rgb * color, ga.a); \n"
+    "#endif // ALPHA_VIGNETTE\n"
+    "} \n";
+
+static const char* kVertexShaderNoChromaticAberrationString =
+    "uniform mat4 uProjectionMatrix;\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+    "uniform LateLatchData {\n"
+    "  mat4 uTexFromRecommendedViewportMatrix;\n"
+    "};\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING2) ", std140)\n"
+    "uniform LateLatchData2 {\n"
+    "  mat4 uTexFromRecommendedViewportMatrix2;\n"
+    "};\n"
+    "#endif\n"
+    "uniform vec4 uTexXMinMax;\n"
+    "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+           ") in vec2 aViewportCoords;\n"
+    "mediump out vec2 vTexCoords;\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "mediump out vec2 vTexCoords2;\n"
+    "#endif\n"
+    "mediump out vec3 vVignette;\n"
+    "\n" GLSL_VIGNETTE_FUNC
+    "void main(void) {\n"
+    "  float fVignette = vignette(aViewportCoords);\n"
+    "  vVignette = vec3(fVignette, fVignette, fVignette);\n"
+    "  vec4 texCoords = (uTexFromRecommendedViewportMatrix * \n"
+    "                    vec4(aViewportCoords, 0., 1.));\n"
+    "  vTexCoords = texCoords.xy / texCoords.w;\n"
+    "  vTexCoords.x = clamp(vTexCoords.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "  texCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+    "               vec4(aViewportCoords, 0., 1.));\n"
+    "  vTexCoords2 = texCoords.xy / texCoords.w;\n"
+    "  vTexCoords2.x = clamp(vTexCoords2.x, uTexXMinMax.z, uTexXMinMax.w);\n"
+    "#endif\n"
+    "  gl_Position = uProjectionMatrix * vec4(aPosition, 0., 1.);\n"
+    "}\n";
+
+static const char* kFragmentShaderNoChromaticAberrationString =
+    "#ifdef GL_ES\n"
+    "precision mediump float;\n"
+    "#endif\n"
+    " \n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+    "uniform sampler2D uDistortionTexture; \n"
+    "mediump in vec2 vTexCoords;\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING2) ")\n"
+    "uniform sampler2D uDistortionTexture2; \n"
+    "mediump in vec2 vTexCoords2;\n"
+    "#endif\n"
+    "mediump in vec3 vVignette;\n"
+    "out vec4 fragColor;\n"
+    " \n"
+    "void main(void) { \n"
+    "  vec4 color = texture(uDistortionTexture, vTexCoords); \n"
+    "#ifdef COMPOSITE_LAYER_2 \n"
+    "  // Alpha blend layer 2 onto layer 1. \n"
+    "  vec4 color2 = texture(uDistortionTexture2, vTexCoords2); \n"
+    "  float alpha2 = color2.a; \n"
+    "  color.rgb = (1.0 - alpha2) * color.rgb + alpha2 * color2.rgb; \n"
+    "#endif \n"
+    "  fragColor = vec4(vVignette * color.rgb, color.a); \n"
+    "} \n";
+
+static const char* kVertexShaderSimpleVideoQuadString =
+    "uniform mat4 uProjectionMatrix;\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+    "uniform LateLatchData {\n"
+    "  mat4 uEdsCorrection;\n"
+    "};\n"
+    "uniform mat4 uTexFromEyeMatrix;\n"
+    "uniform mat4 uEyeFromViewportMatrix;\n"
+    "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+           ") in vec2 aViewportCoords;\n"
+    "mediump out vec2 vTexCoords;\n"
+    "void main(void) {\n"
+    "  mat4 m = uTexFromEyeMatrix * inverse(uEdsCorrection) * uEyeFromViewportMatrix;\n"
+    "  mat3 uTexFromViewportMatrix = inverse(mat3(m[0].xyw, m[1].xyw, m[3].xyw)); \n"
+    "  vec3 texCoords = uTexFromViewportMatrix * vec3(aViewportCoords, 1.0);\n"
+    "  vTexCoords = texCoords.xy / texCoords.z;\n"
+    "  gl_Position = uProjectionMatrix * vec4(aPosition, 0.0, 1.0);\n"
+    "}\n";
+
+static const char* kFragmentShaderSimpleVideoQuadString =
+    "#extension GL_OES_EGL_image_external_essl3 : enable\n"
+    " \n"
+    "#ifdef GL_ES\n"
+    "precision mediump float;\n"
+    "#endif\n"
+    " \n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+    "uniform samplerExternalOES uDistortionTexture; \n"
+    "mediump in vec2 vTexCoords;\n"
+    "out vec4 fragColor;\n"
+    " \n"
+    "void main(void) { \n"
+    "  if (clamp(vTexCoords, 0.0, 1.0) != vTexCoords) { \n"
+    "    fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
+    "  } else { \n"
+    "    fragColor = texture(uDistortionTexture, vTexCoords); \n"
+    "  } \n"
+    "} \n";
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// Note that converting from Clip Space ([-1,1]^3) to Viewport Space
+// for one eye ([0,1]x[0,1]) requires dividing by 2 in x and y.
+const mat4 DistortionRenderer::kViewportFromClipMatrix =
+    Eigen::Translation3f(vec3(0.5f, 0.5f, 0)) *
+    Eigen::DiagonalMatrix<float, 3>(vec3(0.5f, 0.5f, 1.0f));
+
+const mat4 DistortionRenderer::kClipFromViewportMatrix =
+    Eigen::DiagonalMatrix<float, 3>(vec3(2.0f, 2.0f, 1.0f)) *
+    Eigen::Translation3f(vec3(-0.5f, -0.5f, 0));
+
+void DistortionRenderer::EdsShader::load(const char* vertex,
+                                         const char* fragment, int num_layers,
+                                         bool use_alpha_vignette,
+                                         float rotation, bool flip_vertical,
+                                         bool blend_with_previous_layer) {
+  std::string vert_builder = "#version 310 es\n";
+  std::string frag_builder = "#version 310 es\n";
+  if (blend_with_previous_layer && kUseFramebufferReadback) {
+    frag_builder += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
+  }
+
+  if (num_layers == 2) {
+    vert_builder += "#define COMPOSITE_LAYER_2\n";
+    frag_builder += "#define COMPOSITE_LAYER_2\n";
+  } else {
+    LOG_ALWAYS_FATAL_IF(num_layers != 1);
+  }
+  if (blend_with_previous_layer) {
+    // Check for unsupported shader combinations:
+    LOG_ALWAYS_FATAL_IF(num_layers != 1);
+    LOG_ALWAYS_FATAL_IF(use_alpha_vignette);
+    if (kUseFramebufferReadback)
+      frag_builder += "#define BLEND_WITH_PREVIOUS_LAYER\n";
+  }
+  if (use_alpha_vignette) {
+    vert_builder += "#define ALPHA_VIGNETTE\n";
+    frag_builder += "#define ALPHA_VIGNETTE\n";
+  }
+
+  vert_builder += vertex;
+  frag_builder += fragment;
+  pgm.Link(vert_builder, frag_builder);
+  LOG_ALWAYS_FATAL_IF(!pgm.IsUsable());
+
+  pgm.Use();
+
+  uProjectionMatrix =
+      glGetUniformLocation(pgm.GetProgram(), "uProjectionMatrix");
+  uTexFromEyeMatrix =
+      glGetUniformLocation(pgm.GetProgram(), "uTexFromEyeMatrix");
+  uEyeFromViewportMatrix =
+      glGetUniformLocation(pgm.GetProgram(), "uEyeFromViewportMatrix");
+  uTexXMinMax = glGetUniformLocation(pgm.GetProgram(), "uTexXMinMax");
+  CHECK_GL();
+
+  float vertical_multiply = flip_vertical ? -1.0 : 1.0;
+  mat4 projectionMatrix = OrthoMatrix(-0.5f, 0.5f, vertical_multiply * -0.5f,
+                                      vertical_multiply * 0.5f, -1.0f, 1.0f);
+
+  // Rotate the mesh into the screen's orientation.
+  // TODO(hendrikw): Once the display is finalized, and perhaps not portrait,
+  //                 look into removing this matrix altogether.
+  projectionMatrix =
+      projectionMatrix * Eigen::AngleAxisf(rotation, vec3::UnitZ());
+
+  LOG_ALWAYS_FATAL_IF(sizeof(mat4) != 4 * 4 * 4);
+  glUniformMatrix4fv(uProjectionMatrix, 1, false, projectionMatrix.data());
+}
+
+DistortionRenderer::DistortionRenderer(
+    const CompositeHmd& hmd, vec2i display_size, int distortion_mesh_resolution,
+    bool flip_texture_horizontally, bool flip_texture_vertically,
+    bool separated_eye_buffers, bool eds_enabled, bool late_latch_enabled)
+    : shader_type_(kChromaticAberrationCorrection),
+      eds_enabled_(eds_enabled),
+      chromatic_aberration_correction_enabled_(true),
+      use_alpha_vignette_(false),
+      distortion_mesh_resolution_(distortion_mesh_resolution),
+      last_distortion_texture_id_(0),
+      app_texture_target_(GL_TEXTURE_2D),
+      display_size_(display_size),
+      separated_eye_buffers_(separated_eye_buffers) {
+  ATRACE_NAME("DistortionRenderer::DistortionRenderer");
+
+  float device_rotation = 0.0;
+
+  if (eds_enabled_) {
+    // Late latch must be on if eds_enabled_ is true.
+    if (!late_latch_enabled) {
+      ALOGE("Cannot enable EDS without late latch. Force enabling late latch.");
+      late_latch_enabled = true;
+    }
+  }
+
+  // TODO(hendrikw): Look into moving this logic into DisplayMetrics.
+  if (hmd.GetDisplayMetrics().IsPortrait()) {
+    device_rotation = -M_PI / 2.0f;
+  }
+
+  // Create shader programs.
+  shaders_[kNoChromaticAberrationCorrection].load(
+      kVertexShaderNoChromaticAberrationString,
+      kFragmentShaderNoChromaticAberrationString, 1, false, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kNoChromaticAberrationCorrectionTwoLayers].load(
+      kVertexShaderNoChromaticAberrationString,
+      kFragmentShaderNoChromaticAberrationString, 2, false, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrection].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 1, false, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrectionTwoLayers].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 2, false, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrectionAlphaVignette].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 1, true, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrectionAlphaVignetteTwoLayers].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 2, true, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrectionWithBlend].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 1, false, device_rotation,
+      flip_texture_horizontally, true);
+  shaders_[kSimpleVideoQuad].load(
+      kVertexShaderSimpleVideoQuadString,
+      kFragmentShaderSimpleVideoQuadString, 1, false, device_rotation,
+      flip_texture_horizontally, true);
+  CHECK_GL();
+
+  mat4 tex_from_recommended_viewport_matrix[2][2][2];
+  for (int eye = 0; eye < 2; ++eye) {
+    // Near and far plane don't actually matter for the clip_from_eye_matrix
+    // below since it is only used (for EDS) to transform coordinates for
+    // which the Z has been dropped.
+    static const float kNear = 0.1f, kFar = 100.0f;
+    const FieldOfView& fov =
+        (eye == kLeftEye ? hmd.GetEyeFov(kLeftEye) : hmd.GetEyeFov(kRightEye));
+    mat4 c_clip_from_eye_matrix = fov.GetProjectionMatrix(kNear, kFar);
+    mat4 c_eye_from_clip_matrix = c_clip_from_eye_matrix.inverse();
+
+    // Compute tex_from_recommended_viewport_matrix.
+
+    // flip_texture_vertically defines the default flip behavior.
+    // do_flip[0] should be the default, while do_flip[1] should be the
+    // inverse of the default.
+    int do_flip[2] = {flip_texture_vertically ? 1 : 0,
+                      flip_texture_vertically ? 0 : 1};
+    for (int flip = 0; flip < 2; ++flip) {
+      vec2 flip_scale(1.0f, do_flip[flip] ? -1.0f : 1.0f);
+      vec2 flip_offset(0.0f, do_flip[flip] ? 1.0f : 0.0f);
+
+      for (int separate_eye = 0; separate_eye < 2; ++separate_eye) {
+        vec2 viewport_corner_offset = (eye == kLeftEye || separate_eye)
+                                          ? vec2(0.0f, 0.0f)
+                                          : vec2(0.5f, 0.0f);
+        const vec2 txy = viewport_corner_offset + flip_offset;
+        const vec2 scalexy = vec2(separate_eye ? 1.0f : 0.5f, 1.0f);
+        tex_from_recommended_viewport_matrix[eye][flip][separate_eye] =
+            Eigen::Translation3f(vec3(txy.x(), txy.y(), 0.0f)) *
+            Eigen::DiagonalMatrix<float, 3>(vec3(flip_scale.x() * scalexy.x(),
+                                                 flip_scale.y(), scalexy.y()));
+
+        tex_from_eye_matrix_[eye][flip][separate_eye] =
+            tex_from_recommended_viewport_matrix[eye][flip][separate_eye] *
+            kViewportFromClipMatrix * c_clip_from_eye_matrix;
+      }
+    }
+
+    eye_from_viewport_matrix_[eye] =
+        c_eye_from_clip_matrix * kClipFromViewportMatrix;
+  }
+
+  // Create UBO for setting the EDS matrix to identity when EDS is disabled.
+  glGenBuffers(2 * 2 * 2, &uTexFromRecommendedViewportMatrix[0][0][0]);
+  for (int eye = 0; eye < 2; ++eye) {
+    for (int flip = 0; flip < 2; ++flip) {
+      for (int separate_eye = 0; separate_eye < 2; ++separate_eye) {
+        glBindBuffer(
+            GL_UNIFORM_BUFFER,
+            uTexFromRecommendedViewportMatrix[eye][flip][separate_eye]);
+        glBufferData(GL_UNIFORM_BUFFER, sizeof(mat4), 0, GL_STATIC_DRAW);
+        CHECK_GL();
+        mat4* mat = static_cast<mat4*>(glMapBufferRange(
+            GL_UNIFORM_BUFFER, 0, sizeof(mat4), GL_MAP_WRITE_BIT));
+        CHECK_GL();
+        *mat = tex_from_recommended_viewport_matrix[eye][flip][separate_eye];
+        glUnmapBuffer(GL_UNIFORM_BUFFER);
+      }
+    }
+  }
+  glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+  // Create distortion meshes and associated GL resources.
+  glGenBuffers(2, mesh_vbo_);
+  glGenVertexArrays(2, mesh_vao_);
+  glGenBuffers(2, mesh_ibo_);
+  RecomputeDistortion(hmd);
+
+  SetDisplaySize(display_size);
+
+  if (hmd.GetDisplayMetrics().IsPortrait()) {
+    eye_viewport_origin_[0] =
+        vec2i(0, flip_texture_horizontally ? 0 : display_size_[1] / 2);
+    eye_viewport_origin_[1] =
+        vec2i(0, flip_texture_horizontally ? display_size_[1] / 2 : 0);
+    eye_viewport_size_ = vec2i(display_size_[0], display_size_[1] / 2);
+  } else {
+    eye_viewport_origin_[0] = vec2i(0, 0);
+    eye_viewport_origin_[1] = vec2i(display_size_[0] / 2, 0);
+    eye_viewport_size_ = vec2i(display_size_[0] / 2, display_size_[1]);
+  }
+
+  CHECK_GL();
+}
+
+DistortionRenderer::~DistortionRenderer() {
+  glDeleteBuffers(2 * 2 * 2, &uTexFromRecommendedViewportMatrix[0][0][0]);
+  glDeleteBuffers(2, mesh_vbo_);
+  glDeleteVertexArrays(2, mesh_vao_);
+  glDeleteBuffers(2, mesh_ibo_);
+}
+
+void DistortionRenderer::ApplyDistortionCorrectionToTexture(
+    EyeType eye, const GLuint* texture_ids, const bool* vertical_flip,
+    const bool* separate_eye, const int* late_latch_layer, int num_textures,
+    bool blend_with_previous_layer, bool do_gl_state_prep) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+
+  bool use_gl_blend = use_alpha_vignette_ ||
+                      (blend_with_previous_layer && !kUseFramebufferReadback);
+  if (use_gl_blend) {
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  }
+  DrawEye(eye, texture_ids, vertical_flip, separate_eye, late_latch_layer,
+          num_textures, blend_with_previous_layer, do_gl_state_prep);
+  if (use_gl_blend) {
+    glDisable(GL_BLEND);
+  }
+  CHECK_GL();
+}
+
+void DistortionRenderer::DrawVideoQuad(EyeType eye, int layer_i,
+                                       GLuint texture_id,
+                                       const mat4& transform) {
+  shaders_[kSimpleVideoQuad].use();
+
+  shaders_[kSimpleVideoQuad].SetTexFromEyeTransform(
+      tex_from_eye_matrix_[eye][0][1]);
+  shaders_[kSimpleVideoQuad].SetEyeFromViewportTransform(
+      transform * kClipFromViewportMatrix);
+
+  if (eds_enabled_) {
+    // Bind late latch view-projection UBO that is produced by AddEdsLateLatch.
+    late_latch_[layer_i]->BindUniformBuffer(
+        POSE_BINDING, LateLatch::kViewMatrix, eye);
+    CHECK_GL();
+  } else {
+    // When EDS is disabled we just set the matrix here with no pose offset.
+    glBindBufferBase(GL_UNIFORM_BUFFER, POSE_BINDING + layer_i,
+                     uTexFromRecommendedViewportMatrix[eye][0][1]);
+    CHECK_GL();
+  }
+
+  glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING);
+  glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
+  CHECK_GL();
+
+  glDrawElements(GL_TRIANGLE_STRIP, mesh_node_[eye].indices.size(),
+                 GL_UNSIGNED_SHORT, nullptr);
+
+  CHECK_GL();
+}
+
+void DistortionRenderer::DoLateLatch(uint32_t target_vsync_count,
+                                     const uint32_t* render_buffer_index,
+                                     const GLuint* render_pose_buffer_objects,
+                                     const bool* vertical_flip,
+                                     const bool* separate_eye,
+                                     int num_textures) {
+  if (eds_enabled_) {
+    LateLatchInput data;
+    memset(&data, 0, sizeof(data));
+    for (int ti = 0; ti < num_textures; ++ti) {
+      if (late_latch_[ti] == nullptr)
+        late_latch_[ti].reset(new LateLatch(false));
+
+      int flip_index = vertical_flip[ti] ? 1 : 0;
+      int separate_eye_i = separate_eye[ti] ? 1 : 0;
+      // Copy data into late latch input struct.
+      for (int eye = 0; eye < 2; ++eye) {
+        data.eds_mat1[eye] =
+            tex_from_eye_matrix_[eye][flip_index][separate_eye_i];
+        data.eds_mat2[eye] = eye_from_viewport_matrix_[eye];
+      }
+      data.pose_index = target_vsync_count & kPoseAsyncBufferIndexMask;
+      data.render_pose_index = render_buffer_index[ti];
+
+      late_latch_[ti]->AddEdsLateLatch(data, render_pose_buffer_objects[ti]);
+    }
+  }
+}
+
+void DistortionRenderer::PrepGlState(EyeType eye) {
+  glViewport(eye_viewport_origin_[eye].x(), eye_viewport_origin_[eye].y(),
+             eye_viewport_size_.x(), eye_viewport_size_.y());
+
+  glBindVertexArray(mesh_vao_[eye]);
+  CHECK_GL();
+
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_[eye]);
+  CHECK_GL();
+
+  if (!eds_enabled_) {
+    glMemoryBarrier(GL_UNIFORM_BARRIER_BIT);
+  }
+}
+
+void DistortionRenderer::ResetGlState(int num_textures) {
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+  glBindBuffer(GL_ARRAY_BUFFER, 0);
+  glBindVertexArray(0);
+  if (eds_enabled_) {
+    for (int ti = 0; ti < num_textures; ++ti)
+      glBindBufferBase(GL_UNIFORM_BUFFER, POSE_BINDING + ti, 0);
+  } else {
+    glBindBuffer(GL_UNIFORM_BUFFER, 0);
+  }
+
+  CHECK_GL();
+
+  // Unbind all texture inputs.
+  for (int ti = 0; ti < num_textures; ++ti) {
+    glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING + ti);
+    glBindTexture(app_texture_target_, 0);
+  }
+  glActiveTexture(GL_TEXTURE0);
+}
+
+void DistortionRenderer::DrawEye(EyeType eye, const GLuint* texture_ids,
+                                 const bool* vertical_flip,
+                                 const bool* separate_eye,
+                                 const int* late_latch_layer, int num_textures,
+                                 bool blend_with_previous_layer,
+                                 bool do_gl_state_prep) {
+  if (do_gl_state_prep)
+    PrepGlState(eye);
+
+  if (num_textures > kMaxLayers) {
+    ALOGE("Too many textures for DistortionRenderer");
+    num_textures = kMaxLayers;
+  }
+
+  LOG_ALWAYS_FATAL_IF(num_textures != 1 && num_textures != 2);
+
+  if (num_textures == 2) {
+    if (chromatic_aberration_correction_enabled_) {
+      if (use_alpha_vignette_) {
+        shader_type_ = kChromaticAberrationCorrectionAlphaVignetteTwoLayers;
+      } else {
+        shader_type_ = kChromaticAberrationCorrectionTwoLayers;
+      }
+    } else {
+      shader_type_ = kNoChromaticAberrationCorrectionTwoLayers;
+    }
+  } else {
+    if (chromatic_aberration_correction_enabled_) {
+      if (blend_with_previous_layer) {
+        shader_type_ = kChromaticAberrationCorrectionWithBlend;
+      } else if (use_alpha_vignette_) {
+        shader_type_ = kChromaticAberrationCorrectionAlphaVignette;
+      } else {
+        shader_type_ = kChromaticAberrationCorrection;
+      }
+    } else {
+      shader_type_ = kNoChromaticAberrationCorrection;
+    }
+  }
+  shaders_[shader_type_].use();
+
+  for (int ti = 0; ti < num_textures; ++ti) {
+    int flip_index = vertical_flip[ti] ? 1 : 0;
+    if (eds_enabled_) {
+      // Bind late latch view-projection UBO that is produced by
+      // AddEdsLateLatch.
+      late_latch_[late_latch_layer[ti]]->BindUniformBuffer(
+          POSE_BINDING + ti, LateLatch::kViewProjMatrix, eye);
+      CHECK_GL();
+    } else {
+      // When EDS is disabled we just set the matrix here with no pose offset.
+      // With app late-latching, we can't know the pose that the app used
+      // because it's in the app's framebuffer.
+      int separate_eye_i = separate_eye[ti] ? 1 : 0;
+      glBindBufferBase(
+          GL_UNIFORM_BUFFER, POSE_BINDING + ti,
+          uTexFromRecommendedViewportMatrix[eye][flip_index][separate_eye_i]);
+      CHECK_GL();
+    }
+
+    glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING + ti);
+    glBindTexture(app_texture_target_, texture_ids[ti]);
+    CHECK_GL();
+  }
+
+  // Prevents left eye data from bleeding into right eye and vice-versa.
+  vec2 layer_min_max[kMaxLayers];
+  for (int i = 0; i < kMaxLayers; ++i)
+    layer_min_max[i] = vec2(0.0f, 0.0f);
+  for (int ti = 0; ti < num_textures; ++ti) {
+    if (separate_eye[ti]) {
+      layer_min_max[ti] = vec2(0.0f, 1.0f);  // Use the whole texture.
+    } else if (eye == kLeftEye) {
+      layer_min_max[ti] = vec2(0.0f, 0.499f);
+    } else {
+      layer_min_max[ti] = vec2(0.501f, 1.0f);
+    }
+  }
+  // The second layer stores its x min and max in the z,w slots of the vec4.
+  vec4 xTexMinMax(layer_min_max[0].x(), layer_min_max[0].y(),
+                  layer_min_max[1].x(), layer_min_max[1].y());
+
+  glUniform4fv(shaders_[shader_type_].uTexXMinMax, 1, &xTexMinMax[0]);
+  CHECK_GL();
+
+  glDrawElements(GL_TRIANGLE_STRIP, mesh_node_[eye].indices.size(),
+                 GL_UNSIGNED_SHORT, nullptr);
+  CHECK_GL();
+  if (do_gl_state_prep)
+    ResetGlState(num_textures);
+}
+
+void DistortionRenderer::SetDisplaySize(vec2i display_size) {
+  display_size_ = display_size;
+}
+
+void DistortionRenderer::SetEdsEnabled(bool enabled) { eds_enabled_ = enabled; }
+
+void DistortionRenderer::RecomputeDistortion(const CompositeHmd& hmd) {
+  using std::placeholders::_1;
+  using std::placeholders::_2;
+  using std::placeholders::_3;
+  using std::placeholders::_4;
+  DistortionFunction distortion_function =
+      std::bind(&CompositeHmd::ComputeDistortedVertex, &hmd, _1, _2, _3, _4);
+
+  for (int i = 0; i < 2; ++i) {
+    mesh_node_[i] =
+        BuildDistortionMesh(static_cast<EyeType>(i),
+                            distortion_mesh_resolution_, distortion_function);
+
+    glBindVertexArray(mesh_vao_[i]);
+
+    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_[i]);
+    glBufferData(GL_ARRAY_BUFFER,
+                 sizeof(EdsVertex) * mesh_node_[i].vertices.size(),
+                 &mesh_node_[i].vertices.front(), GL_STATIC_DRAW);
+
+    glEnableVertexAttribArray(POSITION_ATTR);
+    glEnableVertexAttribArray(VIEWPORT_COORD_R_ATTR);
+    glEnableVertexAttribArray(VIEWPORT_COORD_G_ATTR);
+    glEnableVertexAttribArray(VIEWPORT_COORD_B_ATTR);
+
+    glVertexAttribPointer(
+        POSITION_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+        reinterpret_cast<void*>(offsetof(EdsVertex, position)));
+
+    glVertexAttribPointer(
+        VIEWPORT_COORD_R_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+        reinterpret_cast<void*>(offsetof(EdsVertex, red_viewport_coords)));
+
+    glVertexAttribPointer(
+        VIEWPORT_COORD_G_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+        reinterpret_cast<void*>(offsetof(EdsVertex, green_viewport_coords)));
+
+    glVertexAttribPointer(
+        VIEWPORT_COORD_B_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+        reinterpret_cast<void*>(offsetof(EdsVertex, blue_viewport_coords)));
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_[i]);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+                 sizeof(uint16_t) * mesh_node_[i].indices.size(),
+                 &mesh_node_[i].indices.front(), GL_STATIC_DRAW);
+    CHECK_GL();
+  }
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+  glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+  glBindVertexArray(0);
+}
+
+bool DistortionRenderer::GetLastEdsPose(LateLatchOutput* out_data, int layer_id) const {
+  if (layer_id >= kMaxLayers) {
+    ALOGE("Accessing invalid layer %d", layer_id);
+    return false;
+  }
+
+  if (late_latch_[layer_id] != nullptr) {
+    late_latch_[layer_id]->CaptureOutputData(out_data);
+    return true;
+  } else {
+    ALOGE("Late latch shader not enabled.");
+    return false;
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/eds.cpp b/libs/vr/libeds/eds.cpp
new file mode 100644
index 0000000..8af5b27
--- /dev/null
+++ b/libs/vr/libeds/eds.cpp
@@ -0,0 +1,35 @@
+#include <dvr/eds.h>
+
+#include <private/dvr/graphics/vr_gl_extensions.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/types.h>
+
+// TODO(jbates) delete this file and eds.h
+
+extern "C" int dvrEdsInit(bool with_late_latch) { return 0; }
+
+extern "C" void dvrEdsDeinit() {}
+
+extern "C" int dvrEdsCapturePoseAsync(int eye, uint32_t target_vsync_count,
+                                      const float* projection_matrix,
+                                      const float* eye_from_head_matrix,
+                                      const float* pose_offset_matrix) {
+  return 0;
+}
+
+extern "C" int dvrEdsBindPose(int eye, uint32_t ubo_binding, intptr_t offset,
+                              ssize_t size) {
+  return 0;
+}
+
+extern "C" int dvrEdsBlitPose(int eye, int viewport_width,
+                              int viewport_height) {
+  return 0;
+}
+
+extern "C" int dvrEdsBlitPoseFromCpu(int eye, int viewport_width,
+                                     int viewport_height,
+                                     const float* pose_quaternion,
+                                     const float* pose_position) {
+  return 0;
+}
diff --git a/libs/vr/libeds/eds_mesh.cpp b/libs/vr/libeds/eds_mesh.cpp
new file mode 100644
index 0000000..01a90cf
--- /dev/null
+++ b/libs/vr/libeds/eds_mesh.cpp
@@ -0,0 +1,136 @@
+#include "include/private/dvr/eds_mesh.h"
+
+#include <log/log.h>
+#include <math.h>
+
+#include <private/dvr/types.h>
+
+namespace {
+
+using android::dvr::EdsVertex;
+using android::dvr::EyeType;
+using android::dvr::DistortionFunction;
+using android::dvr::vec2;
+
+// Computes the vertices for a distortion mesh with resolution |resolution| and
+// distortion provided by |hmd| and stores them in |vertices|.
+static void ComputeDistortionMeshVertices(
+    EdsVertex* vertices, int resolution,
+    const DistortionFunction& distortion_function, EyeType eye) {
+  for (int row = 0; row < resolution; row++) {
+    for (int col = 0; col < resolution; col++) {
+      const float x_norm =
+          static_cast<float>(col) / (static_cast<float>(resolution - 1U));
+      const float y_norm =
+          static_cast<float>(row) / (static_cast<float>(resolution - 1U));
+
+      const vec2 xy_norm(x_norm, y_norm);
+      const size_t index = col * resolution + row;
+
+      // Evaluate distortion function to get the new coordinates for each color
+      // channel. The distortion function returns the new coordinates relative
+      // to a full viewport with 0 <= x <= 1 for each eye.
+      vec2 coords[3];
+      distortion_function(eye, xy_norm, &vertices[index].position, coords);
+
+      // Store distortion mapping in texture coordinates.
+      vertices[index].red_viewport_coords = coords[0];
+      vertices[index].green_viewport_coords = coords[1];
+      vertices[index].blue_viewport_coords = coords[2];
+    }
+  }
+}
+
+// Computes the triangle strip indices for a distortion mesh with resolution
+// |resolution| and stores them in |indices|.
+static void ComputeDistortionMeshIndices(uint16_t* indices, int resolution) {
+  // The following strip method has been used in the Cardboard SDK
+  // (java/com/google/vrtoolkit/cardboard/DistortionRenderer.java) and has
+  // originally been described at:
+  //
+  // http://dan.lecocq.us/wordpress/2009/12/25/triangle-strip-for-grids-a-construction/
+  //
+  // For a grid with 4 rows and 4 columns of vertices, the strip would
+  // look like:
+  //                             ↻
+  //         0    -    4    -    8    -   12
+  //         ↓    ↗    ↓    ↗    ↓    ↗    ↓
+  //         1    -    5    -    9    -   13
+  //         ↓    ↖    ↓    ↖    ↓    ↖    ↓
+  //         2    -    6    -   10    -   14
+  //         ↓    ↗    ↓    ↗    ↓    ↗    ↓
+  //         3    -    7    -   11    -   15
+  //                   ↺
+  //
+  // Note the little circular arrows next to 7 and 8 that indicate
+  // repeating that vertex once so as to produce degenerate triangles.
+  //
+  // To facilitate scanline racing, the vertex order is left to right.
+
+  int16_t index_offset = 0;
+  int16_t vertex_offset = 0;
+  for (int row = 0; row < resolution - 1; ++row) {
+    if (row > 0) {
+      indices[index_offset] = indices[index_offset - 1];
+      ++index_offset;
+    }
+    for (int col = 0; col < resolution; ++col) {
+      if (col > 0) {
+        if (row % 2 == 0) {
+          // Move right on even rows.
+          ++vertex_offset;
+        } else {
+          --vertex_offset;
+        }
+      }
+      // A cast to uint16_t is safe here as |vertex_offset| will not drop below
+      // zero in this loop. As col is initially equal to zero |vertex_offset| is
+      // always incremented before being decremented, is initialized to zero and
+      // is only incremented outside of the loop.
+      indices[index_offset++] = static_cast<uint16_t>(vertex_offset);
+      indices[index_offset++] = static_cast<uint16_t>(
+          vertex_offset + static_cast<int16_t>(resolution));
+    }
+    vertex_offset =
+        static_cast<int16_t>(static_cast<int>(resolution) + vertex_offset);
+  }
+}
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// Builds a distortion mesh of resolution |resolution| using the distortion
+// provided by |hmd| for |eye|.
+EdsMesh BuildDistortionMesh(EyeType eye, int resolution,
+                            const DistortionFunction& distortion_function) {
+  LOG_ALWAYS_FATAL_IF(resolution <= 2);
+
+  // Number of indices produced by the strip method
+  // (see comment in ComputeDistortionMeshIndices):
+  //
+  //     1 vertex per triangle
+  //     2 triangles per quad, (rows - 1) * (cols - 1) quads
+  //     2 vertices at the start of each row for the first triangle
+  //     1 extra vertex per row (except first and last) for a
+  //       degenerate triangle
+  //
+  const uint16_t index_count =
+      static_cast<uint16_t>(resolution * (2 * resolution - 1U) - 2U);
+  const uint16_t vertex_count = static_cast<uint16_t>(resolution * resolution);
+
+  EdsMesh mesh;
+  mesh.vertices.resize(vertex_count);
+  mesh.indices.resize(index_count);
+
+  // Populate vertex and index buffer.
+  ComputeDistortionMeshVertices(&mesh.vertices[0], resolution,
+                                distortion_function, eye);
+  ComputeDistortionMeshIndices(&mesh.indices[0], resolution);
+
+  return mesh;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/include/CPPLINT.cfg b/libs/vr/libeds/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libeds/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libeds/include/dvr/eds.h b/libs/vr/libeds/include/dvr/eds.h
new file mode 100644
index 0000000..37b1297
--- /dev/null
+++ b/libs/vr/libeds/include/dvr/eds.h
@@ -0,0 +1,150 @@
+#ifndef ANDROID_DVR_EDS_H_
+#define ANDROID_DVR_EDS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+// This struct aligns with GLSL uniform blocks with std140 layout.
+// std140 allows padding between certain types, so padding must be explicitly
+// added as struct members.
+struct __attribute__((__packed__)) DvrLateLatchData {
+  // Column-major order.
+  float view_proj_matrix[16];
+  // Column-major order.
+  float view_matrix[16];
+  float pose_quaternion[4];
+  float pose_position[4];
+};
+
+//
+// These APIs are not thread safe and must be called on a single thread with an
+// actively bound GL context corresponding to a display surface.
+//
+
+// Prepares EDS and Late Latching system. Idempotent if called more than once.
+// The target GL context must be created and bound.
+//
+// If |with_late_latch| is true, a thread will be created that asynchronously
+// updates the pose in memory.
+//
+// The following GL states are modified as follows:
+// glBindBuffer(GL_ARRAY_BUFFER, 0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+//
+// Returns 0 on success, negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsInit(bool with_late_latch);
+
+// Stops and destroys the EDS Late Latching system.
+void dvrEdsDeinit();
+
+// Submits GL draw command that will capture the latest head pose into a uniform
+// buffer object. This should be called twice per frame, before the app begins
+// drawing for each eye.
+// For each eye, a later call to dvrEdsBlitPose will write this pose into
+// the application framebuffer corner so that the EDS service knows what pose
+// the frame was rendered with.
+//
+// |eye| is 0 for left eye and 1 for right eye.
+//
+// The following GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, id);
+// glDisable(GL_RASTERIZER_DISCARD);
+//
+// Returns 0 on success, negative error code on failure:
+//   EPERM - dvrEdsInit(true) was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsCapturePoseAsync(int eye, uint32_t target_vsync_count,
+                           const float* projection_matrix,
+                           const float* eye_from_head_matrix,
+                           const float* pose_offset_matrix);
+
+// Binds the late-latch output data as a GL_UNIFORM_BUFFER so that your vertex
+// shaders can use the latest head pose. For example, to bind just the
+// view_matrix from the output:
+//
+// dvrEdsBindPose(eye, BINDING,
+//                       offsetof(DvrLateLatchData, view_matrix),
+//                       sizeof(DvrLateLatchData::view_matrix));
+//
+// Or more commonly, bind the view projection matrix:
+//
+// dvrEdsBindPose(eye, BINDING,
+//                       offsetof(DvrLateLatchData, view_proj_matrix),
+//                       sizeof(DvrLateLatchData::view_proj_matrix));
+//
+// BINDING in the above examples is the binding location of the uniform
+// interface block in the GLSL shader.
+//
+// Shader example (3 would be the |ubo_binding| passed to this function):
+//  layout(binding = 3, std140) uniform LateLatchData {
+//    mat4 uViewProjection;
+//  };
+//
+// |eye| is 0 for left eye and 1 for right eye.
+//
+// The following GL states are modified as follows:
+// glBindBuffer(GL_UNIFORM_BUFFER, ...);
+// glBindBufferRange(GL_UNIFORM_BUFFER, ...);
+//
+// To clear the binding, call glBindBuffer(GL_UNIFORM_BUFFER, 0);
+//
+// Returns 0 on success, negative error code on failure:
+//   EPERM - dvrEdsInit(true) was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBindPose(int eye, uint32_t ubo_binding, intptr_t offset,
+                   ssize_t size);
+
+// DEPRECATED
+//
+// Blits the pose captured previously into the currently bound framebuffer.
+// The current framebuffer is assumed to be the default framebuffer 0, the
+// surface that will be sent to the display and have EDS and lens warp applied
+// to it.
+//
+// |eye| is 0 for left eye and 1 for right eye.
+// |viewport_width| is the width of the viewport for this eye, which is
+//                  usually half the width of the framebuffer.
+// |viewport_height| is the height of the viewport for this eye, which is
+//                   usually the height of the framebuffer.
+//
+// The following GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferRange(GL_UNIFORM_BUFFER, 23, ...);
+//
+// Returns 0 on success, negative error code on failure:
+//   EPERM - dvrEdsInit was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBlitPose(int eye, int viewport_width, int viewport_height);
+
+// DEPRECATED
+//
+// Same as dvrEdsBlitPose except that the pose is provided as an
+// parameter instead of getting it from dvrEdsBindPose. This is for
+// applications that want EDS but do not want late-latching.
+//
+// |pose_quaternion| should point to 4 floats that represent a quaternion.
+// |pose_position| should point to 3 floats that represent x,y,z position.
+//
+// GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferBase(GL_UNIFORM_BUFFER, 23, ...);
+//
+// Returns 0 on success, negative error code on failure:
+//   EPERM - dvrEdsInit was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBlitPoseFromCpu(int eye, int viewport_width, int viewport_height,
+                          const float* pose_quaternion,
+                          const float* pose_position);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_EDS_H_
diff --git a/libs/vr/libeds/include/private/dvr/color_channel_distortion.h b/libs/vr/libeds/include/private/dvr/color_channel_distortion.h
new file mode 100644
index 0000000..4e612cd
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/color_channel_distortion.h
@@ -0,0 +1,30 @@
+#ifndef ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
+#define ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// ColorChannelDistortion encapsulates the way one color channel (wavelength)
+// is distorted optically when an image is viewed through a lens.
+class ColorChannelDistortion {
+ public:
+  virtual ~ColorChannelDistortion() {}
+
+  // Given a 2d point p, returns the corresponding distorted point.
+  // The units of both the input and output points are tan-angle units,
+  // which can be computed as the distance on the screen divided by
+  // distance from the virtual eye to the screen.  For both the input
+  // and output points, the intersection of the optical axis of the lens
+  // with the screen defines the origin, the x axis points right, and
+  // the y axis points up.
+  virtual vec2 Distort(vec2 p) const = 0;
+
+  virtual vec2 DistortInverse(vec2 p) const = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/composite_hmd.h b/libs/vr/libeds/include/private/dvr/composite_hmd.h
new file mode 100644
index 0000000..70727e0
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/composite_hmd.h
@@ -0,0 +1,89 @@
+#ifndef ANDROID_DVR_COMPOSITE_HMD_H_
+#define ANDROID_DVR_COMPOSITE_HMD_H_
+
+#include <private/dvr/display_metrics.h>
+#include <private/dvr/head_mount_metrics.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// An intermediate structure composed of a head mount (described by
+// HeadMountMetrics) and a display (described by DisplayMetrics).
+class CompositeHmd {
+ public:
+  // Constructs a new CompositeHmd given a HeadMountMetrics and a
+  // DisplayMetrics.
+  CompositeHmd(const HeadMountMetrics& head_mount_metrics,
+               const DisplayMetrics& display_metrics);
+
+  CompositeHmd(CompositeHmd&& composite_hmd) = delete;
+  CompositeHmd(const CompositeHmd& composite_hmd) = delete;
+  CompositeHmd& operator=(CompositeHmd&& composite_hmd) = delete;
+  CompositeHmd& operator=(const CompositeHmd& composite_hmd) = delete;
+
+  // Headset metadata.
+  float GetTargetFrameDuration() const;
+  void ComputeDistortedVertex(EyeType eye, vec2 uv_in, vec2* vertex_out,
+                              vec2* uv_out) const;
+
+  // Eye-unspecific view accessors.
+  vec2i GetRecommendedRenderTargetSize() const;
+  Range2i GetDisplayRange() const;
+
+  // Eye-specific view accessors.
+  mat4 GetEyeFromHeadMatrix(EyeType eye) const;
+  FieldOfView GetEyeFov(EyeType eye) const;
+  Range2i GetEyeViewportBounds(EyeType eye) const;
+
+  // Set HeadMountMetrics and recompute everything that depends on
+  // HeadMountMetrics.
+  void SetHeadMountMetrics(const HeadMountMetrics& head_mount_metrics);
+
+  // Returns a reference to the |head_mount_metrics_| member.
+  const HeadMountMetrics& GetHeadMountMetrics() const;
+
+  // Set DisplayMetrics and recompute everything that depends on DisplayMetrics.
+  void SetDisplayMetrics(const DisplayMetrics& display_metrics);
+
+  // Returns a reference to the current display metrics.
+  const DisplayMetrics& GetDisplayMetrics() const;
+
+  // Compute the distorted point for a single channel.
+  vec2 ComputeDistortedPoint(EyeType eye, vec2 position,
+                             RgbColorChannel channel) const;
+
+  // Compute the inverse distorted point for a single channel.
+  vec2 ComputeInverseDistortedPoint(EyeType eye, vec2 position,
+                                    RgbColorChannel channel) const;
+
+ private:
+  FieldOfView eye_fov_[2];
+  Range2i eye_viewport_range_[2];
+  mat4 eye_from_head_matrix_[2];
+  Range2i display_range_;
+  vec2i recommended_render_target_size_;
+
+  // Per-eye scale and translation to convert from normalized Screen Space
+  // ([0:1]x[0:1]) to tan-angle space.
+  mat3 eye_tan_angle_from_norm_screen_matrix_[2];
+  mat3 eye_tan_angle_from_norm_screen_inv_matrix_[2];
+
+  // Per-eye scale and translation to convert from tan-angle space to normalized
+  // Texture Space ([0:1]x[0:1]).
+  mat3 eye_norm_texture_from_tan_angle_matrix_[2];
+  mat3 eye_norm_texture_from_tan_angle_inv_matrix_[2];
+
+  HeadMountMetrics head_mount_metrics_;
+  DisplayMetrics display_metrics_;
+
+  // Called by SetHeadMountMetrics/SetDisplayMetrics after metrics get changed.
+  // This function will update head_mount_metrics_/display_metrics_ based on the
+  // metrics supplied in the above two methods.
+  void MetricsChanged();
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_COMPOSITE_HMD_H_
diff --git a/libs/vr/libeds/include/private/dvr/device_metrics.h b/libs/vr/libeds/include/private/dvr/device_metrics.h
new file mode 100644
index 0000000..7985f28
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/device_metrics.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_DVR_DEVICE_METRICS_H_
+#define ANDROID_DVR_DEVICE_METRICS_H_
+
+#include <private/dvr/display_metrics.h>
+#include <private/dvr/head_mount_metrics.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+HeadMountMetrics CreateHeadMountMetrics();
+HeadMountMetrics CreateHeadMountMetrics(const FieldOfView& l_fov,
+                                        const FieldOfView& r_fov);
+HeadMountMetrics CreateUndistortedHeadMountMetrics();
+HeadMountMetrics CreateUndistortedHeadMountMetrics(const FieldOfView& l_fov,
+                                                   const FieldOfView& r_fov);
+DisplayMetrics CreateDisplayMetrics(vec2i screen_size);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DEVICE_METRICS_H_
diff --git a/libs/vr/libeds/include/private/dvr/display_metrics.h b/libs/vr/libeds/include/private/dvr/display_metrics.h
new file mode 100644
index 0000000..87d9d04
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/display_metrics.h
@@ -0,0 +1,79 @@
+#ifndef ANDROID_DVR_DISPLAY_METRICS_H_
+#define ANDROID_DVR_DISPLAY_METRICS_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+enum class DisplayOrientation { kPortrait, kLandscape };
+
+// DisplayMetrics encapsulates metrics describing a display to be used
+// with a head mount to create a head mounted display.
+class DisplayMetrics {
+ public:
+  DisplayMetrics();
+  // Constructs a DisplayMetrics given a display size in pixels,
+  // meters per pixel, border size in meters, and frame duration in
+  // seconds.
+  //
+  // size_pixels The size of the display in pixels.
+  // meters_per_pixel The meters per pixel in each dimension.
+  // border_size_meters The size of the border around the display
+  //     in meters.  When the device sits on a surface in the proper
+  //     orientation this is the distance from the surface to the edge
+  //     of the display.
+  // frame_duration_seconds The duration in seconds of each frame
+  //     (i.e., 1 / framerate).
+  DisplayMetrics(vec2i size_pixels, vec2 meters_per_pixel,
+                 float border_size_meters, float frame_duration_seconds,
+                 DisplayOrientation orientation);
+
+  // Gets the size of the display in physical pixels (not logical pixels).
+  vec2i GetSizePixels() const { return size_pixels_; }
+
+  DisplayOrientation GetOrientation() const { return orientation_; }
+  bool IsPortrait() const {
+    return orientation_ == DisplayOrientation::kPortrait;
+  }
+
+  // Gets the size of the display in meters.
+  vec2 GetSizeMeters() const {
+    return vec2(static_cast<float>(size_pixels_[0]),
+                static_cast<float>(size_pixels_[1]))
+               .array() *
+           meters_per_pixel_.array();
+  }
+
+  // Gets the meters per pixel.
+  vec2 GetMetersPerPixel() const { return meters_per_pixel_; }
+
+  // Gets the size of the border around the display.
+  // For a phone in landscape position this would be the distance from
+  // the bottom the edge of the phone to the bottom of the screen.
+  float GetBorderSizeMeters() const { return border_size_meters_; }
+
+  // Gets the frame duration in seconds for the display.
+  float GetFrameDurationSeconds() const { return frame_duration_seconds_; }
+
+  // Toggles the orientation and swaps all of the settings such that the
+  // display is being held in the other orientation.
+  void ToggleOrientation();
+
+  // Override the meters per pixel.
+  void SetMetersPerPixel(const vec2& meters_per_pixel) {
+    meters_per_pixel_ = meters_per_pixel;
+  }
+
+ private:
+  vec2i size_pixels_;
+  vec2 meters_per_pixel_;
+  float border_size_meters_;
+  float frame_duration_seconds_;
+  DisplayOrientation orientation_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_METRICS_H_
diff --git a/libs/vr/libeds/include/private/dvr/distortion_renderer.h b/libs/vr/libeds/include/private/dvr/distortion_renderer.h
new file mode 100644
index 0000000..28fd48a
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/distortion_renderer.h
@@ -0,0 +1,232 @@
+#ifndef ANDROID_DVR_DISTORTION_RENDERER_H_
+#define ANDROID_DVR_DISTORTION_RENDERER_H_
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <array>
+#include <functional>
+
+#include <private/dvr/eds_mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/render_texture_params.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class CompositeHmd;
+
+// Encapsulates the rendering operations to correct for the HMD's lens
+// distortion.
+class DistortionRenderer {
+ public:
+  static constexpr int kMaxLayers = 2;
+  static constexpr int kMaxLatchedLayers = 4;
+
+  static const mat4 kViewportFromClipMatrix;
+  static const mat4 kClipFromViewportMatrix;
+
+  // Creates a distortion renderer for distortion function.
+  //
+  // distortion_function the black-box distortion function to apply.
+  // display_size the resolution of the output of the distortion renderer.
+  // distortion_mesh_resolution the amount of subdivision in the
+  //     distortion mesh.
+  DistortionRenderer(const CompositeHmd& hmd, vec2i display_size,
+                     int distortion_mesh_resolution,
+                     bool flip_texture_horizontally,
+                     bool flip_texture_vertically, bool separated_eye_buffers,
+                     bool eds_enabled, bool late_latch_enabled);
+  ~DistortionRenderer();
+
+  // Returns the distortion factor array for the distortion function that was
+  // passed in at creation time. The distortion factor array contains the
+  // magnification factor induced by the distortion mesh at every vertex. There
+  // is one entry per vertex, and entries are ordered in row-major major. The
+  // array contains the magnification for both eyes averaged.
+  const std::vector<float>& GetDistortionFactorArray();
+
+  // |render_pose_buffer_object| is the per-texture pose array buffer object.
+  // |render_buffer_index| is the per-texture index into the pose array buffer
+  //                       object. This selects which pose was rendered into the
+  //                       corresponding texture.
+  void DoLateLatch(uint32_t target_vsync_count,
+                   const uint32_t* render_buffer_index,
+                   const GLuint* render_pose_buffer_objects,
+                   const bool* vertical_flip, const bool* separate_eye,
+                   int num_textures);
+
+  // Convenience method that does no flipping.
+  void DoLateLatch(uint32_t target_vsync_count,
+                   const uint32_t* render_buffer_index,
+                   const GLuint* render_pose_buffer_objects, int num_textures) {
+    bool flip[kMaxLayers] = {false};
+    bool separate[kMaxLayers] = {separated_eye_buffers_};
+    DoLateLatch(target_vsync_count, render_buffer_index,
+                render_pose_buffer_objects, flip, separate, num_textures);
+  }
+
+  void PrepGlState(EyeType eye);
+  void ResetGlState(int num_textures);
+
+  // Applies distortion correction to the given textures by rendering into the
+  // current output target.
+  //
+  // eye Which eye is being corrected.
+  // texture_ids The OpenGL texture IDs of the texture layers.
+  // texture_sizes Dimensions of the corresponding textures.
+  // vertical_flip Whether to flip each input texture vertically.
+  // separate_eye Whether the correspending texture is a separate texture for
+  //              left and right eyes. If false, it is a shared texture with
+  //              the left view on the left half and right on the right half.
+  // late_latch_layer Which late latch layer index to use for each texture.
+  //     Typically this is just {0, 1} unless blend_with_previous_layer is used.
+  // num_textures Number of textures in texture_ids and texture_sizes.
+  // blend_with_previous_layer If enabled, blend this single layer with the
+  //     existing framebuffer contents.
+  void ApplyDistortionCorrectionToTexture(
+      EyeType eye, const GLuint* texture_ids, const bool* vertical_flip,
+      const bool* separate_eye, const int* late_latch_layer, int num_textures,
+      bool blend_with_previous_layer, bool do_gl_state_prep);
+
+  // Convenience method that does no flipping.
+  void ApplyDistortionCorrectionToTexture(EyeType eye,
+                                          const GLuint* texture_ids,
+                                          int num_textures) {
+    bool flip[kMaxLayers] = {false};
+    bool separate[kMaxLayers] = {separated_eye_buffers_,
+                                 separated_eye_buffers_};
+    int latch_layer[kMaxLayers] = {0, 1};
+    ApplyDistortionCorrectionToTexture(eye, texture_ids, flip, separate,
+                                       latch_layer, num_textures, false, true);
+  }
+
+  // Draw a video quad based on the given video texture by rendering into the
+  // current output target.
+  //
+  // eye Which eye is being corrected.
+  // layer_id Which compositor layer the video mesh should be drawn into.
+  // texture_ids The OpenGL texture IDs of the texture layers.
+  // transform The transformation matrix that transforms the video mesh to its
+  //           desired eye space position for the target eye.
+  void DrawVideoQuad(EyeType eye, int layer_id, GLuint texture_id,
+                     const mat4& transform);
+
+  // Modifies the size of the output display. This is the number of physical
+  // pixels per dimension covered by the display on the output device. Calling
+  // this method is cheap; it only updates the state table of the two
+  // eye-specific mesh nodes.
+  void SetDisplaySize(vec2i size);
+
+  void SetEdsEnabled(bool enabled);
+  void SetChromaticAberrationCorrectionEnabled(bool enabled) {
+    chromatic_aberration_correction_enabled_ = enabled;
+  }
+  void SetUseAlphaVignette(bool enabled) { use_alpha_vignette_ = enabled; }
+
+  bool GetLastEdsPose(LateLatchOutput* out_data, int layer_id = 0) const;
+
+ private:
+  enum ShaderProgramType {
+    kNoChromaticAberrationCorrection,
+    kNoChromaticAberrationCorrectionTwoLayers,
+    kChromaticAberrationCorrection,
+    kChromaticAberrationCorrectionTwoLayers,
+    kChromaticAberrationCorrectionAlphaVignette,
+    kChromaticAberrationCorrectionAlphaVignetteTwoLayers,
+    kChromaticAberrationCorrectionWithBlend,
+    kSimpleVideoQuad,
+    kNumShaderPrograms,
+  };
+
+  struct EdsShader {
+    EdsShader() {}
+    ~EdsShader() {
+    }
+
+    void load(const char* vertex, const char* fragment, int num_layers,
+              bool use_alpha_vignette, float rotation, bool flip_vertical,
+              bool blend_with_previous_layer);
+    void use() { pgm.Use(); }
+
+    // Update uTexFromEyeMatrix and uEyeFromViewportMatrix by the distortion
+    // renderer with the transform matrix.
+    void SetTexFromEyeTransform(const mat4& transform) {
+      glUniformMatrix4fv(uTexFromEyeMatrix, 1, false, transform.data());
+    }
+
+    void SetEyeFromViewportTransform(const mat4& transform) {
+      glUniformMatrix4fv(uEyeFromViewportMatrix, 1, false, transform.data());
+    }
+
+    ShaderProgram pgm;
+
+    // Texture variables, named to match shader strings for convenience.
+    GLint uProjectionMatrix;
+    GLint uTexFromEyeMatrix;
+    GLint uEyeFromViewportMatrix;
+    GLint uTexXMinMax;
+  };
+
+  void DrawEye(EyeType eye, const GLuint* texture_ids,
+               const bool* vertical_flip, const bool* separate_eye,
+               const int* late_latch_layer, int num_textures,
+               bool blend_with_previous_layer, bool do_gl_state_prep);
+
+  // This function is called when there is an update on Hmd and distortion mesh
+  // vertices and factor array will be updated.
+  void RecomputeDistortion(const CompositeHmd& hmd);
+
+  // Per-eye, per flip, per separate eye mode buffers for setting EDS matrix
+  // when EDS is disabled.
+  GLuint uTexFromRecommendedViewportMatrix[2][2][2];
+
+  // Distortion mesh for the each eye.
+  EdsMesh mesh_node_[2];
+  // VBO (vertex buffer object) for distortion mesh vertices.
+  GLuint mesh_vbo_[2];
+  // VAO (vertex array object) for distortion mesh vertex array data.
+  GLuint mesh_vao_[2];
+  // IBO (index buffer object) for distortion mesh indices.
+  GLuint mesh_ibo_[2];
+
+  EdsShader shaders_[kNumShaderPrograms];
+
+  // Enum to indicate which shader program is being used.
+  ShaderProgramType shader_type_;
+
+  bool eds_enabled_;
+  bool chromatic_aberration_correction_enabled_;
+  bool use_alpha_vignette_;
+
+  // This keeps track of what distortion mesh resolution we are using currently.
+  // When there is an update on Hmd, the distortion mesh vertices/factor array
+  // will be re-computed with the old resolution that is stored here.
+  int distortion_mesh_resolution_;
+
+  // The OpenGL ID of the last texture passed to
+  // ApplyDistortionCorrectionToTexture().
+  GLuint last_distortion_texture_id_;
+
+  // GL texture 2D target for application texture.
+  GLint app_texture_target_;
+
+  // Precomputed matrices for EDS and viewport transforms.
+  mat4 tex_from_eye_matrix_[2][2][2];
+  mat4 eye_from_viewport_matrix_[2];
+
+  // Eye viewport locations.
+  vec2i eye_viewport_origin_[2];
+  vec2i eye_viewport_size_;
+
+  vec2i display_size_;
+
+  std::unique_ptr<LateLatch> late_latch_[kMaxLatchedLayers];
+  bool separated_eye_buffers_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISTORTION_RENDERER_H_
diff --git a/libs/vr/libeds/include/private/dvr/eds_mesh.h b/libs/vr/libeds/include/private/dvr/eds_mesh.h
new file mode 100644
index 0000000..d2c901e
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/eds_mesh.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_DVR_EDS_MESH_H_
+#define ANDROID_DVR_EDS_MESH_H_
+
+#include <stdint.h>
+#include <functional>
+#include <vector>
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+struct EdsVertex {
+  vec2 position;
+  vec2 red_viewport_coords;
+  vec2 green_viewport_coords;
+  vec2 blue_viewport_coords;
+};
+
+struct EdsMesh {
+  std::vector<EdsVertex> vertices;
+  std::vector<uint16_t> indices;
+};
+
+// Distortion function takes in a point in the range [0..1, 0..1] and returns
+// the vertex position and the three distorted points for separate R, G and B
+// channels.
+typedef std::function<void(EyeType, vec2, vec2*, vec2*)> DistortionFunction;
+
+// Builds a distortion mesh of resolution |resolution| using
+// the distortion provided by |hmd| for |eye|.
+EdsMesh BuildDistortionMesh(EyeType eye, int resolution,
+                            const DistortionFunction& distortion_function);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_EDS_MESH_H_
diff --git a/libs/vr/libeds/include/private/dvr/head_mount_metrics.h b/libs/vr/libeds/include/private/dvr/head_mount_metrics.h
new file mode 100644
index 0000000..f3e63a6
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/head_mount_metrics.h
@@ -0,0 +1,134 @@
+#ifndef ANDROID_DVR_HEAD_MOUNT_METRICS_H_
+#define ANDROID_DVR_HEAD_MOUNT_METRICS_H_
+
+#include <array>
+
+#include <private/dvr/color_channel_distortion.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// HeadMountMetrics encapsulates metrics describing a head mount to be used
+// with a display to create a head mounted display.
+class HeadMountMetrics {
+ public:
+  // The vertical point of the HMD where the lens distance is measured from.
+  enum VerticalAlignment { kBottom = 0, kCenter = 1, kTop = 2 };
+
+  enum EyeOrientation {
+    kCCW0Degrees = 0,
+    kCCW90Degrees = 1,
+    kCCW180Degrees = 2,
+    kCCW270Degrees = 3,
+    kCCW0DegreesMirrored = 4,
+    kCCW90DegreesMirrored = 5,
+    kCCW180DegreesMirrored = 6,
+    kCCW270DegreesMirrored = 7,
+
+    // Rotations that consist of an odd number of 90 degree rotations will swap
+    // the height and width of any bounding boxes/viewports. This bit informs
+    // any viewport manipulating code to perform the appropriate transformation.
+    kRightAngleBit = 0x01,
+    // Viewports are represented as four floating point values (four half
+    // angles). Rotating this structure can be done through a shift operation.
+    // This mask extracts the rotation portion of the orientation.
+    kRotationMask = 0x03,
+    // This mask specifies whether the output is mirrored.
+    kMirroredBit = 0x04
+  };
+
+  HeadMountMetrics(
+      float inter_lens_distance, float tray_to_lens_distance,
+      float virtual_eye_to_screen_distance,
+      VerticalAlignment vertical_alignment, const FieldOfView& left_eye_max_fov,
+      const FieldOfView& right_eye_max_fov,
+      const std::shared_ptr<ColorChannelDistortion>& red_distortion,
+      const std::shared_ptr<ColorChannelDistortion>& green_distortion,
+      const std::shared_ptr<ColorChannelDistortion>& blue_distortion,
+      EyeOrientation left_eye_orientation, EyeOrientation right_eye_orientation,
+      float screen_center_to_lens_distance)
+      : inter_lens_distance_(inter_lens_distance),
+        tray_to_lens_distance_(tray_to_lens_distance),
+        virtual_eye_to_screen_distance_(virtual_eye_to_screen_distance),
+        screen_center_to_lens_distance_(screen_center_to_lens_distance),
+        vertical_alignment_(vertical_alignment),
+        eye_max_fov_({{left_eye_max_fov, right_eye_max_fov}}),
+        color_channel_distortion_(
+            {{red_distortion, green_distortion, blue_distortion}}),
+        supports_chromatic_aberration_correction_(true),
+        eye_orientation_({{left_eye_orientation, right_eye_orientation}}) {
+    // If we're missing the green or blur distortions, assume that we don't
+    // correct for chromatic aberration.
+    if (!green_distortion || !blue_distortion) {
+      color_channel_distortion_[1] = red_distortion;
+      color_channel_distortion_[2] = red_distortion;
+      supports_chromatic_aberration_correction_ = false;
+    }
+  }
+
+  // Returns the distance in meters between the optical centers of the two
+  // lenses.
+  float GetInterLensDistance() const { return inter_lens_distance_; }
+
+  // Returns the distance in meters from the "tray" upon which the display
+  // rests to the optical center of a lens.
+  float GetTrayToLensDistance() const { return tray_to_lens_distance_; }
+
+  // Returns the distance in meters from the virtual eye to the screen.
+  // See http://go/vr-distortion-correction for an explanation of what
+  // this distance is.
+  float GetVirtualEyeToScreenDistance() const {
+    return virtual_eye_to_screen_distance_;
+  }
+
+  // Returns the horizontal distance from the center of the screen to the center
+  // of the lens, in meters.
+  float GetScreenCenterToLensDistance() const {
+    return screen_center_to_lens_distance_;
+  }
+
+  // Returns the vertical alignment of the HMD.  The tray-to-lens distance
+  // is relative to this position.  Exception: if the alignment is kCenter,
+  // then the offset has no meaning.
+  VerticalAlignment GetVerticalAlignment() const { return vertical_alignment_; }
+
+  // Returns the given eye's maximum field of view visible through the lens.
+  // The actual rendered field of view will be limited by this and also by
+  // the size of the screen.
+  const FieldOfView& GetEyeMaxFov(EyeType eye) const {
+    return eye_max_fov_[eye];
+  }
+
+  // Returns the ColorChannelDistortion object representing the distortion
+  // caused by the lenses for the given color channel.
+  const ColorChannelDistortion& GetColorChannelDistortion(
+      RgbColorChannel channel) const {
+    return *color_channel_distortion_[channel];
+  }
+
+  bool supports_chromatic_aberration_correction() const {
+    return supports_chromatic_aberration_correction_;
+  }
+
+  EyeOrientation GetEyeOrientation(EyeType eye) const {
+    return eye_orientation_[eye];
+  }
+
+ private:
+  float inter_lens_distance_;
+  float tray_to_lens_distance_;
+  float virtual_eye_to_screen_distance_;
+  float screen_center_to_lens_distance_;
+  VerticalAlignment vertical_alignment_;
+  std::array<FieldOfView, 2> eye_max_fov_;
+  std::array<std::shared_ptr<ColorChannelDistortion>, 3>
+      color_channel_distortion_;
+  bool supports_chromatic_aberration_correction_;
+  std::array<EyeOrientation, 2> eye_orientation_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_HEAD_MOUNT_METRICS_H_
diff --git a/libs/vr/libeds/include/private/dvr/identity_distortion.h b/libs/vr/libeds/include/private/dvr/identity_distortion.h
new file mode 100644
index 0000000..b9c5cf6
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/identity_distortion.h
@@ -0,0 +1,23 @@
+#ifndef ANDROID_DVR_IDENTITY_DISTORTION_H_
+#define ANDROID_DVR_IDENTITY_DISTORTION_H_
+
+#include <private/dvr/color_channel_distortion.h>
+
+namespace android {
+namespace dvr {
+
+// Provides an identity distortion operation if running the device without any
+// lenses.
+class IdentityDistortion : public ColorChannelDistortion {
+ public:
+  IdentityDistortion() {}
+
+  vec2 Distort(vec2 p) const override { return p; }
+
+  vec2 DistortInverse(vec2 p) const override { return p; }
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_IDENTITY_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/polynomial_radial_distortion.h b/libs/vr/libeds/include/private/dvr/polynomial_radial_distortion.h
new file mode 100644
index 0000000..8f080aa
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/polynomial_radial_distortion.h
@@ -0,0 +1,60 @@
+#ifndef ANDROID_DVR_POLYNOMIAL_RADIAL_DISTORTION_H_
+#define ANDROID_DVR_POLYNOMIAL_RADIAL_DISTORTION_H_
+
+#include <vector>
+
+#include <private/dvr/color_channel_distortion.h>
+
+namespace android {
+namespace dvr {
+
+// PolynomialRadialDistortion implements a radial distortion based using
+// a set of coefficients describing a polynomial function.
+// See http://en.wikipedia.org/wiki/Distortion_(optics).
+//
+// Unless otherwise stated, the units used in this class are tan-angle units
+// which can be computed as distance on the screen divided by distance from the
+// virtual eye to the screen.
+class PolynomialRadialDistortion : public ColorChannelDistortion {
+ public:
+  // Construct a PolynomialRadialDistortion with coefficients for
+  // the radial distortion equation:
+  //
+  //   p' = p (1 + K1 r^2 + K2 r^4 + ... + Kn r^(2n))
+  //
+  // where r is the distance in tan-angle units from the optical center,
+  // p the input point and p' the output point.
+  // The provided vector contains the coefficients for the even monomials
+  // in the distortion equation: coefficients[0] is K1, coefficients[1] is K2,
+  // etc.  Thus the polynomial used for distortion has degree
+  // (2 * coefficients.size()).
+  explicit PolynomialRadialDistortion(const std::vector<float>& coefficients);
+
+  // Given a radius (measuring distance from the optical axis of the lens),
+  // returns the distortion factor for that radius.
+  float DistortionFactor(float r_squared) const;
+
+  // Given a radius (measuring distance from the optical axis of the lens),
+  // returns the corresponding distorted radius.
+  float DistortRadius(float r) const;
+
+  // Given a 2d point p, returns the corresponding distorted point.
+  // distance from the virtual eye to the screen.  The optical axis
+  // of the lens defines the origin for both input and output points.
+  vec2 Distort(vec2 p) const override;
+
+  // Given a 2d point p, returns the point that would need to be passed to
+  // Distort to get point p (approximately).
+  vec2 DistortInverse(vec2 p) const override;
+
+  // Returns the distortion coefficients.
+  const std::vector<float>& GetCoefficients() const;
+
+ private:
+  std::vector<float> coefficients_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_POLYNOMIAL_RADIAL_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/raw_pose.h b/libs/vr/libeds/include/private/dvr/raw_pose.h
new file mode 100644
index 0000000..7058f1a
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/raw_pose.h
@@ -0,0 +1,54 @@
+#ifndef ANDROID_DVR_RAW_POSE_H_
+#define ANDROID_DVR_RAW_POSE_H_
+
+#include <atomic>
+
+namespace android {
+namespace dvr {
+
+// POD raw data of a head pose with a count field for read consistency checking.
+// Warning: The layout of this struct and RawPosePair are specific to match the
+// corresponding buffer type in the shader in late_latch.cpp.
+struct RawPose {
+  void Reset(uint32_t new_count) volatile {
+    qx = qy = qz = 0.0f;
+    qw = 1.0f;
+    px = py = pz = 0.0f;
+    count = new_count;
+  }
+
+  float qx, qy, qz, qw;
+  float px, py, pz;
+  std::atomic<uint32_t> count;
+};
+
+// RawPosePair is used for lock-free writing at about 1khz by the CPU/DSP
+// and reading by the GPU. At creation time, pose1 is given count = 1 and
+// pose2 is given count = 2.
+//
+// The lock-free write pattern is:
+// - write to pose with least count.
+// - memory write barrier.
+// - write count = count + 2.
+//
+// For reads, there is an important assumption about the GPU: it generally
+// processes things contiguously, without arbitrary preemptions that save and
+// restore full cache states. In other words, if the GPU is preempted and then
+// later resumed, any data that was read from memory before the preemption will
+// be re-read from memory after resume. This allows the following read trick to
+// work:
+// - read the full RawPosePair into a shader.
+// - select the pose with the newest count.
+//
+// The older pose may be partially written by the async stores from CPU/DSP, but
+// because of the memory barrier and GPU characteristics, the highest count pose
+// should always be a fully consistent RawPose.
+struct RawPosePair {
+  RawPose pose1;
+  RawPose pose2;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_RAW_POSE_H_
diff --git a/libs/vr/libeds/include/private/dvr/render_texture_params.h b/libs/vr/libeds/include/private/dvr/render_texture_params.h
new file mode 100644
index 0000000..71aebef
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/render_texture_params.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_DVR_RENDER_TEXTURE_PARAMS_H_
+#define ANDROID_DVR_RENDER_TEXTURE_PARAMS_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates information about the render texture, includes the size
+// of the render texture, and the left/right viewport which define the
+// portion each eye is rendering onto. This struct will be passed to
+// PresentFrame every frame before the client actually drawing the scene.
+struct RenderTextureParams {
+  RenderTextureParams() {}
+
+  RenderTextureParams(vec2i target_texture_size,
+                      const Range2i& eye_viewport_bounds_left,
+                      const Range2i& eye_viewport_bounds_right,
+                      const FieldOfView& eye_fov_left,
+                      const FieldOfView& eye_fov_right)
+      : texture_size(target_texture_size) {
+    eye_viewport_bounds[kLeftEye] = eye_viewport_bounds_left;
+    eye_viewport_bounds[kRightEye] = eye_viewport_bounds_right;
+    eye_fov[kLeftEye] = eye_fov_left;
+    eye_fov[kRightEye] = eye_fov_right;
+  }
+
+  explicit RenderTextureParams(vec2i target_texture_size,
+                               const FieldOfView& eye_fov_left,
+                               const FieldOfView& eye_fov_right) {
+    texture_size = target_texture_size;
+    eye_viewport_bounds[0] = Range2i::FromSize(
+        vec2i(0, 0), vec2i(texture_size[0] / 2, texture_size[1]));
+    eye_viewport_bounds[1] =
+        Range2i::FromSize(vec2i(texture_size[0] / 2, 0),
+                          vec2i(texture_size[0] / 2, texture_size[1]));
+
+    eye_fov[kLeftEye] = eye_fov_left;
+    eye_fov[kRightEye] = eye_fov_right;
+  }
+
+  // The render texture size.
+  vec2i texture_size;
+
+  // The viewport bounds on the render texture for each eye.
+  Range2i eye_viewport_bounds[2];
+
+  // The field of view for each eye in degrees.
+  FieldOfView eye_fov[2];
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_RENDER_TEXTURE_PARAMS_H_
diff --git a/libs/vr/libeds/polynomial_radial_distortion.cpp b/libs/vr/libeds/polynomial_radial_distortion.cpp
new file mode 100644
index 0000000..fa01bb4
--- /dev/null
+++ b/libs/vr/libeds/polynomial_radial_distortion.cpp
@@ -0,0 +1,53 @@
+#include "include/private/dvr/polynomial_radial_distortion.h"
+
+namespace android {
+namespace dvr {
+
+PolynomialRadialDistortion::PolynomialRadialDistortion(
+    const std::vector<float>& coefficients)
+    : coefficients_(coefficients) {}
+
+float PolynomialRadialDistortion::DistortionFactor(float r_squared) const {
+  float r_factor = 1.0f;
+  float distortion_factor = 1.0f;
+
+  for (float ki : coefficients_) {
+    r_factor *= r_squared;
+    distortion_factor += ki * r_factor;
+  }
+
+  return distortion_factor;
+}
+
+float PolynomialRadialDistortion::DistortRadius(float r) const {
+  return r * DistortionFactor(r * r);
+}
+
+vec2 PolynomialRadialDistortion::Distort(vec2 p) const {
+  return p * DistortionFactor(p.squaredNorm());
+}
+
+vec2 PolynomialRadialDistortion::DistortInverse(vec2 p) const {
+  // Secant method.
+  const float radius = p.norm();
+  float r0 = radius / 0.9f;
+  float r1 = radius * 0.9f;
+  float r2;
+  float dr0 = radius - DistortRadius(r0);
+  float dr1;
+  while (fabsf(r1 - r0) > 0.0001f /** 0.1mm */) {
+    dr1 = radius - DistortRadius(r1);
+    r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
+    r0 = r1;
+    r1 = r2;
+    dr0 = dr1;
+  }
+  return (r1 / radius) * p;
+}
+
+const std::vector<float>& PolynomialRadialDistortion::GetCoefficients() const {
+  return coefficients_;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/tests/eds_app_tests.cpp b/libs/vr/libeds/tests/eds_app_tests.cpp
new file mode 100644
index 0000000..549d864
--- /dev/null
+++ b/libs/vr/libeds/tests/eds_app_tests.cpp
@@ -0,0 +1,140 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+namespace {
+
+#define POSE_BINDING 0
+
+#ifndef STRINGIFY
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+#endif
+
+static const char g_vert_shader[] =
+    "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+    "uniform LateLatchData {\n"
+    "  mat4 uViewProjection;\n"
+    "};\n"
+    "void main() {\n"
+    "  vec2 verts[4];\n"
+    "  verts[0] = vec2(-1, -1);\n"
+    "  verts[1] = vec2(-1, 1);\n"
+    "  verts[2] = vec2(1, -1);\n"
+    "  verts[3] = vec2(1, 1);\n"
+    "  gl_Position = uViewProjection * vec4(verts[gl_VertexID], 0.0, 1.0);\n"
+    "}\n";
+
+static const char g_frag_shader[] =
+    "precision mediump float;\n"
+    "out vec4 outColor;\n"
+    "void main() {\n"
+    "  outColor = vec4(1.0);\n"
+    "}\n";
+
+DvrGraphicsContext* CreateContext(int* surface_width, int* surface_height) {
+  DvrGraphicsContext* context = nullptr;
+  int display_width = 0, display_height = 0;
+  float inter_lens_meters = 0.0f;
+  float left_fov[4] = {0.0f};
+  float right_fov[4] = {0.0f};
+  int disable_warp = 0;
+  int enable_late_latch = 1;
+  DvrSurfaceParameter surface_params[] = {
+      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+      DVR_SURFACE_PARAMETER_IN(ENABLE_LATE_LATCH, enable_late_latch),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, surface_width),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, surface_height),
+      DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+      DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+      DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+  dvrGraphicsContextCreate(surface_params, &context);
+  return context;
+}
+
+}  // namespace
+
+TEST(SensorAppTests, EdsWithLateLatch) {
+  int surface_width = 0, surface_height = 0;
+  DvrGraphicsContext* context = CreateContext(&surface_width, &surface_height);
+  ASSERT_NE(nullptr, context);
+
+  android::dvr::ShaderProgram shader(g_vert_shader, g_frag_shader);
+
+  for (int i = 0; i < 5; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+
+    const auto ident_mat = android::dvr::mat4::Identity();
+    const float* ident_mats[] = { ident_mat.data(), ident_mat.data() };
+    GLuint late_latch_buffer_id = 0;
+    int ret = dvrBeginRenderFrameLateLatch(context, 0, schedule.vsync_count, 2,
+                                           ident_mats, ident_mats, ident_mats,
+                                           &late_latch_buffer_id);
+    EXPECT_EQ(0, ret);
+    for (int eye = 0; eye < 2; ++eye) {
+      if (eye == 0)
+        glViewport(0, 0, surface_width / 2, surface_height);
+      else
+        glViewport(surface_width / 2, 0, surface_width / 2, surface_height);
+
+      glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+      shader.Use();
+
+      // Bind late latch pose matrix buffer.
+      glBindBufferRange(
+          GL_UNIFORM_BUFFER, POSE_BINDING, late_latch_buffer_id,
+          offsetof(DvrGraphicsLateLatchData, view_proj_matrix[eye]),
+          16 * sizeof(float));
+
+      // TODO(jbates): use transform feedback here to grab the vertex output
+      // and verify that it received late-latch pose data. Combine this with
+      // mocked pose data to verify that late-latching is working.
+      glDrawArrays(GL_POINTS, 0, 4);
+    }
+    dvrPresent(context);
+  }
+
+  glFinish();
+  dvrGraphicsContextDestroy(context);
+}
+
+TEST(SensorAppTests, EdsWithoutLateLatch) {
+  int surface_width = 0, surface_height = 0;
+  DvrGraphicsContext* context = CreateContext(&surface_width, &surface_height);
+  ASSERT_NE(nullptr, context);
+  DvrPose* client = dvrPoseCreate();
+  ASSERT_NE(nullptr, client);
+
+  for (int i = 0; i < 5; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+    DvrPoseAsync pose;
+    int ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+    ASSERT_EQ(0, ret);
+
+    dvrBeginRenderFrameEds(context, pose.orientation, pose.translation);
+    for (int eye = 0; eye < 2; ++eye) {
+      if (eye == 0)
+        glViewport(0, 0, surface_width / 2, surface_height);
+      else
+        glViewport(surface_width / 2, 0, surface_width / 2, surface_height);
+
+      glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+      EXPECT_EQ(0, ret);
+    }
+    dvrPresent(context);
+  }
+
+  dvrPoseDestroy(client);
+  dvrGraphicsContextDestroy(context);
+}
diff --git a/libs/vr/libimageio/Android.bp b/libs/vr/libimageio/Android.bp
new file mode 100644
index 0000000..7dde075
--- /dev/null
+++ b/libs/vr/libimageio/Android.bp
@@ -0,0 +1,26 @@
+
+
+sourceFiles = [
+    "image_io.cpp",
+    "image_io_png.cpp",
+    "image_io_ppm.cpp",
+]
+
+includeFiles = ["include"]
+
+sharedLibraries = [
+    "libcutils",
+    "libpng",
+]
+
+cc_library_static {
+    srcs: sourceFiles,
+    export_include_dirs: includeFiles,
+    shared_libs: sharedLibraries,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    name: "libimageio",
+    tags: ["optional"],
+}
diff --git a/libs/vr/libimageio/image_io.cpp b/libs/vr/libimageio/image_io.cpp
new file mode 100644
index 0000000..5ad6c2d
--- /dev/null
+++ b/libs/vr/libimageio/image_io.cpp
@@ -0,0 +1,92 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include <private/dvr/image_io_base.h>
+#include <private/dvr/image_io_logging.h>
+#include <private/dvr/image_io_png.h>
+#include <private/dvr/image_io_ppm.h>
+
+namespace {
+
+// Returns true if |str| ends with |suffix|.
+bool EndsWith(const std::string& str, const std::string& suffix) {
+  if (str.length() < suffix.length())
+    return false;
+
+  return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
+}
+
+// Returns lower case copy of the input string.
+std::string ToLower(std::string str) {
+  std::transform(str.begin(), str.end(), str.begin(),
+                 [](char x) { return std::tolower(x); });
+  return str;
+}
+
+}  // namespace
+
+std::unique_ptr<ImageIoReader> ImageIoReader::Create(const char* filename) {
+  std::unique_ptr<ImageIoReader> reader;
+  std::string filename_lower = ToLower(filename);
+
+  if (EndsWith(filename_lower, ".ppm"))
+    reader.reset(new ImageIoPpmReader(filename));
+
+  if (!reader) {
+    ALOGE("Unknown/unsupported image file format.");
+    return nullptr;
+  }
+
+  return reader;
+}
+
+std::unique_ptr<ImageIoWriter> ImageIoWriter::Create(const char* filename,
+                                                     int width, int height,
+                                                     const uint8_t* image) {
+  std::unique_ptr<ImageIoWriter> writer;
+  std::string filename_lower = ToLower(filename);
+
+  if (EndsWith(filename_lower, ".ppm"))
+    writer.reset(new ImageIoPpmWriter(filename, width, height, image));
+  else if (EndsWith(filename_lower, ".png"))
+    writer.reset(new ImageIoPngWriter(filename, width, height, image));
+
+  if (!writer) {
+    ALOGE("Unknown/unsupported image file format.");
+    return nullptr;
+  }
+
+  return writer;
+}
+
+extern "C" {
+
+bool image_io_write_rgb888(const char* filename, int width, int height,
+                           const uint8_t* image) {
+  auto writer = ImageIoWriter::Create(filename, width, height, image);
+  if (!writer)
+    return false;
+  return writer->WriteRgb888();
+}
+
+bool image_io_read_rgb888(const char* filename, int* width, int* height,
+                          uint8_t** image) {
+  auto reader = ImageIoReader::Create(filename);
+  if (!reader)
+    return false;
+  if (!reader->ReadRgb888())
+    return false;
+  *width = reader->width();
+  *height = reader->height();
+  *image = reader->ReleaseImage();
+  return true;
+}
+
+void image_io_release_buffer(uint8_t* image) { delete[] image; }
+
+}  // extern "C"
diff --git a/libs/vr/libimageio/image_io_png.cpp b/libs/vr/libimageio/image_io_png.cpp
new file mode 100644
index 0000000..e0a118b
--- /dev/null
+++ b/libs/vr/libimageio/image_io_png.cpp
@@ -0,0 +1,87 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io_png.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <private/dvr/image_io_logging.h>
+
+#include "png.h"
+
+namespace {
+
+void WriteChunkCallback(png_structp out_ptr, png_bytep chunk_ptr,
+                        png_size_t chunk_size) {
+  auto* writer = static_cast<ImageIoPngWriter*>(png_get_io_ptr(out_ptr));
+  const char* chunk = reinterpret_cast<const char*>(chunk_ptr);
+  writer->WriteChunk(chunk, chunk_size);
+}
+
+}  // namespace
+
+ImageIoPngWriter::ImageIoPngWriter(const char* filename, int width, int height,
+                                   const uint8_t* image)
+    : ImageIoWriter(filename, width, height, image),
+      out_(filename_),
+      write_failed_(false) {}
+
+bool ImageIoPngWriter::WriteChunk(const char* chunk, int chunk_size) {
+  out_.write(chunk, chunk_size);
+  if (!out_) {
+    if (write_failed_) {
+      // Error was already logged once.
+      return false;
+    }
+
+    ALOGE("Failed to write .png image to %s.", filename_.c_str());
+    write_failed_ = true;
+    return false;
+  }
+  return true;
+}
+
+// Writes RGB888 image to png file.
+// Refactored from Chromium:
+// WebKit/Source/platform/image-encoders/skia/PNGImageEncoder.cpp
+bool ImageIoPngWriter::WriteRgb888() {
+  if (width_ <= 0 || height_ <= 0) {
+    ALOGE("Invalid width or height.");
+    return false;
+  }
+
+  if (!out_) {
+    ALOGE("Failed to open output file %s.", filename_.c_str());
+    return false;
+  }
+
+  png_struct* png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+  png_info* info = png_create_info_struct(png);
+  if (!png || !info || setjmp(png_jmpbuf(png))) {
+    png_destroy_write_struct(png ? &png : 0, info ? &info : 0);
+    return false;
+  }
+
+  png_set_compression_level(png, 3);
+  png_set_filter(png, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
+
+  png_set_write_fn(png, this, WriteChunkCallback, 0);
+  png_set_IHDR(png, info, width_, height_, 8, PNG_COLOR_TYPE_RGB, 0, 0, 0);
+  png_write_info(png, info);
+
+  unsigned char* pixels =
+      reinterpret_cast<unsigned char*>(const_cast<uint8_t*>(image_));
+  const size_t stride = width_ * 3;
+  for (int y = 0; y < height_; ++y) {
+    png_write_row(png, pixels);
+    if (write_failed_)
+      return false;
+    pixels += stride;
+  }
+
+  png_write_end(png, info);
+  png_destroy_write_struct(&png, &info);
+
+  return !write_failed_;
+}
diff --git a/libs/vr/libimageio/image_io_ppm.cpp b/libs/vr/libimageio/image_io_ppm.cpp
new file mode 100644
index 0000000..2411888
--- /dev/null
+++ b/libs/vr/libimageio/image_io_ppm.cpp
@@ -0,0 +1,93 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io_ppm.h>
+
+#include <cwctype>
+#include <fstream>
+#include <string>
+
+#include <private/dvr/image_io_logging.h>
+
+bool ImageIoPpmWriter::WriteRgb888() {
+  std::ofstream out(filename_);
+  if (!out) {
+    ALOGE("Failed to open output file %s.", filename_.c_str());
+    return false;
+  }
+
+  // Write a PPM header. See http://netpbm.sourceforge.net/doc/ppm.html for
+  // the format specification.
+  constexpr int maximum_intensity = 255;
+  out << "P6\n"
+      << width_ << "\n"
+      << height_ << "\n"
+      << maximum_intensity << "\n";
+
+  // Write out the image itself.
+  out.write(reinterpret_cast<const char*>(image_), 3 * width_ * height_);
+
+  if (!out) {
+    ALOGE("Failed to write .ppm image to %s.", filename_.c_str());
+    return false;
+  }
+  return true;
+}
+
+bool ImageIoPpmReader::ReadRgb888() {
+  std::ifstream in(filename_);
+  if (!in) {
+    ALOGE("Failed to open input file %s.", filename_.c_str());
+    return false;
+  }
+
+  // Read PPM header. See http://netpbm.sourceforge.net/doc/ppm.html for
+  // the format specification.
+  char magic_number[2];
+  in.read(magic_number, 2);
+  if (magic_number[0] != 'P' || magic_number[1] != '6') {
+    ALOGE("Failed to read PPM, not a P6 file %s.", filename_.c_str());
+    return false;
+  }
+
+  int maximum_intensity = 0;
+
+  in >> width_;
+  in >> height_;
+  in >> maximum_intensity;
+
+  char delimiter;
+  in.read(&delimiter, 1);
+
+  if (!iswspace(delimiter) || width_ <= 0 || height_ <= 0 ||
+      maximum_intensity <= 0) {
+    ALOGE("Failed to parse PPM header for %s.", filename_.c_str());
+    return false;
+  }
+
+  if (maximum_intensity != 255) {
+    ALOGE("Failed to read PPM, only 8-bit depth supported %s.",
+          filename_.c_str());
+    return false;
+  }
+
+  // Read RGB data.
+  const int data_begin = in.tellg();
+  in.seekg(0, in.end);
+  const int data_end = in.tellg();
+  in.seekg(data_begin, in.beg);
+
+  const int data_size = data_end - data_begin;
+  if (data_size != 3 * width_ * height_) {
+    ALOGE("Failed to read PPM, unexpected data size %s.", filename_.c_str());
+    return false;
+  }
+
+  image_.reset(new uint8_t[data_size]);
+  char* data = reinterpret_cast<char*>(image_.get());
+
+  const auto it_data_begin = std::istreambuf_iterator<char>(in);
+  const auto it_data_end = std::istreambuf_iterator<char>();
+  std::copy(it_data_begin, it_data_end, data);
+
+  return true;
+}
diff --git a/libs/vr/libimageio/include/CPPLINT.cfg b/libs/vr/libimageio/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libimageio/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libimageio/include/private/dvr/image_io.h b/libs/vr/libimageio/include/private/dvr/image_io.h
new file mode 100644
index 0000000..5cb115d
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io.h
@@ -0,0 +1,32 @@
+#ifndef DVR_IMAGE_IO_H_
+#define DVR_IMAGE_IO_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+// Supported filetypes.
+#define DVR_IMAGE_IO_SUPPORTED_WRITE "png, ppm"
+#define DVR_IMAGE_IO_SUPPORTED_READ "ppm"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Writes an RGB888 image to file. Intended file type is autodetected
+// based on the extension. Currently supported formats: PNG, PPM.
+bool image_io_write_rgb888(const char* filename, int width, int height,
+                           const uint8_t* image);
+
+// Reads an RGB888 image from file. Image buffer needs to be released with
+// image_io_release_image. Currently supported formats: PPM.
+bool image_io_read_rgb888(const char* filename, int* width, int* height,
+                          uint8_t** image);
+
+// Releases image buffer allocated within the library.
+void image_io_release_buffer(uint8_t* image);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // DVR_IMAGE_IO_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_base.h b/libs/vr/libimageio/include/private/dvr/image_io_base.h
new file mode 100644
index 0000000..5f29de7
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_base.h
@@ -0,0 +1,56 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_BASE_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_BASE_H_
+
+#include <memory>
+#include <string>
+
+class ImageIoReader {
+ public:
+  virtual ~ImageIoReader() {}
+
+  static std::unique_ptr<ImageIoReader> Create(const char* filename);
+
+  virtual bool ReadRgb888() = 0;
+
+  int width() const { return width_; }
+
+  int height() const { return height_; }
+
+  uint8_t* ReleaseImage() { return image_.release(); }
+
+ protected:
+  int width_;
+  int height_;
+  std::unique_ptr<uint8_t[]> image_;
+  const std::string filename_;
+
+  explicit ImageIoReader(const char* filename)
+      : width_(0), height_(0), filename_(filename) {}
+
+  ImageIoReader() = delete;
+};
+
+class ImageIoWriter {
+ public:
+  virtual ~ImageIoWriter() {}
+
+  static std::unique_ptr<ImageIoWriter> Create(const char* filename, int width,
+                                               int height,
+                                               const uint8_t* image);
+
+  virtual bool WriteRgb888() = 0;
+
+ protected:
+  const int width_;
+  const int height_;
+  const uint8_t* image_;
+  const std::string filename_;
+
+  ImageIoWriter(const char* filename, int width, int height,
+                const uint8_t* image)
+      : width_(width), height_(height), image_(image), filename_(filename) {}
+
+  ImageIoWriter() = delete;
+};
+
+#endif  // LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_BASE_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_logging.h b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
new file mode 100644
index 0000000..a2629f3
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
@@ -0,0 +1,39 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_LOGGING_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_LOGGING_H_
+
+// This header acts as log/log.h if LOG_TO_STDERR is not defined.
+// If LOG_TO_STDERR is defined, then android logging macros (such as ALOGE)
+// would log to stderr. This is useful if the code is also being used/tested on
+// a desktop.
+
+#ifdef LOG_TO_STDERR
+#include <stdarg.h>
+#include <cstdio>
+
+#ifndef LOG_TAG
+#define LOG_TAG " "
+#endif  // LOG_TAG
+
+inline void LogToStderr(const char* severity, const char* fmt, ...) {
+  fprintf(stderr, "%s %s: ", LOG_TAG, severity);
+  va_list args;
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  va_end(args);
+  fprintf(stderr, "\n");
+  fflush(stderr);
+}
+
+#define ALOGE(fmt, ...) LogToStderr("ERROR", fmt, ##__VA_ARGS__)
+
+#define ALOGW(fmt, ...) LogToStderr("WARNING", fmt, ##__VA_ARGS__)
+
+#define ALOGI(fmt, ...) LogToStderr("INFO", fmt, ##__VA_ARGS__)
+
+#define ALOGV(fmt, ...) LogToStderr("VERBOSE", fmt, ##__VA_ARGS__)
+
+#else  // LOG_TO_STDERR
+#include <log/log.h>
+#endif  // LOG_TO_STDERR
+
+#endif  // LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_LOGGING_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_png.h b/libs/vr/libimageio/include/private/dvr/image_io_png.h
new file mode 100644
index 0000000..e06a17a
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_png.h
@@ -0,0 +1,24 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_PNG_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_PNG_H_
+
+#include <fstream>
+
+#include <private/dvr/image_io_base.h>
+
+class ImageIoPngWriter : public ImageIoWriter {
+ public:
+  bool WriteRgb888() override;
+
+  bool WriteChunk(const char* chunk, int chunk_size);
+
+ private:
+  ImageIoPngWriter(const char* filename, int width, int height,
+                   const uint8_t* image);
+
+  std::ofstream out_;
+  bool write_failed_;
+
+  friend class ImageIoWriter;
+};
+
+#endif  // LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_PNG_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_ppm.h b/libs/vr/libimageio/include/private/dvr/image_io_ppm.h
new file mode 100644
index 0000000..8a1a96c
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_ppm.h
@@ -0,0 +1,28 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_PPM_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_PPM_H_
+
+#include <private/dvr/image_io_base.h>
+
+class ImageIoPpmReader : public ImageIoReader {
+ public:
+  bool ReadRgb888() override;
+
+ private:
+  explicit ImageIoPpmReader(const char* filename) : ImageIoReader(filename) {}
+
+  friend class ImageIoReader;
+};
+
+class ImageIoPpmWriter : public ImageIoWriter {
+ public:
+  bool WriteRgb888() override;
+
+ private:
+  ImageIoPpmWriter(const char* filename, int width, int height,
+                   const uint8_t* image)
+      : ImageIoWriter(filename, width, height, image) {}
+
+  friend class ImageIoWriter;
+};
+
+#endif  // LIB_LIBIMAGEIO_PRIVATE_DVR_IMAGE_IO_PPM_H_
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
new file mode 100644
index 0000000..69300d6
--- /dev/null
+++ b/libs/vr/libpdx/Android.bp
@@ -0,0 +1,59 @@
+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",
+    ],
+}
+
+// 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..cfc2022
--- /dev/null
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -0,0 +1,54 @@
+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",
+    ],
+}
+
+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..850c6d3
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel_factory.cpp
@@ -0,0 +1,151 @@
+#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, 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);
+    }
+    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 ErrorStatus(status.error());
+  ResponseHeader<LocalHandle> response;
+  status = ReceiveData(socket_.Borrow(), &response);
+  if (!status)
+    return ErrorStatus(status.error());
+  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..5b7e5ff
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
@@ -0,0 +1,205 @@
+#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 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..eb87827
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
@@ -0,0 +1,165 @@
+#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);
+
+  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..6c92259
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -0,0 +1,715 @@
+#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 <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 channel_fd{accept4(socket_fd_.Get(),
+                                 reinterpret_cast<sockaddr*>(&remote), &addrlen,
+                                 SOCK_CLOEXEC)};
+  if (!channel_fd) {
+    ALOGE("Endpoint::AcceptConnection: failed to accept connection: %s",
+          strerror(errno));
+    return ErrorStatus(errno);
+  }
+
+  int optval = 1;
+  if (setsockopt(channel_fd.Get(), SOL_SOCKET, SO_PASSCRED, &optval,
+                 sizeof(optval)) == -1) {
+    ALOGE(
+        "Endpoint::AcceptConnection: Failed to enable the receiving of the "
+        "credentials for channel %d: %s",
+        channel_fd.Get(), strerror(errno));
+    return ErrorStatus(errno);
+  }
+
+  // Borrow the channel handle before we pass (move) it into OnNewChannel().
+  BorrowedHandle borrowed_channel_handle = channel_fd.Borrow();
+  auto status = OnNewChannel(std::move(channel_fd));
+  if (status)
+    status = ReceiveMessageForChannel(borrowed_channel_handle, message);
+  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<RemoteChannelHandle> Endpoint::PushChannel(Message* message,
+                                                  int /*flags*/,
+                                                  Channel* channel,
+                                                  int* channel_id) {
+  int channel_pair[2] = {};
+  if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, channel_pair) == -1) {
+    ALOGE("Endpoint::PushChannel: Failed to create a socket pair: %s",
+          strerror(errno));
+    return ErrorStatus(errno);
+  }
+
+  LocalHandle local_socket{channel_pair[0]};
+  LocalHandle remote_socket{channel_pair[1]};
+
+  int optval = 1;
+  if (setsockopt(local_socket.Get(), SOL_SOCKET, SO_PASSCRED, &optval,
+                 sizeof(optval)) == -1) {
+    ALOGE(
+        "Endpoint::PushChannel: Failed to enable the receiving of the "
+        "credentials for channel %d: %s",
+        local_socket.Get(), strerror(errno));
+    return ErrorStatus(errno);
+  }
+
+  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/libposepredictor/Android.bp b/libs/vr/libposepredictor/Android.bp
new file mode 100644
index 0000000..2f1d2f5
--- /dev/null
+++ b/libs/vr/libposepredictor/Android.bp
@@ -0,0 +1,58 @@
+// 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 = [
+    "predictor.cpp",
+    "buffered_predictor.cpp",
+    "linear_predictor.cpp",
+    "polynomial_predictor.cpp",
+    "dvr_pose_predictor.cpp",
+]
+
+includeFiles = [
+    "include",
+]
+
+staticLibraries = ["libvrsensor"]
+
+sharedLibraries = []
+
+headerLibraries = [ "libeigen" ]
+
+cc_library {
+    srcs: sourceFiles,
+    cflags: [
+      "-DLOG_TAG=\"libposepredictor\"",
+      "-DTRACE=0",
+    ],
+    export_include_dirs: includeFiles,
+    static_libs: staticLibraries,
+    shared_libs: sharedLibraries,
+    header_libs: headerLibraries,
+    export_header_lib_headers: headerLibraries,
+    name: "libposepredictor",
+}
+
+cc_test {
+    tags: ["optional"],
+    srcs: [
+        "predictor_tests.cpp",
+        "linear_predictor_tests.cpp",
+        "polynomial_predictor_tests.cpp",
+    ],
+
+    static_libs: ["libposepredictor"] + staticLibraries,
+    shared_libs: sharedLibraries,
+    name: "pose_predictor_tests",
+}
diff --git a/libs/vr/libposepredictor/buffered_predictor.cpp b/libs/vr/libposepredictor/buffered_predictor.cpp
new file mode 100644
index 0000000..f3b41dc
--- /dev/null
+++ b/libs/vr/libposepredictor/buffered_predictor.cpp
@@ -0,0 +1,38 @@
+#include <buffered_predictor.h>
+
+namespace posepredictor {
+
+BufferedPredictor::BufferedPredictor(size_t buffer_size) {
+  buffer_.resize(buffer_size);
+}
+
+void BufferedPredictor::BufferSample(const Pose& sample) {
+  const auto& prev_sample = buffer_[current_pose_index_];
+
+  // If we are updating a sample (the same time stamp), do not advance the
+  // counter.
+  if (sample.time_ns != prev_sample.time_ns) {
+    current_pose_index_ = (current_pose_index_ + 1) % buffer_.size();
+  }
+
+  buffer_[current_pose_index_] = sample;
+
+  // Make sure the subsequent orientations are the closest in quaternion space.
+  if (PrevSample(1).orientation.coeffs().dot(sample.orientation.coeffs()) < 0) {
+    // Flip the quaternion to be closest to the previous sample.
+    buffer_[current_pose_index_].orientation =
+        quat(-sample.orientation.w(), -sample.orientation.x(),
+             -sample.orientation.y(), -sample.orientation.z());
+  }
+
+  ++num_poses_added_;
+}
+
+const Pose& BufferedPredictor::PrevSample(size_t index) const {
+  // We must not request a pose too far in the past.
+  assert(index < buffer_.size());
+  return buffer_[(current_pose_index_ - index + buffer_.size()) %
+                 buffer_.size()];
+}
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/dvr_pose_predictor.cpp b/libs/vr/libposepredictor/dvr_pose_predictor.cpp
new file mode 100644
index 0000000..7f2ecc0
--- /dev/null
+++ b/libs/vr/libposepredictor/dvr_pose_predictor.cpp
@@ -0,0 +1,70 @@
+#include <private/dvr/dvr_pose_predictor.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+template <typename Vec3Type>
+float32x4_t FromVec3(const Vec3Type& from) {
+  return {static_cast<float>(from.x()), static_cast<float>(from.y()),
+          static_cast<float>(from.z()), 0};
+}
+
+template <typename QuatType>
+float32x4_t FromQuat(const QuatType& from) {
+  return {static_cast<float>(from.x()), static_cast<float>(from.y()),
+          static_cast<float>(from.z()), static_cast<float>(from.w())};
+}
+
+}  //  namespace
+
+void AddPredictorPose(posepredictor::Predictor* predictor,
+                      const posepredictor::vec3& start_t_head,
+                      const posepredictor::quat& start_q_head,
+                      int64_t pose_timestamp, DvrPoseAsync* out) {
+  // Feed the predictor.
+  predictor->Add(
+      posepredictor::Pose{pose_timestamp, start_t_head, start_q_head});
+
+  // Fill the output.
+  out->timestamp_ns = pose_timestamp;
+
+  out->translation = FromVec3(start_t_head);
+  out->orientation = FromQuat(start_q_head);
+
+  out->right_translation = out->translation;
+  out->right_orientation = out->orientation;
+
+  const auto velocity = predictor->PredictVelocity(pose_timestamp);
+
+  out->velocity = FromVec3(velocity.linear);
+  out->angular_velocity = FromVec3(velocity.angular);
+
+  out->flags = DVR_POSE_FLAG_HEAD | DVR_POSE_FLAG_VALID;
+  memset(out->pad, 0, sizeof(out->pad));
+}
+
+void PredictPose(const posepredictor::Predictor* predictor, int64_t left_ns,
+                 int64_t right_ns, DvrPoseAsync* out) {
+  const auto left_pose = predictor->Predict(left_ns);
+  const auto right_pose = predictor->Predict(right_ns);
+  const auto velocity = predictor->PredictVelocity((left_ns + right_ns) / 2);
+
+  // Fill the output.
+  out->timestamp_ns = left_ns;
+
+  out->translation = FromVec3(left_pose.position);
+  out->orientation = FromQuat(left_pose.orientation);
+
+  out->right_translation = FromVec3(right_pose.position);
+  out->right_orientation = FromQuat(right_pose.orientation);
+
+  out->velocity = FromVec3(velocity.linear);
+  out->angular_velocity = FromVec3(velocity.angular);
+
+  out->flags = DVR_POSE_FLAG_HEAD | DVR_POSE_FLAG_VALID;
+  memset(out->pad, 0, sizeof(out->pad));
+}
+
+}  //  dvr
+}  //  android
diff --git a/libs/vr/libposepredictor/include/buffered_predictor.h b/libs/vr/libposepredictor/include/buffered_predictor.h
new file mode 100644
index 0000000..eab0150
--- /dev/null
+++ b/libs/vr/libposepredictor/include/buffered_predictor.h
@@ -0,0 +1,40 @@
+#ifndef POSEPREDICTOR_BUFFERED_PREDICTOR_H_
+#define POSEPREDICTOR_BUFFERED_PREDICTOR_H_
+
+#include <vector>
+
+#include "predictor.h"
+
+namespace posepredictor {
+
+// Keeps the previous n poses around in a ring buffer.
+// The orientations are also unrolled so that a . b > 0 for two subsequent
+// quaternions a and b.
+class BufferedPredictor : public Predictor {
+ public:
+  BufferedPredictor(size_t buffer_size);
+  ~BufferedPredictor() = default;
+
+ protected:
+  // Add a pose sample into the buffer.
+  void BufferSample(const Pose& sample);
+
+  // Grab a previous sample.
+  // index = 0: last sample
+  // index = 1: the one before that
+  // ...
+  const Pose& PrevSample(size_t index) const;
+
+  // Where we keep the last n poses.
+  std::vector<Pose> buffer_;
+
+  // Where the last valid pose is in the buffer.
+  size_t current_pose_index_ = 0;
+
+  // The number of poses we have added.
+  size_t num_poses_added_ = 0;
+};
+
+}  // namespace posepredictor
+
+#endif  // POSEPREDICTOR_BUFFERED_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/linear_predictor.h b/libs/vr/libposepredictor/include/linear_predictor.h
new file mode 100644
index 0000000..0d17ec5
--- /dev/null
+++ b/libs/vr/libposepredictor/include/linear_predictor.h
@@ -0,0 +1,43 @@
+#ifndef POSEPREDICTOR_LINEAR_POSE_PREDICTOR_H_
+#define POSEPREDICTOR_LINEAR_POSE_PREDICTOR_H_
+
+#include "predictor.h"
+
+namespace posepredictor {
+
+// This class makes a linear prediction using the last two samples we received.
+class LinearPosePredictor : public Predictor {
+ public:
+  LinearPosePredictor() = default;
+
+  // Add a new sample.
+  void Add(const Pose& sample) override;
+
+  // Predict using the last two samples.
+  Pose Predict(int64_t time_ns) const override;
+
+  // Just copy the velocity over.
+  Velocity PredictVelocity(int64_t time_ns) const override;
+
+ private:
+  // The index of the last sample we received.
+  size_t current_index_ = 0;
+
+  // The previous two samples.
+  Pose samples_[2];
+
+  // Experimental
+  bool forward_predict_angular_speed_ = false;
+
+  // Transient variables updated when a sample is added.
+  vec3 velocity_ = vec3::Zero();
+  vec3 rotational_velocity_ = vec3::Zero();
+  vec3 rotational_axis_ = vec3::Zero();
+  real last_angular_speed_ = 0;
+  real angular_speed_ = 0;
+  real angular_accel_ = 0;
+};
+
+}  // namespace posepredictor
+
+#endif  // POSEPREDICTOR_LINEAR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/polynomial_predictor.h b/libs/vr/libposepredictor/include/polynomial_predictor.h
new file mode 100644
index 0000000..4b8d51b
--- /dev/null
+++ b/libs/vr/libposepredictor/include/polynomial_predictor.h
@@ -0,0 +1,168 @@
+#ifndef POSEPREDICTOR_POLYNOMIAL_POSE_PREDICTOR_H_
+#define POSEPREDICTOR_POLYNOMIAL_POSE_PREDICTOR_H_
+
+#include <vector>
+
+#include <Eigen/Dense>
+
+#include "buffered_predictor.h"
+
+namespace posepredictor {
+
+// Make a polynomial prediction of the form
+// y = coefficients_[0] + coefficients_[1] * t + coefficients_[2] * t^2 + ...
+// where t is time and y is the position and orientation.
+// We recompute the coefficients whenever we add a new sample using
+// training_window previous samples.
+template <size_t PolynomialDegree, size_t TrainingWindow>
+class PolynomialPosePredictor : public BufferedPredictor {
+ public:
+  PolynomialPosePredictor(real regularization = 1e-9)
+      : BufferedPredictor(TrainingWindow), regularization_(regularization) {
+    static_assert(TrainingWindow >= PolynomialDegree + 1,
+                  "Underconstrained polynomial regressor");
+  }
+
+  ~PolynomialPosePredictor() = default;
+
+  // We convert pose samples into a vector for matrix arithmetic using this
+  // mapping.
+  enum Components {
+    kPositionX = 0,
+    kPositionY,
+    kPositionZ,
+    kOrientationX,
+    kOrientationY,
+    kOrientationZ,
+    kOrientationW,
+    kNumComponents
+  };
+
+  // Add a new sample.
+  void Add(const Pose& sample) override {
+    // Add the sample to the ring buffer.
+    BufferedPredictor::BufferSample(sample);
+
+    Eigen::Matrix<real, TrainingWindow, kNumComponents> values;
+
+    // Get the pose samples into matrices for fitting.
+    real t_vector[TrainingWindow];
+    for (size_t i = 0; i < TrainingWindow; ++i) {
+      const auto& prev_sample = PrevSample(i);
+
+      t_vector[i] = NsToT(prev_sample.time_ns);
+
+      // Save the values we will be fitting to at each sample time.
+      values(i, kPositionX) = prev_sample.position.x();
+      values(i, kPositionY) = prev_sample.position.y();
+      values(i, kPositionZ) = prev_sample.position.z();
+      values(i, kOrientationX) = prev_sample.orientation.x();
+      values(i, kOrientationY) = prev_sample.orientation.y();
+      values(i, kOrientationZ) = prev_sample.orientation.z();
+      values(i, kOrientationW) = prev_sample.orientation.w();
+    }
+
+    // Some transient matrices for solving for coefficient matrix.
+    Eigen::Matrix<real, PolynomialDegree + 1, PolynomialDegree + 1> M;
+    Eigen::Matrix<real, PolynomialDegree + 1, 1> d;
+    Eigen::Matrix<real, PolynomialDegree + 1, 1> p;
+
+    // Create a polynomial fit for each component.
+    for (size_t component = 0; component < kNumComponents; ++component) {
+      // A = [ 1 t t^2 ... ]'
+      // x = [ coefficients[0] coefficients[1] .... ]'
+      // b = [ position.x ]'
+      // We would like to solve A' x + regularization * I = b'
+      // given the samples we have in our training window.
+      //
+      // The loop below will compute:
+      // M = A' * A
+      // d = A' * b
+      // so we can solve M * coefficients + regularization * I = b
+
+      M.setIdentity();
+      d.setZero();
+      p[0] = 1;
+
+      // M = regularization * I
+      M = M * regularization_;
+
+      // Accumulate the poses in the training window.
+      for (size_t i = 0; i < TrainingWindow; ++i) {
+        // Compute the polynomial at this sample.
+        for (size_t j = 1; j <= PolynomialDegree; ++j) {
+          p[j] = p[j - 1] * t_vector[i];
+        }
+
+        // Accumulate the left and right hand sides.
+        M = M + p * p.transpose();
+        d = d + p * values(i, component);
+      }
+
+      // M is symmetric, positive semi-definite.
+      // Note: This is not the most accurate solver out there but is fast.
+      coefficients_.row(component) = Eigen::LLT<Eigen::MatrixXd>(M).solve(d);
+    }
+  }
+
+  // Predict using the polynomial coefficients.
+  Pose Predict(int64_t time_ns) const override {
+    // Predict the left side.
+    const auto components = SamplePolynomial(time_ns);
+
+    return {time_ns,
+            vec3(components[kPositionX], components[kPositionY],
+                 components[kPositionZ]),
+            quat(components[kOrientationW], components[kOrientationX],
+                 components[kOrientationY], components[kOrientationZ])
+                .normalized()};
+  }
+
+ private:
+  // Evaluate the polynomial at a particular time.
+  Eigen::Matrix<real, kNumComponents, 1> SamplePolynomial(
+      int64_t time_ns) const {
+    const auto t = NsToT(time_ns);
+    Eigen::Matrix<real, PolynomialDegree + 1, 1> polynomial;
+    real current_polynomial = t;
+
+    // Compute polynomial = [ 1 t t^2 ... ]
+    polynomial[0] = 1;
+    for (size_t degree = 1; degree <= PolynomialDegree;
+         ++degree, current_polynomial *= t) {
+      polynomial[degree] = polynomial[degree - 1] * t;
+    }
+
+    // The coefficients_ = [ numComponents x (polynomial degree + 1) ].
+    return coefficients_ * polynomial;
+  }
+
+  // Convert a time in nanoseconds to t.
+  // We could use the seconds as t but this would create make it more difficult
+  // to tweak the regularization amount. So we subtract the last sample time so
+  // the scale of the regularization constant doesn't change as a function of
+  // time.
+  real NsToT(int64_t time_ns) const {
+    return NsToSeconds(time_ns - buffer_[current_pose_index_].time_ns);
+  }
+
+  // The ridge regularization constant.
+  real regularization_;
+
+  // This is where we store the polynomial coefficients.
+  Eigen::Matrix<real, kNumComponents, PolynomialDegree + 1> coefficients_;
+};
+
+// Some common polynomial types.
+extern template class PolynomialPosePredictor<1, 2>;
+extern template class PolynomialPosePredictor<2, 3>;
+extern template class PolynomialPosePredictor<3, 4>;
+extern template class PolynomialPosePredictor<4, 5>;
+
+using QuadricPosePredictor = PolynomialPosePredictor<2, 3>;
+using CubicPosePredictor = PolynomialPosePredictor<3, 4>;
+using QuarticPosePredictor = PolynomialPosePredictor<4, 5>;
+
+}  // namespace posepredictor
+
+#endif  // POSEPREDICTOR_POLYNOMIAL_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/predictor.h b/libs/vr/libposepredictor/include/predictor.h
new file mode 100644
index 0000000..78db272
--- /dev/null
+++ b/libs/vr/libposepredictor/include/predictor.h
@@ -0,0 +1,73 @@
+#ifndef POSEPREDICTOR_POSE_PREDICTOR_H_
+#define POSEPREDICTOR_POSE_PREDICTOR_H_
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+// This is the only file you need to include for pose prediction.
+
+namespace posepredictor {
+
+// The precision for the predictor.
+// TODO(okana): double precision is probably not necessary.
+typedef double real;
+
+using vec3 = Eigen::Matrix<real, 3, 1>;
+using quat = Eigen::Quaternion<real>;
+
+// Encapsulates a pose sample.
+struct Pose {
+  int64_t time_ns = 0;
+  vec3 position = vec3::Zero();
+  quat orientation = quat::Identity();
+};
+
+// Encapsulates the derivative at a time.
+struct Velocity {
+  vec3 linear = vec3::Zero();
+  vec3 angular = vec3::Zero();
+};
+
+// The preset types we support.
+enum class PredictorType { Linear, Quadric, Cubic };
+
+// This is an abstract base class for prediction 6dof pose given
+// a set of samples.
+class Predictor {
+ public:
+  Predictor() = default;
+  virtual ~Predictor() = default;
+
+  // The nanoseconds to use for finite differencing.
+  static constexpr int64_t kFiniteDifferenceNs = 100;
+
+  // Instantiate a new pose predictor for a type.
+  static std::unique_ptr<Predictor> Create(PredictorType type);
+
+  // Compute the angular velocity from orientation start_orientation to
+  // end_orientation in delta_time.
+  static vec3 AngularVelocity(const quat& start_orientation,
+                              const quat& end_orientation, real delta_time);
+
+  // Add a pose sample coming from the sensors.
+  virtual void Add(const Pose& sample) = 0;
+
+  // Make a pose prediction for at specific time.
+  virtual Pose Predict(int64_t time_ns) const = 0;
+
+  // Evaluate velocity at a particular time.
+  // The default implementation uses finite differencing.
+  virtual Velocity PredictVelocity(int64_t time_ns) const;
+
+  // Helpers
+  static real NsToSeconds(int64_t time_ns) {
+    return static_cast<real>(time_ns / 1e9);
+  }
+  static int64_t SecondsToNs(real seconds) {
+    return static_cast<int64_t>(seconds * 1e9);
+  }
+};
+
+}  // namespace posepredictor
+
+#endif  // POSEPREDICTOR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/private/dvr/dvr_pose_predictor.h b/libs/vr/libposepredictor/include/private/dvr/dvr_pose_predictor.h
new file mode 100644
index 0000000..bd2dcbc
--- /dev/null
+++ b/libs/vr/libposepredictor/include/private/dvr/dvr_pose_predictor.h
@@ -0,0 +1,25 @@
+#ifndef ANDROID_DVR_POSE_PREDICTOR_H_
+#define ANDROID_DVR_POSE_PREDICTOR_H_
+
+#include <dvr/pose_client.h>
+#include <predictor.h>
+
+// Some shim functions for connecting dvr to pose predictor.
+
+namespace android {
+namespace dvr {
+
+// Feed a pose to the predictor.
+void AddPredictorPose(posepredictor::Predictor* predictor,
+                      const posepredictor::vec3& start_t_head,
+                      const posepredictor::quat& start_q_head,
+                      int64_t pose_timestamp, DvrPoseAsync* out);
+
+// Make a prediction for left and right eyes.
+void PredictPose(const posepredictor::Predictor* predictor, int64_t left_ns,
+                 int64_t right_ns, DvrPoseAsync* out);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/linear_predictor.cpp b/libs/vr/libposepredictor/linear_predictor.cpp
new file mode 100644
index 0000000..6f924dc
--- /dev/null
+++ b/libs/vr/libposepredictor/linear_predictor.cpp
@@ -0,0 +1,70 @@
+#include <linear_predictor.h>
+
+namespace posepredictor {
+
+using AngleAxis = Eigen::AngleAxis<real>;
+
+void LinearPosePredictor::Add(const Pose& sample) {
+  // If we are receiving a new sample, move the index to the next item.
+  // If the time stamp is the same as the last frame, we will just overwrite
+  // it with the new data.
+  if (sample.time_ns != samples_[current_index_].time_ns) {
+    current_index_ ^= 1;
+  }
+
+  // Save the sample.
+  samples_[current_index_] = sample;
+
+  // The previous sample we received.
+  const auto& previous_sample = samples_[current_index_ ^ 1];
+
+  // Ready to compute velocities.
+  const auto pose_delta_time =
+      NsToSeconds(sample.time_ns - previous_sample.time_ns);
+
+  if (pose_delta_time > 0.0) {
+    velocity_ = (sample.position - previous_sample.position) / pose_delta_time;
+    rotational_velocity_ = Predictor::AngularVelocity(
+        previous_sample.orientation, sample.orientation, pose_delta_time);
+  } else {
+    velocity_ = vec3::Zero();
+    rotational_velocity_ = vec3::Zero();
+  }
+
+  // Temporary experiment with acceleration estimate.
+  angular_speed_ = rotational_velocity_.norm();
+  angular_accel_ = 0.0;
+  if (forward_predict_angular_speed_) {
+    angular_accel_ =
+        pose_delta_time > 0.0
+            ? (angular_speed_ - last_angular_speed_) / pose_delta_time
+            : 0.0;
+  }
+  last_angular_speed_ = angular_speed_;
+
+  rotational_axis_ = vec3(0.0, 1.0, 0.0);
+  if (angular_speed_ > 0.0) {
+    rotational_axis_ = rotational_velocity_ / angular_speed_;
+  }
+}
+
+Pose LinearPosePredictor::Predict(int64_t time_ns) const {
+  const auto& sample = samples_[current_index_];
+
+  const auto dt = NsToSeconds(time_ns - sample.time_ns);
+
+  // Temporary forward prediction code.
+  auto angle = angular_speed_ * dt;
+  if (__builtin_expect(forward_predict_angular_speed_, 0)) {
+    angle += 0.5 * angular_accel_ * dt * dt;
+  }
+
+  return {time_ns, sample.position + velocity_ * dt,
+          sample.orientation * quat(AngleAxis(angle, rotational_axis_))};
+}
+
+Velocity LinearPosePredictor::PredictVelocity(int64_t /* time_ns */) const {
+  return {velocity_, rotational_velocity_};
+}
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/linear_predictor_tests.cpp b/libs/vr/libposepredictor/linear_predictor_tests.cpp
new file mode 100644
index 0000000..d94aa2d
--- /dev/null
+++ b/libs/vr/libposepredictor/linear_predictor_tests.cpp
@@ -0,0 +1,170 @@
+#include <gtest/gtest.h>
+
+#include <linear_predictor.h>
+
+namespace posepredictor {
+
+namespace {
+
+// For comparing expected and actual.
+constexpr real kAbsErrorTolerance = 1e-5;
+
+// The default rotation axis we will be using.
+const vec3 kRotationAxis = vec3(1, 4, 3).normalized();
+
+// Linearly interpolate between a and b.
+vec3 lerp(const vec3& a, const vec3& b, real t) { return (b - a) * t + a; }
+
+// Linearly interpolate between two angles and return the resulting rotation as
+// a quaternion (around the kRotationAxis).
+quat qlerp(real angle1, real angle2, real t) {
+  return quat(
+      Eigen::AngleAxis<real>((angle2 - angle1) * t + angle1, kRotationAxis));
+}
+
+// Compare two positions.
+void TestPosition(const vec3& expected, const vec3& actual) {
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_NEAR(expected[i], actual[i], kAbsErrorTolerance);
+  }
+}
+
+// Compare two orientations.
+void TestOrientation(const quat& expected, const quat& actual) {
+  // abs(expected.dot(actual)) > 1-eps
+  EXPECT_GE(std::abs(actual.coeffs().dot(expected.coeffs())), 0.99);
+}
+}
+
+// Test the extrapolation from two samples.
+TEST(LinearPosePredictorTest, Extrapolation) {
+  LinearPosePredictor predictor;
+
+  // We wil extrapolate linearly from [position|orientation] 1 -> 2.
+  const vec3 position1(0, 0, 0);
+  const vec3 position2(1, 2, 3);
+  const real angle1 = M_PI * 0.3;
+  const real angle2 = M_PI * 0.5;
+  const quat orientation1(Eigen::AngleAxis<real>(angle1, kRotationAxis));
+  const quat orientation2(Eigen::AngleAxis<real>(angle2, kRotationAxis));
+  const int64_t t1_ns = 0;           //< First sample time stamp
+  const int64_t t2_ns = 10;          //< The second sample time stamp
+  const int64_t eval_left_ns = 23;   //< The eval time for left
+  const int64_t eval_right_ns = 31;  //< The eval time for right
+  Pose start_pose, end_pose, extrapolated_pose;
+
+  predictor.Add(Pose{
+      .position = position1, .orientation = orientation1, .time_ns = t1_ns});
+
+  predictor.Add(Pose{
+      .position = position2, .orientation = orientation2, .time_ns = t2_ns});
+
+  // Extrapolate from t1 - t2 to eval_[left/right].
+  extrapolated_pose = predictor.Predict(eval_left_ns);
+
+  // The interpolation factors for left and right.
+  const auto left_t = (eval_left_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+  EXPECT_EQ(2.3, left_t);
+
+  TestPosition(lerp(position1, position2, left_t), extrapolated_pose.position);
+
+  TestOrientation(qlerp(angle1, angle2, left_t), extrapolated_pose.orientation);
+
+  extrapolated_pose = predictor.Predict(eval_right_ns);
+
+  const auto right_t =
+      (eval_right_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+  EXPECT_EQ(3.1, right_t);
+
+  TestPosition(lerp(position1, position2, right_t), extrapolated_pose.position);
+
+  TestOrientation(qlerp(angle1, angle2, right_t),
+                  extrapolated_pose.orientation);
+}
+
+// Test three samples, where the last two samples have the same timestamp.
+TEST(LinearPosePredictorTest, DuplicateSamples) {
+  LinearPosePredictor predictor;
+
+  const vec3 position1(0, 0, 0);
+  const vec3 position2(1, 2, 3);
+  const vec3 position3(2, 2, 3);
+  const real angle1 = M_PI * 0.3;
+  const real angle2 = M_PI * 0.5;
+  const real angle3 = M_PI * 0.65;
+  const quat orientation1(Eigen::AngleAxis<real>(angle1, kRotationAxis));
+  const quat orientation2(Eigen::AngleAxis<real>(angle2, kRotationAxis));
+  const quat orientation3(Eigen::AngleAxis<real>(angle3, kRotationAxis));
+  const int64_t t1_ns = 0;
+  const int64_t t2_ns = 10;
+  const int64_t eval_left_ns = 27;
+  const int64_t eval_right_ns = 31;
+  Pose extrapolated_pose;
+
+  predictor.Add(Pose{
+      .position = position1, .orientation = orientation1, .time_ns = t1_ns});
+
+  predictor.Add(Pose{
+      .position = position2, .orientation = orientation2, .time_ns = t2_ns});
+
+  {
+    // Extrapolate from t1 - t2 to eval_[left/right].
+    extrapolated_pose = predictor.Predict(eval_left_ns);
+
+    // The interpolation factors for left and right.
+    const auto left_t =
+        (eval_left_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+
+    // Test the result.
+    TestPosition(lerp(position1, position2, left_t),
+                 extrapolated_pose.position);
+
+    TestOrientation(qlerp(angle1, angle2, left_t),
+                    extrapolated_pose.orientation);
+
+    extrapolated_pose = predictor.Predict(eval_right_ns);
+
+    const auto right_t =
+        (eval_right_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+
+    TestPosition(lerp(position1, position2, right_t),
+                 extrapolated_pose.position);
+
+    TestOrientation(qlerp(angle1, angle2, right_t),
+                    extrapolated_pose.orientation);
+  }
+
+  // Sending a duplicate sample here.
+  predictor.Add(Pose{
+      .position = position3, .orientation = orientation3, .time_ns = t2_ns});
+
+  {
+    // Extrapolate from t1 - t2 to eval_[left/right].
+    extrapolated_pose = predictor.Predict(eval_left_ns);
+
+    // The interpolation factors for left and right.
+    const auto left_t =
+        (eval_left_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+
+    TestPosition(lerp(position1, position3, left_t),
+                 extrapolated_pose.position);
+
+    TestOrientation(qlerp(angle1, angle3, left_t),
+                    extrapolated_pose.orientation);
+
+    extrapolated_pose = predictor.Predict(eval_right_ns);
+
+    const auto right_t =
+        (eval_right_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+
+    // Test the result.
+
+    TestPosition(lerp(position1, position3, right_t),
+                 extrapolated_pose.position);
+
+    TestOrientation(qlerp(angle1, angle3, right_t),
+                    extrapolated_pose.orientation);
+  }
+}
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/polynomial_predictor.cpp b/libs/vr/libposepredictor/polynomial_predictor.cpp
new file mode 100644
index 0000000..98fd28a
--- /dev/null
+++ b/libs/vr/libposepredictor/polynomial_predictor.cpp
@@ -0,0 +1,11 @@
+#include <polynomial_predictor.h>
+
+namespace posepredictor {
+
+// Instantiate the common polynomial types.
+template class PolynomialPosePredictor<1, 2>;
+template class PolynomialPosePredictor<2, 3>;
+template class PolynomialPosePredictor<3, 4>;
+template class PolynomialPosePredictor<4, 5>;
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/polynomial_predictor_tests.cpp b/libs/vr/libposepredictor/polynomial_predictor_tests.cpp
new file mode 100644
index 0000000..88cb2b9
--- /dev/null
+++ b/libs/vr/libposepredictor/polynomial_predictor_tests.cpp
@@ -0,0 +1,120 @@
+#include <gtest/gtest.h>
+
+#include <polynomial_predictor.h>
+
+namespace posepredictor {
+
+namespace {
+
+// For comparing expected and actual.
+constexpr real kAbsErrorTolerance = 1e-5;
+
+// Test the linear extrapolation from two samples.
+TEST(PolynomialPosePredictor, Linear) {
+  // Degree = 1, simple line, passing through two points.
+  // Note the regularization is 0 so we expect the exact fit.
+  PolynomialPosePredictor<1, 2> predictor(0);
+
+  // Add two samples.
+  predictor.Add(
+      Pose{.position = {0, 0, 0}, .orientation = {0, 0, 0, 1}, .time_ns = 0});
+
+  predictor.Add(
+      Pose{.position = {1, 2, 3}, .orientation = {0, 0, 0, 1}, .time_ns = 10});
+
+  Pose predicted_pose;
+
+  predicted_pose = predictor.Predict(20);
+
+  // Check the x,y,z components for the expected translation.
+  EXPECT_NEAR(predicted_pose.position[0], 2, kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[1], 4, kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[2], 6, kAbsErrorTolerance);
+
+  predicted_pose = predictor.Predict(30);
+  EXPECT_NEAR(predicted_pose.position[0], 3, kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[1], 6, kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[2], 9, kAbsErrorTolerance);
+}
+
+// Test the degree two polynomial fit.
+TEST(PolynomialPosePredictor, Quadric) {
+  // Degree = 2, need three samples to fit a polynomial.
+  // Note the regularization is 0 so we expect the exact fit.
+  PolynomialPosePredictor<2, 3> predictor(0);
+
+  // Add three samples.
+  predictor.Add(
+      Pose{.position = {1, 2, 3}, .orientation = {0, 0, 0, 1}, .time_ns = 0});
+
+  predictor.Add(
+      Pose{.position = {0, 0, 0}, .orientation = {0, 0, 0, 1}, .time_ns = 10});
+
+  predictor.Add(
+      Pose{.position = {1, 2, 3}, .orientation = {0, 0, 0, 1}, .time_ns = 20});
+
+  // The expected polynomials for x/y/z.
+
+  // x:  0.01 * t^2 - 0.2 * t + 1
+  const auto x = [](auto t) { return 0.01 * t * t - 0.2 * t + 1; };
+
+  // y:  0.02 * t^2 - 0.4 * t + 2
+  const auto y = [](auto t) { return 0.02 * t * t - 0.4 * t + 2; };
+
+  // z:  0.03 * t^2 - 0.6 * t + 3
+  const auto z = [](auto t) { return 0.03 * t * t - 0.6 * t + 3; };
+
+  Pose predicted_pose;
+  predicted_pose = predictor.Predict(40);
+
+  // Check the x,y,z components for the expected translation.
+  EXPECT_NEAR(predicted_pose.position[0], x(40), kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[1], y(40), kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[2], z(40), kAbsErrorTolerance);
+
+  predicted_pose = predictor.Predict(50);
+  EXPECT_NEAR(predicted_pose.position[0], x(50), kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[1], y(50), kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[2], z(50), kAbsErrorTolerance);
+}
+
+// Test the degree two polynomial fit with degenerate input.
+//
+// The input samples all lie in a line which would normally make our system
+// degenerate. We will rely on the regularization term to recover the linear
+// solution in a quadric predictor.
+TEST(PolynomialPosePredictor, QuadricDegenate) {
+  // Degree = 2, need three samples to fit a polynomial.
+  // Note that we are using the default regularization term here.
+  // We cannot use 0 regularizer since the input is degenerate.
+  PolynomialPosePredictor<2, 3> predictor(1e-20);
+
+  // Add three samples.
+  predictor.Add(
+      Pose{.position = {0, 0, 0}, .orientation = {0, 0, 0, 1}, .time_ns = 0});
+
+  predictor.Add(
+      Pose{.position = {1, 2, 3}, .orientation = {0, 0, 0, 1}, .time_ns = 10});
+
+  predictor.Add(
+      Pose{.position = {2, 4, 6}, .orientation = {0, 0, 0, 1}, .time_ns = 20});
+
+  Pose predicted_pose;
+
+  predicted_pose = predictor.Predict(30);
+
+  // Check the x,y,z components for the expected translation.
+  // We are using a higher error threshold since this is now approximate.
+  EXPECT_NEAR(predicted_pose.position[0], 3, 0.001);
+  EXPECT_NEAR(predicted_pose.position[1], 6, 0.001);
+  EXPECT_NEAR(predicted_pose.position[2], 9, 0.001);
+
+  predicted_pose = predictor.Predict(40);
+  EXPECT_NEAR(predicted_pose.position[0], 4, 0.001);
+  EXPECT_NEAR(predicted_pose.position[1], 8, 0.001);
+  EXPECT_NEAR(predicted_pose.position[2], 12, 0.001);
+}
+
+}  // namespace
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/predictor.cpp b/libs/vr/libposepredictor/predictor.cpp
new file mode 100644
index 0000000..beba156
--- /dev/null
+++ b/libs/vr/libposepredictor/predictor.cpp
@@ -0,0 +1,34 @@
+#include <linear_predictor.h>
+#include <polynomial_predictor.h>
+#include <predictor.h>
+
+namespace posepredictor {
+
+vec3 Predictor::AngularVelocity(const quat& a, const quat& b, real delta_time) {
+  const auto delta_q = a.inverse() * b;
+  // Check that delta_q.w() == 1, Eigen doesn't respect this convention. If
+  // delta_q.w() == -1, we'll get the opposite velocity.
+  return 2.0 * (delta_q.w() < 0 ? static_cast<vec3>(-delta_q.vec()) : delta_q.vec()) / delta_time;
+}
+
+Velocity Predictor::PredictVelocity(int64_t time_ns) const {
+  const auto a = Predict(time_ns - kFiniteDifferenceNs);
+  const auto b = Predict(time_ns + kFiniteDifferenceNs);
+  const auto delta_time = NsToSeconds(2 * kFiniteDifferenceNs);
+
+  return {(b.position - a.position) / delta_time,
+          AngularVelocity(a.orientation, b.orientation, delta_time)};
+}
+
+// The factory method.
+std::unique_ptr<Predictor> Predictor::Create(PredictorType type) {
+  switch (type) {
+    case PredictorType::Linear:
+      return std::make_unique<LinearPosePredictor>();
+    case PredictorType::Quadric:
+      return std::make_unique<QuadricPosePredictor>();
+    case PredictorType::Cubic:
+      return std::make_unique<CubicPosePredictor>();
+  }
+}
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/predictor_tests.cpp b/libs/vr/libposepredictor/predictor_tests.cpp
new file mode 100644
index 0000000..e84a93a
--- /dev/null
+++ b/libs/vr/libposepredictor/predictor_tests.cpp
@@ -0,0 +1,50 @@
+#include <gtest/gtest.h>
+
+#include <predictor.h>
+
+namespace posepredictor {
+
+namespace {
+
+// For comparing expected and actual.
+constexpr real kAbsErrorTolerance = 1e-4;
+
+// Test the angular velocity computation from two orientations.
+TEST(PosePredictor, AngularVelocity) {
+  // Some random rotation axis we will rotate around.
+  const vec3 kRotationAxis = vec3(1, 2, 3).normalized();
+
+  // Some random angle we will be rotating by.
+  const real kRotationAngle = M_PI / 30;
+
+  // Random start orientation we are currently at.
+  const quat kStartOrientation = quat(5, 3, 4, 1).normalized();
+
+  // The orientation we will end up at.
+  const quat kEndOrientation =
+      kStartOrientation *
+      quat(Eigen::AngleAxis<real>(kRotationAngle, kRotationAxis));
+
+  // The delta time for going from start orientation to end.
+  const real kDeltaTime = 1.0;
+
+  // Compute the angular velocity from start orientation to end.
+  const auto angularVelocity = Predictor::AngularVelocity(
+      kStartOrientation, kEndOrientation, kDeltaTime);
+
+  // Extract the axis and the angular speed.
+  const auto angularSpeed = angularVelocity.norm();
+  const auto rotationAxis = angularVelocity.normalized();
+
+  // The speed must match.
+  EXPECT_NEAR(angularSpeed, kRotationAngle / kDeltaTime, kAbsErrorTolerance);
+
+  // The rotation axis must match.
+  EXPECT_NEAR(rotationAxis[0], kRotationAxis[0], kAbsErrorTolerance);
+  EXPECT_NEAR(rotationAxis[1], kRotationAxis[1], kAbsErrorTolerance);
+  EXPECT_NEAR(rotationAxis[2], kRotationAxis[2], kAbsErrorTolerance);
+}
+
+}  // namespace
+
+}  // namespace posepredictor
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..632978b
--- /dev/null
+++ b/libs/vr/libvrflinger/Android.bp
@@ -0,0 +1,86 @@
+// 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",
+    "compositor.cpp",
+    "debug_hud_data.cpp",
+    "debug_hud_view.cpp",
+    "display_manager_service.cpp",
+    "display_service.cpp",
+    "display_surface.cpp",
+    "hardware_composer.cpp",
+    "screenshot_service.cpp",
+    "surface_channel.cpp",
+    "video_compositor.cpp",
+    "video_mesh_surface.cpp",
+    "vr_flinger.cpp",
+    "vsync_service.cpp",
+]
+
+includeFiles = [ "include" ]
+
+staticLibraries = [
+    "libsurfaceflingerincludes",
+    "libhwcomposer-command-buffer",
+    "libbufferhub",
+    "libbufferhubqueue",
+    "libeds",
+    "libdisplay",
+    "libdvrcommon",
+    "libdvrgraphics",
+    "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..5a3aa7f
--- /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, uint64_t /*sequence*/)
+    : 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..1d14a38
--- /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, uint64_t sequence);
+
+  // 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/compositor.cpp b/libs/vr/libvrflinger/compositor.cpp
new file mode 100644
index 0000000..d1d4f45
--- /dev/null
+++ b/libs/vr/libvrflinger/compositor.cpp
@@ -0,0 +1,878 @@
+#include "compositor.h"
+
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+
+#include <memory>
+
+#include <cutils/properties.h>
+
+#include <dvr/graphics.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/device_metrics.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/dummy_native_window.h>
+#include <private/dvr/gl_fenced_flush.h>
+#include <private/dvr/graphics/blur.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/native_buffer.h>
+#include <private/dvr/platform_defines.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "debug_hud_data.h"
+#include "debug_hud_view.h"
+#include "display_surface.h"
+
+#define BINNING_CONTROL_HINT_QCOM 0x8FB0
+
+// Accepted by the <hint> parameter of glHint:
+#define BINNING_QCOM 0x8FB1
+#define VISIBILITY_OPTIMIZED_BINNING_QCOM 0x8FB2
+#define RENDER_DIRECT_TO_FRAMEBUFFER_QCOM 0x8FB3
+
+#ifndef EGL_CONTEXT_MAJOR_VERSION
+#define EGL_CONTEXT_MAJOR_VERSION 0x3098
+#define EGL_CONTEXT_MINOR_VERSION 0x30FB
+#endif
+
+using android::pdx::LocalHandle;
+
+static const int kDistortionMeshResolution = 40;
+
+static std::shared_ptr<int64_t> eds_gpu_duration_ns =
+    std::make_shared<int64_t>(0);
+
+static constexpr char kDisableLensDistortionProp[] =
+    "persist.dvr.disable_distort";
+
+static constexpr char kEnableEdsPoseSaveProp[] =
+    "persist.dvr.save_eds_pose";
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// An implementation of ANativeWindowBuffer backed by a temporary IonBuffer.
+// Do not hold on to this kind of object, because the IonBuffer may become
+// invalid in other scopes.
+class TemporaryNativeBuffer
+    : public ANativeObjectBase<ANativeWindowBuffer, TemporaryNativeBuffer,
+                               LightRefBase<TemporaryNativeBuffer>> {
+ public:
+  explicit TemporaryNativeBuffer(const IonBuffer* buffer) : BASE() {
+    ANativeWindowBuffer::width = buffer->width();
+    ANativeWindowBuffer::height = buffer->height();
+    ANativeWindowBuffer::stride = buffer->stride();
+    ANativeWindowBuffer::format = buffer->format();
+    ANativeWindowBuffer::usage = buffer->usage();
+    // TODO(eieio): Update NYC to support layer_count.
+    // ANativeWindowBuffer::layer_count = 1;
+    handle = buffer->handle();
+  }
+
+ private:
+  friend class android::LightRefBase<TemporaryNativeBuffer>;
+
+  TemporaryNativeBuffer(const TemporaryNativeBuffer&) = delete;
+  void operator=(TemporaryNativeBuffer&) = delete;
+};
+
+std::vector<uint8_t> ReadTextureRGBA(GLuint texture_id, int width, int height) {
+  std::vector<uint8_t> data(width * height * 4);
+  GLuint fbo;
+  glGenFramebuffers(1, &fbo);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         texture_id, 0);
+  // Using default GL_PACK_ALIGNMENT of 4 for the 4 byte source data.
+  glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  glDeleteFramebuffers(1, &fbo);
+  CHECK_GL();
+  return data;
+}
+
+}  // namespace
+
+class Compositor::Texture {
+ public:
+  Texture(std::shared_ptr<BufferConsumer> consumer, EGLDisplay display,
+          int index);
+  ~Texture();
+
+  std::shared_ptr<BufferConsumer> consumer() const { return consumer_; }
+  GLuint texture_id() const { return texture_id_; }
+  vec2i size() const {
+    return vec2i(native_buffer_.get()->width, native_buffer_.get()->height);
+  }
+  int index() const { return index_; }
+
+  bool Initialize();
+
+ private:
+  Texture(const Texture&) = delete;
+  void operator=(const Texture&) = delete;
+
+  std::shared_ptr<BufferConsumer> consumer_;
+
+  android::sp<NativeBufferConsumer> native_buffer_;
+
+  EGLDisplay display_;
+  EGLImageKHR image_;
+  GLuint texture_id_;
+  int index_;
+};
+
+Compositor::Texture::Texture(std::shared_ptr<BufferConsumer> consumer,
+                             EGLDisplay display, int index)
+    : consumer_(consumer),
+      display_(display),
+      image_(nullptr),
+      texture_id_(0),
+      index_(index) {}
+
+Compositor::Texture::~Texture() {
+  glDeleteTextures(1, &texture_id_);
+  eglDestroyImageKHR(display_, image_);
+}
+
+bool Compositor::Texture::Initialize() {
+  native_buffer_ = new NativeBufferConsumer(consumer_, index_);
+
+  CHECK_GL();
+  image_ = eglCreateImageKHR(
+      display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer_.get()), nullptr);
+  if (!image_) {
+    ALOGE("Failed to create EGLImage\n");
+    return false;
+  }
+
+  glGenTextures(1, &texture_id_);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, texture_id_);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image_);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  CHECK_GL();
+  return true;
+}
+
+Compositor::RenderTarget::RenderTarget()
+    : buffer_texture_id_(0),
+      buffer_framebuffer_id_(0),
+      buffer_image_(nullptr) {}
+
+Compositor::RenderTarget::~RenderTarget() { Destroy(); }
+
+void Compositor::RenderTarget::Destroy() {
+  glDeleteFramebuffers(1, &buffer_framebuffer_id_);
+  glDeleteTextures(1, &buffer_texture_id_);
+  eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), buffer_image_);
+  buffer_texture_id_ = 0;
+  buffer_framebuffer_id_ = 0;
+  buffer_image_ = nullptr;
+}
+
+void Compositor::RenderTarget::Initialize(int width, int height) {
+  LOG_ALWAYS_FATAL_IF(buffer_texture_id_ || buffer_framebuffer_id_ ||
+                      buffer_image_);
+  constexpr int usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_COMPOSER |
+                        GRALLOC_USAGE_HW_RENDER |
+                        GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION;
+  buffer_ = std::make_shared<IonBuffer>(width, height,
+                                        HAL_PIXEL_FORMAT_RGBA_8888, usage);
+
+  native_buffer_ = new NativeBuffer(buffer_);
+
+  buffer_image_ = eglCreateImageKHR(
+      eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
+      EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer_.get()), nullptr);
+
+  glGenTextures(1, &buffer_texture_id_);
+  glBindTexture(GL_TEXTURE_2D, buffer_texture_id_);
+  CHECK_GL();
+
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer_image_);
+  CHECK_GL();
+
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glBindTexture(GL_TEXTURE_2D, 0);
+
+  // Generate a framebuffer.
+  glGenFramebuffers(1, &buffer_framebuffer_id_);
+  glBindFramebuffer(GL_FRAMEBUFFER, buffer_framebuffer_id_);
+  CHECK_GL();
+
+  // Attach the color buffer
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         buffer_texture_id_, 0);
+  CHECK_GL();
+  GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+  CHECK_GL();
+  if (result != GL_FRAMEBUFFER_COMPLETE) {
+    ALOGE("Framebuffer incomplete: %d", result);
+  }
+
+  // Clear the render target to black once. In direct render mode we never draw
+  // the corner pixels.
+  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+  glClear(GL_COLOR_BUFFER_BIT);
+  glFlush();
+
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  CHECK_GL();
+}
+
+void Compositor::RenderTarget::BindFramebuffer() {
+  glBindFramebuffer(GL_FRAMEBUFFER, buffer_framebuffer_id_);
+}
+
+void Compositor::RenderTarget::DiscardColorAttachment() {
+  GLenum attachment = GL_COLOR_ATTACHMENT0;
+  glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachment);
+  CHECK_GL();
+}
+
+class Compositor::RenderPoseBufferObject {
+ public:
+  RenderPoseBufferObject(LocalHandle&& render_pose_buffer_fd) :
+      fd_(std::move(render_pose_buffer_fd)) {
+    // Create new pose tracking buffer for this surface.
+    glGenBuffers(1, &render_pose_buffer_object_);
+    glBindBuffer(GL_UNIFORM_BUFFER, render_pose_buffer_object_);
+    if (fd_) {
+      LOG_ALWAYS_FATAL_IF(!glBindSharedBufferQCOM);
+      if (glBindSharedBufferQCOM)
+        glBindSharedBufferQCOM(GL_UNIFORM_BUFFER,
+                               sizeof(DisplaySurfaceMetadata),
+                               fd_.Get());
+      else
+        ALOGE("Error: Missing gralloc buffer extension");
+      CHECK_GL();
+    }
+    glBindBuffer(GL_UNIFORM_BUFFER, 0);
+  }
+
+  ~RenderPoseBufferObject() { glDeleteBuffers(1, &render_pose_buffer_object_); }
+
+  GLuint object_id() const { return render_pose_buffer_object_; }
+
+ private:
+  // Render pose buffer object. This contains an array of poses that corresponds
+  // with the surface buffers.
+  GLuint render_pose_buffer_object_;
+  LocalHandle fd_;
+
+  RenderPoseBufferObject(const RenderPoseBufferObject&) = delete;
+  void operator=(const RenderPoseBufferObject&) = delete;
+};
+
+HeadMountMetrics CreateDefaultHeadMountMetrics() {
+  const bool enable_distortion =
+      property_get_bool(kDisableLensDistortionProp, 0) == 0;
+  return enable_distortion ? CreateHeadMountMetrics()
+                           : CreateUndistortedHeadMountMetrics();
+}
+
+Compositor::Compositor()
+    : head_mount_metrics_(CreateDefaultHeadMountMetrics()),
+      display_(0),
+      config_(0),
+      surface_(0),
+      context_(0),
+      active_render_target_(0),
+      is_render_direct_(false),
+      compute_fbo_(0),
+      compute_fbo_texture_(0),
+      hmd_metrics_requires_update_(false),
+      eds_pose_capture_enabled_(false) {}
+
+Compositor::~Compositor() {}
+
+bool Compositor::Initialize(const DisplayMetrics& display_metrics) {
+  ATRACE_NAME("Compositor::Initialize");
+  if (!InitializeEGL())
+    return false;
+
+  display_metrics_ = display_metrics;
+  const int width = display_metrics_.GetSizePixels().x();
+  const int height = display_metrics_.GetSizePixels().y();
+
+  render_target_[0].Initialize(width, height);
+  render_target_[1].Initialize(width, height);
+
+  // EDS:
+  GpuProfiler::Get()->SetEnableGpuTracing(true);
+
+  eds_pose_capture_enabled_ = property_get_bool(kEnableEdsPoseSaveProp, 0) == 1;
+
+  CheckAndUpdateHeadMountMetrics(true);
+
+  debug_hud_.reset(new DebugHudView(*composite_hmd_.get()));
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  return true;
+}
+
+void Compositor::UpdateHeadMountMetrics(
+    const HeadMountMetrics& head_mount_metrics) {
+  // Recalculating the mesh must be done in the draw loop, defer until then.
+  std::lock_guard<std::mutex> _lock(mutex_);
+  head_mount_metrics_ = head_mount_metrics;
+  hmd_metrics_requires_update_ = true;
+}
+
+void Compositor::CheckAndUpdateHeadMountMetrics(bool force_update) {
+  std::lock_guard<std::mutex> _lock(mutex_);
+  if (hmd_metrics_requires_update_ || force_update) {
+    hmd_metrics_requires_update_ = false;
+    composite_hmd_.reset(
+        new CompositeHmd(head_mount_metrics_, display_metrics_));
+    CHECK_GL();
+    eds_renderer_.reset(new DistortionRenderer(
+        *composite_hmd_.get(), display_metrics_.GetSizePixels(),
+        kDistortionMeshResolution, true, false, false, true, true));
+  }
+}
+
+bool Compositor::InitializeEGL() {
+  ATRACE_NAME("Compositor::InitializeEGL");
+  display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  if (!display_) {
+    ALOGE("Failed to get egl display\n");
+    return false;
+  }
+
+  eglInitialize(display_, nullptr, nullptr);
+
+  EGLint attribs[] = {
+      EGL_BUFFER_SIZE,
+      32,
+      EGL_ALPHA_SIZE,
+      0,
+      EGL_BLUE_SIZE,
+      8,
+      EGL_RED_SIZE,
+      8,
+      EGL_GREEN_SIZE,
+      8,
+      EGL_DEPTH_SIZE,
+      0,
+      EGL_SURFACE_TYPE,
+      EGL_WINDOW_BIT,
+      EGL_RENDERABLE_TYPE,
+      EGL_OPENGL_ES2_BIT,
+      EGL_NONE,
+  };
+
+  EGLint num_configs;
+  if (!eglChooseConfig(display_, attribs, &config_, 1, &num_configs)) {
+    ALOGE("Couldn't find config");
+    return false;
+  }
+
+  std::unique_ptr<DummyNativeWindow> window(new DummyNativeWindow());
+
+  surface_ = eglCreateWindowSurface(display_, config_, window.get(), nullptr);
+  if (surface_ == EGL_NO_SURFACE) {
+    ALOGE("Failed to create egl surface");
+    return false;
+  }
+  window.release();
+
+  EGLint context_attribs[] = {EGL_CONTEXT_MAJOR_VERSION,
+                              3,
+                              EGL_CONTEXT_MINOR_VERSION,
+                              1,
+                              EGL_CONTEXT_PRIORITY_LEVEL_IMG,
+                              EGL_CONTEXT_PRIORITY_HIGH_IMG,
+                              EGL_NONE};
+  context_ = eglCreateContext(display_, config_, nullptr, context_attribs);
+  if (!eglMakeCurrent(display_, surface_, surface_, context_)) {
+    ALOGE("Unable to create GLESv2 context");
+    return false;
+  }
+
+  load_gl_extensions();
+  GpuProfiler::Get()->OnGlContextCreated();
+
+  glEnable(BINNING_CONTROL_HINT_QCOM);
+  glHint(BINNING_CONTROL_HINT_QCOM, RENDER_DIRECT_TO_FRAMEBUFFER_QCOM);
+  is_render_direct_ = true;
+  CHECK_GL();
+
+  // Initialize the placeholder 1x1 framebuffer that we bind during compute
+  // shader instances to avoid accesses to other framebuffers.
+  glGenFramebuffers(1, &compute_fbo_);
+  glGenTextures(1, &compute_fbo_texture_);
+  glBindFramebuffer(GL_FRAMEBUFFER, compute_fbo_);
+  glBindTexture(GL_TEXTURE_2D, compute_fbo_texture_);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+               nullptr);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         compute_fbo_texture_, 0);
+  CHECK_GL();
+  CHECK_GL_FBO();
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+  return true;
+}
+
+void Compositor::Shutdown() {
+  glFinish();
+  render_target_[0].Destroy();
+  render_target_[1].Destroy();
+  layers_.clear();
+  glDeleteFramebuffers(1, &compute_fbo_);
+  glDeleteTextures(1, &compute_fbo_texture_);
+
+  debug_hud_.reset();
+  eds_renderer_.reset();
+
+  if (context_) {
+    GpuProfiler::Get()->OnGlContextDestroyed();
+    eglDestroyContext(display_, context_);
+    context_ = 0;
+  }
+
+  if (surface_ != EGL_NO_SURFACE) {
+    eglDestroySurface(display_, surface_);
+    surface_ = EGL_NO_SURFACE;
+  }
+}
+
+void Compositor::RemoveAllBuffers() { layers_.clear(); }
+
+void Compositor::UpdateSurfaces(
+    const std::vector<std::shared_ptr<DisplaySurface>>& surfaces) {
+  // Delete the removed surfaces.
+  layers_.erase(
+      std::remove_if(layers_.begin(), layers_.end(),
+                     [&surfaces](const AppFrame& layer) {
+                       for (const auto& surface : surfaces)
+                         if (surface->surface_id() == layer.surface_id())
+                           return false;
+                       return true;
+                     }),
+      layers_.end());
+  // New surfaces are added on-demand as buffers are posted.
+}
+
+Compositor::AppFrame::AppFrame()
+    : surface_id_(-1),
+      blur_(0.0f),
+      z_order_(0),
+      vertical_flip_(false),
+      enable_cac_(true),
+      render_buffer_index_(0) {}
+
+Compositor::AppFrame::~AppFrame() {}
+
+const Compositor::Texture* Compositor::AppFrame::GetGlTextureId(
+    EGLDisplay display, int index) {
+  auto buffer_consumer = buffer_.buffer();
+  if (!buffer_consumer) {
+    return nullptr;
+  }
+  auto texture_it = std::find_if(
+      textures_.begin(), textures_.end(),
+      [buffer_consumer, index](const std::shared_ptr<Texture>& t) {
+        return t->consumer() == buffer_consumer && t->index() == index;
+      });
+
+  if (texture_it != textures_.end()) {
+    return (*texture_it).get();
+  }
+
+  textures_.push_back(
+      std::make_shared<Texture>(buffer_consumer, display, index));
+  if (!textures_.back()->Initialize()) {
+    textures_.pop_back();
+    return nullptr;
+  }
+  return textures_.back().get();
+}
+
+bool Compositor::AppFrame::UpdateSurface(
+    const std::shared_ptr<DisplaySurface>& surface) {
+  int surface_id = surface->surface_id();
+  float blur = surface->manager_blur();
+  bool need_sort = false;
+  if (z_order_ != surface->layer_order()) {
+    need_sort = true;
+    z_order_ = surface->layer_order();
+  }
+
+  surface_id_ = surface_id;
+  if (!render_pose_buffer_object_) {
+    render_pose_buffer_object_.reset(
+        new RenderPoseBufferObject(surface->GetMetadataBufferFd()));
+  }
+
+  blur_ = blur;
+  vertical_flip_ =
+      !!(surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP);
+  enable_cac_ =
+      !(surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC);
+
+  AcquiredBuffer skipped_buffer;
+  AcquiredBuffer buffer =
+      surface->AcquireNewestAvailableBuffer(&skipped_buffer);
+  if (!skipped_buffer.IsEmpty()) {
+    DebugHudData::data.SkipLayerFrame(z_order_);
+    ATRACE_NAME("DropToCatchUp");
+    ATRACE_ASYNC_END("BufferPost", skipped_buffer.buffer()->id());
+  }
+  if (!buffer.IsEmpty()) {
+    DebugHudData::data.AddLayerFrame(z_order_);
+    // Buffer was already ready, so we don't need to wait on the fence.
+    buffer.ClaimAcquireFence().Close();
+    ATRACE_ASYNC_END("BufferPost", buffer.buffer()->id());
+
+    render_buffer_index_ = surface->GetRenderBufferIndex(buffer.buffer()->id());
+
+#ifdef TRACE
+    const volatile DisplaySurfaceMetadata* data =
+        surface->GetMetadataBufferPtr();
+#endif
+    ALOGE_IF(TRACE, "read pose index %d %f %f", render_buffer_index_,
+             data->orientation[render_buffer_index_][0],
+             data->orientation[render_buffer_index_][1]);
+
+    // Move the new buffer over the old. AcquiredBuffer releases the old one.
+    buffer_ = std::move(buffer);
+  }
+  return need_sort;
+}
+
+void Compositor::AppFrame::UpdateVideoMeshSurface(
+    const std::shared_ptr<DisplaySurface>& surface) {
+  // Update |video_compositors_| with |video_surface|. Note that
+  // |UpdateVideoMeshSurface| should only be called on the PostThread before
+  // |DrawFrame| is called. Thus, no synchronization is required for
+  // |video_compositors_|.
+  if (!surface->video_mesh_surfaces_updated())
+    return;
+
+  // TODO(jwcai) The following loop handles adding new surfaces; video mesh
+  // removal logic shall be handled by listening to |OnChannelClose| event from
+  // DisplayService.
+  for (const auto& video_surface : surface->GetVideoMeshSurfaces()) {
+    // Here we assume number of |video_surface|s is relatively small, thus, the
+    // merge should be efficient enough.
+    auto video_compositor_it = std::find_if(
+        video_compositors_.begin(), video_compositors_.end(),
+        [video_surface](const std::shared_ptr<VideoCompositor>& c) {
+          return c->surface_id() == video_surface->surface_id();
+        });
+
+    if (video_compositor_it == video_compositors_.end()) {
+      // This video surface is new, create a new VideoCompositor.
+      video_compositors_.push_back(std::make_shared<VideoCompositor>(
+          video_surface, surface->GetMetadataBufferPtr()));
+    } else {
+      // There is a compositor in |video_compositors_| is already set up for
+      // this |video_surface|.
+      ALOGW("Duplicated video_mesh_surface: surface_id=%d",
+            video_surface->surface_id());
+    }
+  }
+}
+
+void Compositor::AppFrame::ResetBlurrers() { blurrers_.clear(); }
+
+void Compositor::AppFrame::AddBlurrer(Blur* blurrer) {
+  blurrers_.emplace_back(blurrer);
+}
+
+void Compositor::PostBuffer(const std::shared_ptr<DisplaySurface>& surface) {
+  int surface_id = surface->surface_id();
+
+  ALOGD_IF(TRACE, "Post surface %d", surface_id);
+
+  auto layer_it = std::find_if(layers_.begin(), layers_.end(),
+                               [surface_id](const AppFrame& frame) {
+                                 return frame.surface_id() == surface_id;
+                               });
+
+  bool need_sort = false;
+  if (layer_it == layers_.end()) {
+    layers_.push_back(AppFrame());
+    layer_it = layers_.end() - 1;
+    need_sort = true;
+  }
+
+  need_sort |= layer_it->UpdateSurface(surface);
+  layer_it->UpdateVideoMeshSurface(surface);
+
+  if (need_sort) {
+    std::stable_sort(layers_.begin(), layers_.end());
+  }
+}
+
+std::vector<uint8_t> Compositor::ReadLayerPixels(size_t index, int* width,
+                                                 int* height) {
+  if (index >= layers_.size()) {
+    return {};
+  }
+
+  const Texture* texture = layers_[index].GetGlTextureId(display_, 0);
+  if (!texture) {
+    return {};
+  }
+
+  *width = texture->size()[0];
+  *height = texture->size()[1];
+  return ReadTextureRGBA(texture->texture_id(), *width, *height);
+}
+
+std::vector<uint8_t> Compositor::ReadBufferPixels(const IonBuffer* buffer) {
+  android::sp<TemporaryNativeBuffer> native_buffer =
+      new TemporaryNativeBuffer(buffer);
+
+  // Finish to make sure the GL driver has completed drawing of prior FBOs.
+  // Since we are creating an EGL image here, the driver will not know that
+  // there is a dependency on earlier GL draws.
+  glFinish();
+
+  EGLImageKHR image = eglCreateImageKHR(
+      display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer.get()), nullptr);
+  if (!image) {
+    ALOGE("Failed to create EGLImage\n");
+    return {};
+  }
+
+  GLuint texture_id;
+  glGenTextures(1, &texture_id);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, texture_id);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+
+  int width = buffer->width();
+  int height = buffer->height();
+  std::vector<uint8_t> data = ReadTextureRGBA(texture_id, width, height);
+
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glDeleteTextures(1, &texture_id);
+  eglDestroyImageKHR(display_, image);
+  return data;
+}
+
+bool Compositor::DrawFrame(uint32_t target_vsync_count,
+                           LocalHandle* buffer_fence_fd) {
+  CheckAndUpdateHeadMountMetrics(false);
+
+  ATRACE_NAME("Compositor::DrawFrame");
+  GpuProfiler::Get()->PollGlTimerQueries();
+
+  if (buffer_fence_fd)
+    buffer_fence_fd->Close();
+
+  int num_layers = 0;
+  const int kMaxLayers = 4;
+  GLuint texture_id[2][kMaxLayers] = {{0}};
+  GLuint render_pose_buffer_id[kMaxLayers] = {0};
+  uint32_t render_buffer_index[kMaxLayers] = {0};
+  bool vertical_flip[kMaxLayers] = {false};
+  bool separate_eye_textures[kMaxLayers] = {false};
+  bool enable_cac[kMaxLayers] = {};
+  CHECK_GL();
+  for (auto& layer : layers_) {
+    if (!layer.buffer().buffer()) {
+      ATRACE_NAME("no_buffer");
+      continue;
+    }
+
+    // Extract surface parameters.
+    render_buffer_index[num_layers] = layer.render_buffer_index();
+    render_pose_buffer_id[num_layers] =
+        layer.render_pose_buffer_object()->object_id();
+    vertical_flip[num_layers] = layer.vertical_flip();
+    enable_cac[num_layers] =
+        head_mount_metrics_.supports_chromatic_aberration_correction() &&
+        layer.enable_cac();
+
+    // Extract per-eye textures. These may be separate or joined (atlased).
+    vec2i size(0, 0);
+    int view_count = layer.buffer().buffer()->slice_count();
+    ALOGE_IF(view_count > 2, "Error: more than 2 views not supported");
+    view_count = std::min(2, view_count);
+    separate_eye_textures[num_layers] = (view_count > 1);
+    bool is_missing_texture = false;
+    for (int eye = 0; eye < 2; ++eye) {
+      // If view_count is 1, each eye texture is the 0th.
+      int view_index = (view_count == 2) ? eye : 0;
+      const Texture* texture = layer.GetGlTextureId(display_, view_index);
+      // Texture will be null if the EGL image creation fails (hopefully never).
+      if (!texture) {
+        is_missing_texture = true;
+        break;
+      }
+      // All views are currently expected to have the same size.
+      size = texture->size();
+      texture_id[eye][num_layers] = texture->texture_id();
+    }
+    if (is_missing_texture) {
+      continue;
+    }
+
+    // Perform blur if requested.
+    if (fabs(layer.blur()) > 0.001f) {
+      // No need for CAC on blurred layers.
+      enable_cac[num_layers] = false;
+      if (layer.blurrer_count() < 1 || layer.blurrer(0)->width() != size[0] ||
+          layer.blurrer(0)->height() != size[1]) {
+        // Blur is created with the left eye texture, but the same instance
+        // can be used for the right eye as well.
+        layer.ResetBlurrers();
+        layer.AddBlurrer(new Blur(size[0], size[1], texture_id[0][num_layers],
+                                  GL_TEXTURE_2D, GL_TEXTURE_2D, true, display_,
+                                  view_count));
+      }
+      // Reset blur instances to prepare for drawing.
+      layer.blurrer(0)->StartFrame();
+      layer.blurrer(0)->set_scale(layer.blur());
+      // Perform blur and replace source texture with blurred output texture.
+      if (view_count == 1) {
+        // Single wide buffer for both eyes, blur both eyes in one operation.
+        texture_id[0][num_layers] = texture_id[1][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[0][num_layers]);
+      } else {
+        // Split eye buffers in a single frame, blur each framebuffer.
+        texture_id[0][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[0][num_layers]);
+        texture_id[1][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[1][num_layers]);
+      }
+    }
+
+    ++num_layers;
+    if (num_layers >= kMaxLayers)
+      break;
+  }
+
+  CHECK_GL();
+  // Set appropriate binning mode for the number of layers.
+  if (num_layers > 1 && is_render_direct_) {
+    is_render_direct_ = false;
+    glDisable(BINNING_CONTROL_HINT_QCOM);
+  } else if (num_layers <= 1 && !is_render_direct_) {
+    is_render_direct_ = true;
+    glEnable(BINNING_CONTROL_HINT_QCOM);
+    glHint(BINNING_CONTROL_HINT_QCOM, RENDER_DIRECT_TO_FRAMEBUFFER_QCOM);
+  }
+
+  // Workaround for GL driver bug that causes the currently bound FBO to be
+  // accessed during a compute shader pass (DoLateLatch below). Based on an
+  // analysis with systrace, the best pattern here was to run the compute shader
+  // with a *different* FBO than what will be drawn to afterward. So we bind
+  // a dummy 1x1 FBO here and discard it. If instead, the current render target
+  // is bound during the compute shader, the following draw calls will be forced
+  // into direct mode rendering.
+  glBindFramebuffer(GL_FRAMEBUFFER, compute_fbo_);
+  GLenum attachment = GL_COLOR_ATTACHMENT0;
+  glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachment);
+
+  // Double buffer the render target.  Get the render target we're drawing into,
+  // and update the active buffer to the next buffer.
+  RenderTarget& render_target = GetRenderTarget();
+  SetNextRenderTarget();
+
+  if (num_layers > 0) {
+    // This trace prints the EDS+Warp GPU overhead and prints every 5 seconds:
+    TRACE_GPU_PRINT("GPU EDS+Warp", 5 * 60);
+    CHECK_GL();
+    eds_renderer_->DoLateLatch(target_vsync_count, render_buffer_index,
+                               render_pose_buffer_id, vertical_flip,
+                               separate_eye_textures, num_layers);
+
+    render_target.BindFramebuffer();
+
+    // Discard to avoid unresolving the framebuffer during tiled rendering.
+    render_target.DiscardColorAttachment();
+
+    // For tiled mode rendering, we clear every frame to avoid garbage showing
+    // up in the parts of tiles that are not rendered.
+    if (!is_render_direct_) {
+      glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+      glClear(GL_COLOR_BUFFER_BIT);
+    }
+
+    for (int eye = kLeftEye; eye <= kRightEye; ++eye) {
+      eds_renderer_->PrepGlState(static_cast<EyeType>(eye));
+      for (int layer_i = 0; layer_i < num_layers; ++layer_i) {
+        bool blend_with_previous = layer_i > 0;
+        uint32_t current_buffer_index = render_buffer_index[layer_i];
+
+        // Render video mesh in the background of each graphics layer.
+        layers_[layer_i].ForEachVideoCompositor([this, eye, layer_i,
+                                                 current_buffer_index,
+                                                 &blend_with_previous](
+            const std::shared_ptr<VideoCompositor>& video_compositor) mutable {
+          eds_renderer_->DrawVideoQuad(
+              static_cast<EyeType>(eye), layer_i,
+              video_compositor->GetActiveTextureId(display_),
+              video_compositor->GetTransform(eye, current_buffer_index));
+          blend_with_previous = true;
+        });
+
+        // Apply distortion to frame submitted from the app's GL context.
+        eds_renderer_->SetChromaticAberrationCorrectionEnabled(
+            enable_cac[layer_i]);
+        eds_renderer_->ApplyDistortionCorrectionToTexture(
+            static_cast<EyeType>(eye), &texture_id[eye][layer_i],
+            &vertical_flip[layer_i], &separate_eye_textures[layer_i], &layer_i,
+            1, blend_with_previous, false);
+      }
+    }
+    eds_renderer_->ResetGlState(1);
+    CHECK_GL();
+  } else {
+    ALOGI("No buffers for compositing, clearing to black.");
+    render_target.BindFramebuffer();
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+  }
+
+  debug_hud_->Update();
+  debug_hud_->Draw();
+
+  LocalHandle fence_fd = CreateGLSyncAndFlush(display_);
+
+  if (buffer_fence_fd)
+    *buffer_fence_fd = std::move(fence_fd);
+
+  if (eds_pose_capture_enabled_) {
+    std::lock_guard<std::mutex> _lock(mutex_);
+    eds_renderer_->GetLastEdsPose(&eds_pose_capture_);
+  }
+
+  return true;
+}
+
+bool Compositor::GetLastEdsPose(LateLatchOutput* out_data) {
+  if (eds_pose_capture_enabled_) {
+    std::lock_guard<std::mutex> _lock(mutex_);
+    *out_data = eds_pose_capture_;
+    return true;
+  } else {
+    ALOGE("Eds pose capture is not enabled.");
+    return false;
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/compositor.h b/libs/vr/libvrflinger/compositor.h
new file mode 100644
index 0000000..be26d31
--- /dev/null
+++ b/libs/vr/libvrflinger/compositor.h
@@ -0,0 +1,233 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_COMPOSITOR_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_COMPOSITOR_H_
+
+#include <EGL/egl.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <vector>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/display_metrics.h>
+#include <private/dvr/distortion_renderer.h>
+#include <private/dvr/frame_time_history.h>
+#include <private/dvr/ion_buffer.h>
+#include <private/dvr/native_buffer.h>
+
+#include "acquired_buffer.h"
+#include "video_compositor.h"
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class Blur;
+class BufferConsumer;
+class CompositeHmd;
+class DebugHudView;
+class DisplaySurface;
+
+// This is a GPU compositor for software EDS and lens warp on buffers provided
+// by HardwareComposer.
+class Compositor {
+ public:
+  Compositor();
+  ~Compositor();
+
+  bool Initialize(const DisplayMetrics& display_metrics);
+  void UpdateHeadMountMetrics(const HeadMountMetrics& head_mount_metrics);
+  void Shutdown();
+
+  // Renders a frame with the latest buffers with EDS and warp applied.
+  // buffer_fence_fd can be used to get a fence for the rendered frame. It can
+  // be set to null if the fence isn't needed.
+  bool DrawFrame(uint32_t target_vsync_count,
+                 pdx::LocalHandle* buffer_fence_fd);
+
+  // Remove all buffers.
+  void RemoveAllBuffers();
+
+  // Synchronize compositor layers with in given surfaces.
+  void UpdateSurfaces(
+      const std::vector<std::shared_ptr<DisplaySurface>>& surfaces);
+
+  // This must be called for each surface before DrawFrame is called.
+  void PostBuffer(const std::shared_ptr<DisplaySurface>& surface);
+
+  std::shared_ptr<IonBuffer> GetBuffer() const {
+    return render_target_[active_render_target_].buffer();
+  }
+
+  // Returns the number of layers being rendered by the compositor.
+  size_t GetLayerCount() const { return layers_.size(); }
+
+  // Returns the source buffer at the given layer index or nullptr if none is
+  // available.
+  std::shared_ptr<BufferConsumer> PeekAtLayer(size_t index) const {
+    if (index >= GetLayerCount())
+      return nullptr;
+    return layers_[index].buffer().buffer();
+  }
+
+  // Expensive operation to transfer the pixels of the given layer index into
+  // unformatted memory and return as a RGBA buffer.
+  // On success, returns non-zero sized vector and sets width and height.
+  // On failure, returns empty vector.
+  std::vector<uint8_t> ReadLayerPixels(size_t index, int* width, int* height);
+
+  // Expensive operation to transfer the pixels of the given buffer into
+  // unformatted memory and return as a RGBA buffer.
+  // On success, returns non-zero sized vector.
+  // On failure, returns empty vector.
+  std::vector<uint8_t> ReadBufferPixels(const IonBuffer* buffer);
+
+  bool GetLastEdsPose(LateLatchOutput* out_data);
+
+  const HeadMountMetrics& head_mount_metrics() const {
+    return head_mount_metrics_;
+  }
+
+ private:
+  class Texture;
+  class RenderPoseBufferObject;
+
+  // A rendered frame from an application.
+  class AppFrame {
+   public:
+    AppFrame();
+    ~AppFrame();
+
+    AppFrame(AppFrame&& other) = default;
+    AppFrame& operator=(AppFrame&&) = default;
+
+    // Gets a GL texture object for the current buffer. The resulting texture
+    // object will be cached for future calls. Returns a pointer for temporary
+    // access - not meant to hold on to.
+    const Texture* GetGlTextureId(EGLDisplay display, int index);
+
+    bool operator<(const AppFrame& rhs) const {
+      return z_order_ < rhs.z_order_;
+    }
+    int z_order() const { return z_order_; }
+    // Return true if this surface z order has been changed.
+    bool UpdateSurface(const std::shared_ptr<DisplaySurface>& surface);
+    void UpdateVideoMeshSurface(const std::shared_ptr<DisplaySurface>& surface);
+    void ResetBlurrers();
+    void AddBlurrer(Blur* blurrer);
+
+    const AcquiredBuffer& buffer() const { return buffer_; }
+    int surface_id() const { return surface_id_; }
+    float blur() const { return blur_; }
+    bool vertical_flip() const { return vertical_flip_; }
+    bool enable_cac() const { return enable_cac_; }
+    size_t blurrer_count() const { return blurrers_.size(); }
+    Blur* blurrer(size_t i) {
+      return blurrers_.size() < i ? nullptr : blurrers_[i].get();
+    }
+    uint32_t render_buffer_index() const { return render_buffer_index_; }
+    const RenderPoseBufferObject* render_pose_buffer_object() const {
+      return render_pose_buffer_object_.get();
+    }
+
+    template <class A>
+    void ForEachVideoCompositor(A action) const {
+      for (auto& c : video_compositors_) {
+        action(c);
+      }
+    }
+
+   private:
+    int surface_id_;
+    float blur_;
+    int z_order_;
+    bool vertical_flip_;
+    bool enable_cac_;
+    std::vector<std::unique_ptr<Blur>> blurrers_;
+    AcquiredBuffer buffer_;
+    std::vector<std::shared_ptr<Texture>> textures_;
+    uint32_t render_buffer_index_;
+    std::unique_ptr<RenderPoseBufferObject> render_pose_buffer_object_;
+
+    // Active video mesh compositors
+    std::vector<std::shared_ptr<VideoCompositor>> video_compositors_;
+
+    AppFrame(const AppFrame& other) = delete;
+    AppFrame& operator=(const AppFrame&) = delete;
+  };
+
+  class RenderTarget {
+   public:
+    RenderTarget();
+    ~RenderTarget();
+
+    void Initialize(int width, int height);
+    void Destroy();
+    void BindFramebuffer();
+    void DiscardColorAttachment();
+
+    std::shared_ptr<IonBuffer> buffer() const { return buffer_; }
+
+   private:
+    std::shared_ptr<IonBuffer> buffer_;
+    android::sp<NativeBuffer> native_buffer_;
+
+    GLuint buffer_texture_id_;
+    GLuint buffer_framebuffer_id_;
+    EGLImageKHR buffer_image_;
+  };
+
+  Compositor(const Compositor&) = delete;
+  void operator=(const Compositor&) = delete;
+
+  bool InitializeEGL();
+
+  void UpdateHudToggle();
+  void PrintStatsHud();
+  void CheckAndUpdateHeadMountMetrics(bool force_update);
+
+  RenderTarget& GetRenderTarget() {
+    return render_target_[active_render_target_];
+  }
+
+  void SetNextRenderTarget() {
+    active_render_target_ = (active_render_target_ + 1) & 1;
+  }
+
+  std::vector<AppFrame> layers_;
+
+  DisplayMetrics display_metrics_;
+  HeadMountMetrics head_mount_metrics_;
+
+  EGLDisplay display_;
+  EGLConfig config_;
+  EGLSurface surface_;
+  EGLContext context_;
+  int active_render_target_;
+  RenderTarget render_target_[2];
+  bool is_render_direct_;
+
+  // FBO for compute shader.
+  GLuint compute_fbo_;
+  GLuint compute_fbo_texture_;
+
+  std::unique_ptr<DebugHudView> debug_hud_;
+
+  // EDS:
+  std::unique_ptr<CompositeHmd> composite_hmd_;
+  bool hmd_metrics_requires_update_;
+  std::unique_ptr<DistortionRenderer> eds_renderer_;
+
+  bool eds_pose_capture_enabled_;
+  std::mutex mutex_;
+  LateLatchOutput eds_pose_capture_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_COMPOSITOR_H_
diff --git a/libs/vr/libvrflinger/debug_hud_data.cpp b/libs/vr/libvrflinger/debug_hud_data.cpp
new file mode 100644
index 0000000..d387bba
--- /dev/null
+++ b/libs/vr/libvrflinger/debug_hud_data.cpp
@@ -0,0 +1,9 @@
+#include "debug_hud_data.h"
+
+namespace android {
+namespace dvr {
+
+DebugHudData DebugHudData::data;
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/debug_hud_data.h b/libs/vr/libvrflinger/debug_hud_data.h
new file mode 100644
index 0000000..778169d
--- /dev/null
+++ b/libs/vr/libvrflinger/debug_hud_data.h
@@ -0,0 +1,110 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_DATA_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_DATA_H_
+
+#include <stdint.h>
+
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/frame_time_history.h>
+
+namespace android {
+namespace dvr {
+
+// Tracks debug stats for the displayd debug HUD. Unless otherwise noted,
+// there is no synchronization of data accesses to avoid performance impact.
+// All accesses to this data are on the displayd HWC post thread. Accesses from
+// other threads will need to be duly protected from races.
+// This is a lightweight struct to make it easy to add and remove
+// tracking data.
+struct DebugHudData {
+  // Maximum supported layers for the debug HUD.
+  enum { kMaxLayers = 4 };
+
+  // The global singleton HUD data instance.
+  static DebugHudData data;
+
+  // Tracks framerate and skipped frames.
+  struct FrameStats {
+    void AddFrame() {
+      int64_t now = GetSystemClockNs();
+      frame_time.AddSample(now - last_frame_ts);
+      last_frame_ts = now;
+    }
+
+    void SkipFrame() {
+      AddFrame();
+      ++drops;
+    }
+
+    int drops = 0;
+    int64_t last_frame_ts = 0;
+    FrameTimeHistory frame_time;
+  };
+
+  // Debug data for compositor layers (applications, system UI, etc).
+  struct LayerData {
+    void Reset() {
+      ResetStats();
+      width = 0;
+      height = 0;
+      is_separate = false;
+    }
+
+    void ResetStats() { frame_stats.drops = 0; }
+
+    FrameStats frame_stats;
+    int width = 0;
+    int height = 0;
+    bool is_separate = false;
+  };
+
+  // Resets the stats.
+  void ResetStats() {
+    hwc_frame_stats.drops = 0;
+    hwc_latency = 0;
+    for (auto& l : layer_data)
+      l.ResetStats();
+  }
+
+  // Resets the layer configuration.
+  void ResetLayers() {
+    num_layers = 0;
+    for (auto& l : layer_data)
+      l.Reset();
+  }
+
+  // Tracks a frame arrival for the given layer.
+  void AddLayerFrame(size_t layer) {
+    if (layer < kMaxLayers) {
+      num_layers = std::max(layer + 1, num_layers);
+      layer_data[layer].frame_stats.AddFrame();
+    }
+  }
+
+  // Tracks a frame skip/drop for the given layer.
+  void SkipLayerFrame(size_t layer) {
+    if (layer < kMaxLayers) {
+      num_layers = std::max(layer + 1, num_layers);
+      layer_data[layer].frame_stats.SkipFrame();
+    }
+  }
+
+  // Sets the resolution and other details of the layer.
+  void SetLayerInfo(size_t layer, int width, int height, bool is_separate) {
+    if (layer < kMaxLayers) {
+      num_layers = std::max(layer + 1, num_layers);
+      layer_data[layer].width = width;
+      layer_data[layer].height = height;
+      layer_data[layer].is_separate = is_separate;
+    }
+  }
+
+  FrameStats hwc_frame_stats;
+  int64_t hwc_latency = 0;
+  size_t num_layers = 0;
+  LayerData layer_data[kMaxLayers];
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_DATA_H_
diff --git a/libs/vr/libvrflinger/debug_hud_view.cpp b/libs/vr/libvrflinger/debug_hud_view.cpp
new file mode 100644
index 0000000..4936ac6
--- /dev/null
+++ b/libs/vr/libvrflinger/debug_hud_view.cpp
@@ -0,0 +1,91 @@
+#include "debug_hud_view.h"
+
+#include <dvr/pose_client.h>
+
+#include "debug_hud_data.h"
+
+namespace android {
+namespace dvr {
+
+DebugHudView::DebugHudView(const CompositeHmd& hmd) {
+  pose_client_ = dvrPoseCreate();
+
+  display_size_ = hmd.GetDisplayMetrics().GetSizePixels();
+  vec2 display_size_meters = hmd.GetDisplayMetrics().GetSizeMeters();
+  inter_lens_dist_screen_space_ =
+      2.0f * hmd.GetHeadMountMetrics().GetInterLensDistance() /
+      std::max(display_size_meters[0], display_size_meters[1]);
+}
+
+DebugHudView::~DebugHudView() {
+  if (pose_client_)
+    dvrPoseDestroy(pose_client_);
+  pose_client_ = nullptr;
+}
+
+void DebugHudView::Update() {
+  // Check for gesture that enables the debug stats HUD.
+  if (!pose_client_)
+    return;
+  DvrPoseAsync pose;
+  dvrPoseGet(pose_client_, 0, &pose);
+  float32x4_t q = pose.orientation;
+  quat orientation(q[3], q[0], q[1], q[2]);
+  vec3 up = orientation * vec3(0, 1, 0);
+  if (up[1] < -0.8f) {
+    ++switch_timer_;
+  } else {
+    switch_timer_ = 0;
+  }
+  // A few seconds upside down => toggle stats HUD.
+  if (switch_timer_ > 200) {
+    switch_timer_ = 0;
+    enabled_ = !enabled_;
+    DebugHudData::data.ResetStats();
+    ALOGE("Toggle debug stats HUD: %s", enabled_ ? "ON" : "OFF");
+  }
+}
+
+void DebugHudView::Draw() {
+  if (!enabled_)
+    return;
+  if (!debug_text_)
+    debug_text_.reset(new DebugText(400, display_size_[0], display_size_[1]));
+
+  const DebugHudData& data = DebugHudData::data;
+  const size_t layer_char_count = 50;
+  char layer_data[DebugHudData::kMaxLayers][layer_char_count];
+  for (size_t i = 0; i < data.num_layers; ++i) {
+    float fps = data.layer_data[i].frame_stats.frame_time.GetAverageFps();
+    snprintf(layer_data[i], layer_char_count,
+             "Layer %d %dx%d%s FPS: %.2f Drops: %d\n", static_cast<int>(i),
+             data.layer_data[i].width, data.layer_data[i].height,
+             data.layer_data[i].is_separate ? "x2" : "", fps,
+             data.layer_data[i].frame_stats.drops);
+  }
+
+  float hwc_fps = data.hwc_frame_stats.frame_time.GetAverageFps();
+
+  char text[400];
+  float hwc_latency_ms = static_cast<float>(data.hwc_latency) / 1000000.0f;
+  snprintf(text, sizeof(text), "HWC FPS: %.2f Latency: %.3f ms Skips: %d\n",
+           hwc_fps, hwc_latency_ms, data.hwc_frame_stats.drops);
+
+  for (size_t i = 0; i < data.num_layers; ++i) {
+    strncat(text, layer_data[i], sizeof(text) - strlen(text) - 1);
+  }
+
+  // Ensure text termination.
+  text[sizeof(text) - 1] = '\0';
+
+  glViewport(0, 0, display_size_[0], display_size_[1]);
+  glEnable(GL_BLEND);
+  // No stereo, because you can see the HUD OK in one eye. Stereo actually
+  // makes it more difficult to focus sometimes. To enable stereo:
+  // replace the second to last parameter with inter_lens_dist_screen_space_.
+  debug_text_->Draw(0.0f, -0.7f * inter_lens_dist_screen_space_, text, 0.0f, 1);
+  glDisable(GL_BLEND);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/debug_hud_view.h b/libs/vr/libvrflinger/debug_hud_view.h
new file mode 100644
index 0000000..50f38a8
--- /dev/null
+++ b/libs/vr/libvrflinger/debug_hud_view.h
@@ -0,0 +1,48 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_VIEW_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_VIEW_H_
+
+#include <stdint.h>
+
+#include <utils/Log.h>
+
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/graphics/debug_text.h>
+
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class CompositeHmd;
+
+// The view and the controller for the displayd debug HUD.
+// The HUD is enabled and disabled by internally tracking the head pose.
+// When the head pose is upside down for ~3 seconds, the enabled state toggles.
+// See DebugHudData for the data that is reported.
+class DebugHudView {
+ public:
+  DebugHudView(const CompositeHmd& hmd);
+  ~DebugHudView();
+
+  // Updates HUD state.
+  void Update();
+
+  // Draws HUD into the current framebuffer if it is currently enabled.
+  void Draw();
+
+ private:
+  DebugHudView(const DebugHudView&) = delete;
+  DebugHudView& operator=(const DebugHudView&) = delete;
+
+  DvrPose* pose_client_ = nullptr;
+  vec2i display_size_;
+  bool enabled_ = false;
+  int switch_timer_ = 0;
+  float inter_lens_dist_screen_space_ = 0.0f;
+  std::unique_ptr<DebugText> debug_text_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_VIEW_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..99f93bf
--- /dev/null
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -0,0 +1,224 @@
+#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_rpc.h>
+#include <private/dvr/trusted_uids.h>
+#include <sys/poll.h>
+
+#include <array>
+
+using android::pdx::Channel;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Message;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::IfAnyOf;
+
+namespace {
+
+// As a first line of defense, the display manager endpoint is only accessible
+// to the user and group.
+
+// TODO(dnicoara): Remove read/write permission for others. This is in here just
+// to allow us to experiment with cast functionality from a plain old app.
+constexpr mode_t kDisplayManagerEndpointFileMode =
+    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
+constexpr size_t kMaxSurfacesPerRequest = 32;
+
+}  // anonymous namespace
+
+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(DisplayManagerRPC::kClientPath,
+                            kDisplayManagerEndpointFileMode)),
+      display_service_(display_service) {
+  display_service_->SetDisplayConfigurationUpdateNotifier(
+      std::bind(&DisplayManagerService::OnDisplaySurfaceChange, this));
+}
+
+std::shared_ptr<pdx::Channel> DisplayManagerService::OnChannelOpen(
+    pdx::Message& message) {
+  // Prevent more than one display manager from registering at a time.
+  if (display_manager_)
+    REPLY_ERROR_RETURN(message, EPERM, 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 DisplayManagerRPC::GetSurfaceList::Opcode:
+      DispatchRemoteMethod<DisplayManagerRPC::GetSurfaceList>(
+          *this, &DisplayManagerService::OnGetSurfaceList, message);
+      return {};
+
+    case DisplayManagerRPC::UpdateSurfaces::Opcode:
+      DispatchRemoteMethod<DisplayManagerRPC::UpdateSurfaces>(
+          *this, &DisplayManagerService::OnUpdateSurfaces, message);
+      return {};
+
+    case DisplayManagerRPC::SetupNamedBuffer::Opcode:
+      DispatchRemoteMethod<DisplayManagerRPC::SetupNamedBuffer>(
+          *this, &DisplayManagerService::OnSetupNamedBuffer, message);
+      return {};
+
+    default:
+      return Service::DefaultHandleMessage(message);
+  }
+}
+
+std::vector<DisplaySurfaceInfo> DisplayManagerService::OnGetSurfaceList(
+    pdx::Message& /*message*/) {
+  std::vector<DisplaySurfaceInfo> items;
+
+  display_service_->ForEachDisplaySurface(
+      [&items](const std::shared_ptr<DisplaySurface>& surface) mutable {
+        DisplaySurfaceInfo item;
+
+        item.surface_id = surface->surface_id();
+        item.process_id = surface->process_id();
+        item.type = surface->type();
+        item.flags = 0;  // TODO(eieio)
+        item.client_attributes = DisplaySurfaceAttributes{
+            {DisplaySurfaceAttributeEnum::Visible,
+             DisplaySurfaceAttributeValue{surface->client_visible()}},
+            {DisplaySurfaceAttributeEnum::ZOrder,
+             DisplaySurfaceAttributeValue{surface->client_z_order()}},
+            {DisplaySurfaceAttributeEnum::Blur,
+             DisplaySurfaceAttributeValue{0.f}}};
+        item.manager_attributes = DisplaySurfaceAttributes{
+            {DisplaySurfaceAttributeEnum::Visible,
+             DisplaySurfaceAttributeValue{surface->manager_visible()}},
+            {DisplaySurfaceAttributeEnum::ZOrder,
+             DisplaySurfaceAttributeValue{surface->manager_z_order()}},
+            {DisplaySurfaceAttributeEnum::Blur,
+             DisplaySurfaceAttributeValue{surface->manager_blur()}}};
+
+        items.push_back(item);
+      });
+
+  // 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;
+}
+
+int DisplayManagerService::OnUpdateSurfaces(
+    pdx::Message& /*message*/,
+    const std::map<int, DisplaySurfaceAttributes>& updates) {
+  for (const auto& surface_update : updates) {
+    const int surface_id = surface_update.first;
+    const DisplaySurfaceAttributes& attributes = surface_update.second;
+
+    std::shared_ptr<DisplaySurface> surface =
+        display_service_->GetDisplaySurface(surface_id);
+
+    if (!surface)
+      return -ENOENT;
+
+    for (const auto& attribute : attributes) {
+      const auto& key = attribute.first;
+      const auto* variant = &attribute.second;
+      bool invalid_value = false;
+      switch (key) {
+        case DisplaySurfaceAttributeEnum::ZOrder:
+          invalid_value =
+              !IfAnyOf<int32_t>::Call(variant, [&surface](const auto& value) {
+                surface->ManagerSetZOrder(value);
+              });
+          break;
+        case DisplaySurfaceAttributeEnum::Visible:
+          invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+              variant, [&surface](const auto& value) {
+                surface->ManagerSetVisible(value);
+              });
+          break;
+        case DisplaySurfaceAttributeEnum::Blur:
+          invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
+              variant, [&surface](const auto& value) {
+                surface->ManagerSetBlur(value);
+              });
+          break;
+        default:
+          ALOGW(
+              "DisplayManagerService::OnUpdateSurfaces: Attempt to set invalid "
+              "attribute %u on surface %d",
+              key, surface_id);
+          break;
+      }
+
+      if (invalid_value) {
+        ALOGW(
+            "DisplayManagerService::OnUpdateSurfaces: Failed to set display "
+            "surface attribute '%s' because of incompatible type: %d",
+            DisplaySurfaceAttributeEnum::ToString(key).c_str(),
+            variant->index());
+      }
+    }
+  }
+
+  // Reconfigure the display layers for any active surface changes.
+  display_service_->UpdateActiveDisplaySurfaces();
+  return 0;
+}
+
+pdx::Status<BorrowedNativeBufferHandle>
+DisplayManagerService::OnSetupNamedBuffer(pdx::Message& message,
+                                          const std::string& name, size_t size,
+                                          uint64_t producer_usage,
+                                          uint64_t consumer_usage) {
+  if (message.GetEffectiveUserId() != AID_ROOT &&
+      !IsTrustedUid(message.GetEffectiveUserId())) {
+    // Only trusted users can setup named buffers.
+    ALOGE("DisplayService::SetupNamedBuffer: Called by untrusted user: uid=%d.",
+          message.GetEffectiveUserId());
+    return {};
+  }
+  return display_service_->SetupNamedBuffer(name, size, producer_usage,
+                                            consumer_usage);
+}
+
+void DisplayManagerService::OnDisplaySurfaceChange() {
+  if (display_manager_) {
+    display_manager_->SetNotificationsPending(true);
+  } else {
+    // If there isn't a display manager registered, default all display surfaces
+    // to visible.
+    display_service_->ForEachDisplaySurface(
+        [](const std::shared_ptr<DisplaySurface>& surface) {
+          surface->ManagerSetVisible(true);
+        });
+    display_service_->UpdateActiveDisplaySurfaces();
+  }
+}
+
+}  // 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..7b037de
--- /dev/null
+++ b/libs/vr/libvrflinger/display_manager_service.h
@@ -0,0 +1,75 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_MANAGER_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_MANAGER_SERVICE_H_
+
+#include <pdx/service.h>
+#include <private/dvr/display_rpc.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);
+
+  std::vector<DisplaySurfaceInfo> OnGetSurfaceList(pdx::Message& message);
+  int OnUpdateSurfaces(pdx::Message& message,
+                       const std::map<int, DisplaySurfaceAttributes>& updates);
+
+  pdx::Status<BorrowedNativeBufferHandle> OnSetupNamedBuffer(
+      pdx::Message& message, const std::string& name, size_t size,
+      uint64_t producer_usage, uint64_t consumer_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_DISPLAYD_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..d3d50d0
--- /dev/null
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -0,0 +1,371 @@
+#include "display_service.h"
+
+#include <unistd.h>
+#include <vector>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/remote_method.h>
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/device_metrics.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/numeric.h>
+#include <private/dvr/polynomial_radial_distortion.h>
+#include <private/dvr/types.h>
+
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::WrapBuffer;
+
+namespace android {
+namespace dvr {
+
+DisplayService::DisplayService() : DisplayService(nullptr) {}
+
+DisplayService::DisplayService(Hwc2::Composer* hidl)
+    : BASE("DisplayService", Endpoint::Create(DisplayRPC::kClientPath)),
+      hardware_composer_(hidl) {
+  hardware_composer_.Initialize();
+}
+
+bool DisplayService::IsInitialized() const {
+  return BASE::IsInitialized() && hardware_composer_.IsInitialized();
+}
+
+std::string DisplayService::DumpState(size_t max_length) {
+  std::vector<char> buffer(max_length);
+  uint32_t max_len_p = static_cast<uint32_t>(max_length);
+  hardware_composer_.Dump(buffer.data(), &max_len_p);
+  return std::string(buffer.data());
+}
+
+void DisplayService::OnChannelClose(pdx::Message& /*message*/,
+                                    const std::shared_ptr<Channel>& channel) {
+  auto surface = std::static_pointer_cast<SurfaceChannel>(channel);
+  if (surface && surface->type() == SurfaceTypeEnum::Normal) {
+    auto display_surface = std::static_pointer_cast<DisplaySurface>(surface);
+    display_surface->ManagerSetVisible(false);
+    display_surface->ClientSetVisible(false);
+    NotifyDisplayConfigurationUpdate();
+  }
+  // TODO(jwcai) Handle ChannelClose of VideoMeshSurface.
+}
+
+// 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.
+pdx::Status<void> DisplayService::HandleMessage(pdx::Message& message) {
+  auto channel = message.GetChannel<SurfaceChannel>();
+
+  switch (message.GetOp()) {
+    case DisplayRPC::GetMetrics::Opcode:
+      DispatchRemoteMethod<DisplayRPC::GetMetrics>(
+          *this, &DisplayService::OnGetMetrics, message);
+      return {};
+
+    case DisplayRPC::GetEdsCapture::Opcode:
+      DispatchRemoteMethod<DisplayRPC::GetEdsCapture>(
+          *this, &DisplayService::OnGetEdsCapture, message);
+      return {};
+
+    case DisplayRPC::CreateSurface::Opcode:
+      DispatchRemoteMethod<DisplayRPC::CreateSurface>(
+          *this, &DisplayService::OnCreateSurface, message);
+      return {};
+
+    case DisplayRPC::SetViewerParams::Opcode:
+      DispatchRemoteMethod<DisplayRPC::SetViewerParams>(
+          *this, &DisplayService::OnSetViewerParams, message);
+      return {};
+
+    case DisplayRPC::GetNamedBuffer::Opcode:
+      DispatchRemoteMethod<DisplayRPC::GetNamedBuffer>(
+          *this, &DisplayService::OnGetNamedBuffer, message);
+      return {};
+
+    case DisplayRPC::IsVrAppRunning::Opcode:
+      DispatchRemoteMethod<DisplayRPC::IsVrAppRunning>(
+          *this, &DisplayService::IsVrAppRunning, message);
+      return {};
+
+    // Direct the surface specific messages to the surface instance.
+    case DisplayRPC::CreateBufferQueue::Opcode:
+    case DisplayRPC::SetAttributes::Opcode:
+    case DisplayRPC::GetMetadataBuffer::Opcode:
+    case DisplayRPC::CreateVideoMeshSurface::Opcode:
+    case DisplayRPC::VideoMeshSurfaceCreateProducerQueue::Opcode:
+      return HandleSurfaceMessage(message);
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+SystemDisplayMetrics DisplayService::OnGetMetrics(pdx::Message& message) {
+  const Compositor* compositor = hardware_composer_.GetCompositor();
+  if (compositor == nullptr)
+    REPLY_ERROR_RETURN(message, EINVAL, {});
+
+  HeadMountMetrics head_mount = compositor->head_mount_metrics();
+  CompositeHmd hmd(head_mount, hardware_composer_.GetHmdDisplayMetrics());
+  vec2i distorted_render_size = hmd.GetRecommendedRenderTargetSize();
+  FieldOfView left_fov = hmd.GetEyeFov(kLeftEye);
+  FieldOfView right_fov = hmd.GetEyeFov(kRightEye);
+
+  SystemDisplayMetrics metrics;
+
+  metrics.display_native_width = GetDisplayMetrics().width;
+  metrics.display_native_height = GetDisplayMetrics().height;
+  metrics.display_x_dpi = GetDisplayMetrics().dpi.x;
+  metrics.display_y_dpi = GetDisplayMetrics().dpi.y;
+  metrics.distorted_width = distorted_render_size[0];
+  metrics.distorted_height = distorted_render_size[1];
+  metrics.vsync_period_ns =
+      hardware_composer_.native_display_metrics().vsync_period_ns;
+  metrics.hmd_ipd_mm = 0;
+  metrics.inter_lens_distance_m = head_mount.GetInterLensDistance();
+  metrics.left_fov_lrbt[0] = left_fov.GetLeft();
+  metrics.left_fov_lrbt[1] = left_fov.GetRight();
+  metrics.left_fov_lrbt[2] = left_fov.GetBottom();
+  metrics.left_fov_lrbt[3] = left_fov.GetTop();
+  metrics.right_fov_lrbt[0] = right_fov.GetLeft();
+  metrics.right_fov_lrbt[1] = right_fov.GetRight();
+  metrics.right_fov_lrbt[2] = right_fov.GetBottom();
+  metrics.right_fov_lrbt[3] = right_fov.GetTop();
+
+  return metrics;
+}
+
+// Creates a new DisplaySurface and associates it with this channel. This may
+// only be done once per channel.
+int DisplayService::OnCreateSurface(pdx::Message& message, int width,
+                                    int height, int format, int usage,
+                                    DisplaySurfaceFlags flags) {
+  // A surface may only be created once per channel.
+  if (message.GetChannel())
+    return -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();
+
+  ALOGI_IF(TRACE,
+           "DisplayService::OnCreateSurface: surface_id=%d process_id=%d "
+           "width=%d height=%d format=%x usage=%x flags=%x",
+           surface_id, process_id, width, height, format, usage, flags);
+
+  // TODO(eieio,jbates): Validate request parameters.
+  auto channel = std::make_shared<DisplaySurface>(
+      this, surface_id, process_id, width, height, format, usage, flags);
+
+  message.SetChannel(channel);
+  NotifyDisplayConfigurationUpdate();
+  return 0;
+}
+
+DisplayRPC::ByteBuffer DisplayService::OnGetEdsCapture(pdx::Message& message) {
+  Compositor* compositor = hardware_composer_.GetCompositor();
+  if (compositor == nullptr)
+    REPLY_ERROR_RETURN(message, EINVAL, {});
+
+  std::vector<std::uint8_t> buffer(sizeof(LateLatchOutput));
+
+  if (!compositor->GetLastEdsPose(
+          reinterpret_cast<LateLatchOutput*>(buffer.data()))) {
+    REPLY_ERROR_RETURN(message, EPERM, {});
+  }
+
+  return WrapBuffer(std::move(buffer));
+}
+
+void DisplayService::OnSetViewerParams(pdx::Message& message,
+                                       const ViewerParams& view_params) {
+  Compositor* compositor = hardware_composer_.GetCompositor();
+  if (compositor == nullptr)
+    REPLY_ERROR_RETURN(message, EINVAL);
+
+  FieldOfView left(55.0f, 55.0f, 55.0f, 55.0f);
+  FieldOfView right(55.0f, 55.0f, 55.0f, 55.0f);
+  if (view_params.left_eye_field_of_view_angles.size() >= 4) {
+    left = FieldOfView(ToRad(view_params.left_eye_field_of_view_angles[0]),
+                       ToRad(view_params.left_eye_field_of_view_angles[1]),
+                       ToRad(view_params.left_eye_field_of_view_angles[2]),
+                       ToRad(view_params.left_eye_field_of_view_angles[3]));
+    right = FieldOfView(ToRad(view_params.left_eye_field_of_view_angles[1]),
+                        ToRad(view_params.left_eye_field_of_view_angles[0]),
+                        ToRad(view_params.left_eye_field_of_view_angles[2]),
+                        ToRad(view_params.left_eye_field_of_view_angles[3]));
+  }
+
+  std::shared_ptr<ColorChannelDistortion> red_distortion;
+  std::shared_ptr<ColorChannelDistortion> green_distortion;
+  std::shared_ptr<ColorChannelDistortion> blue_distortion;
+
+  // We should always have a red distortion.
+  LOG_FATAL_IF(view_params.distortion_coefficients_r.empty());
+  red_distortion = std::make_shared<PolynomialRadialDistortion>(
+      view_params.distortion_coefficients_r);
+
+  if (!view_params.distortion_coefficients_g.empty()) {
+    green_distortion = std::make_shared<PolynomialRadialDistortion>(
+        view_params.distortion_coefficients_g);
+  }
+
+  if (!view_params.distortion_coefficients_b.empty()) {
+    blue_distortion = std::make_shared<PolynomialRadialDistortion>(
+        view_params.distortion_coefficients_b);
+  }
+
+  HeadMountMetrics::EyeOrientation left_orientation =
+      HeadMountMetrics::EyeOrientation::kCCW0Degrees;
+  HeadMountMetrics::EyeOrientation right_orientation =
+      HeadMountMetrics::EyeOrientation::kCCW0Degrees;
+
+  if (view_params.eye_orientations.size() > 1) {
+    left_orientation = static_cast<HeadMountMetrics::EyeOrientation>(
+        view_params.eye_orientations[0]);
+    right_orientation = static_cast<HeadMountMetrics::EyeOrientation>(
+        view_params.eye_orientations[1]);
+  }
+
+  HeadMountMetrics head_mount_metrics(
+      view_params.inter_lens_distance, view_params.tray_to_lens_distance,
+      view_params.screen_to_lens_distance,
+      static_cast<HeadMountMetrics::VerticalAlignment>(
+          view_params.vertical_alignment),
+      left, right, red_distortion, green_distortion, blue_distortion,
+      left_orientation, right_orientation,
+      view_params.screen_center_to_lens_distance);
+
+  compositor->UpdateHeadMountMetrics(head_mount_metrics);
+}
+
+pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnGetNamedBuffer(
+    pdx::Message& /* message */, const std::string& name) {
+  auto named_buffer = named_buffers_.find(name);
+  if (named_buffer != named_buffers_.end()) {
+    return {BorrowedNativeBufferHandle(*named_buffer->second, 0)};
+  }
+
+  return pdx::ErrorStatus(EINVAL);
+}
+
+// Calls the message handler for the DisplaySurface associated with this
+// channel.
+pdx::Status<void> DisplayService::HandleSurfaceMessage(pdx::Message& message) {
+  auto surface = std::static_pointer_cast<SurfaceChannel>(message.GetChannel());
+  ALOGW_IF(!surface,
+           "DisplayService::HandleSurfaceMessage: surface is nullptr!");
+
+  if (surface)
+    return surface->HandleMessage(message);
+  else
+    REPLY_ERROR_RETURN(message, 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<DisplaySurface>>
+DisplayService::GetVisibleDisplaySurfaces() const {
+  std::vector<std::shared_ptr<DisplaySurface>> visible_surfaces;
+
+  ForEachDisplaySurface(
+      [&](const std::shared_ptr<DisplaySurface>& surface) mutable {
+        if (surface->IsVisible())
+          visible_surfaces.push_back(surface);
+      });
+
+  return visible_surfaces;
+}
+
+void DisplayService::UpdateActiveDisplaySurfaces() {
+  auto visible_surfaces = GetVisibleDisplaySurfaces();
+
+  // Sort the surfaces based on manager z order first, then client z order.
+  std::sort(visible_surfaces.begin(), visible_surfaces.end(),
+            [](const std::shared_ptr<DisplaySurface>& a,
+               const std::shared_ptr<DisplaySurface>& b) {
+              return a->manager_z_order() != b->manager_z_order()
+                         ? a->manager_z_order() < b->manager_z_order()
+                         : a->client_z_order() < b->client_z_order();
+            });
+
+  ALOGD_IF(TRACE,
+           "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
+           visible_surfaces.size());
+
+  // TODO(jbates) Have the shell manage blurred layers.
+  bool blur_requested = false;
+  auto end = visible_surfaces.crend();
+  for (auto it = visible_surfaces.crbegin(); it != end; ++it) {
+    auto surface = *it;
+    // Surfaces with exclude_from_blur==true are not blurred
+    // and are excluded from blur computation of other layers.
+    if (surface->client_exclude_from_blur()) {
+      surface->ManagerSetBlur(0.0f);
+      continue;
+    }
+    surface->ManagerSetBlur(blur_requested ? 1.0f : 0.0f);
+    if (surface->client_blur_behind())
+      blur_requested = true;
+  }
+
+  hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
+}
+
+pdx::Status<BorrowedNativeBufferHandle> DisplayService::SetupNamedBuffer(
+    const std::string& name, size_t size, int producer_usage,
+    int consumer_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, producer_usage,
+        consumer_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_();
+}
+
+int DisplayService::IsVrAppRunning(pdx::Message& message) {
+  bool visible = false;
+  ForEachDisplaySurface(
+      [&visible](const std::shared_ptr<DisplaySurface>& surface) {
+        if (surface->client_z_order() == 0 && surface->IsVisible())
+          visible = true;
+      });
+
+  REPLY_SUCCESS_RETURN(message, visible, 0);
+}
+
+}  // 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..db89064
--- /dev/null
+++ b/libs/vr/libvrflinger/display_service.h
@@ -0,0 +1,114 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
+
+#include <pdx/service.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/late_latch.h>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "acquired_buffer.h"
+#include "display_surface.h"
+#include "hardware_composer.h"
+
+namespace android {
+namespace dvr {
+
+// DisplayService implements the displayd display service over ServiceFS.
+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<DisplaySurface>> 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, int producer_usage,
+      int consumer_usage);
+
+  template <class A>
+  void ForEachDisplaySurface(A action) const {
+    ForEachChannel([action](const ChannelIterator::value_type& pair) mutable {
+      auto surface = std::static_pointer_cast<SurfaceChannel>(pair.second);
+      if (surface->type() == SurfaceTypeEnum::Normal)
+        action(std::static_pointer_cast<DisplaySurface>(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;
+
+  DisplayService();
+  DisplayService(android::Hwc2::Composer* hidl);
+
+  SystemDisplayMetrics OnGetMetrics(pdx::Message& message);
+  int OnCreateSurface(pdx::Message& message, int width, int height, int format,
+                      int usage, DisplaySurfaceFlags flags);
+
+  DisplayRPC::ByteBuffer OnGetEdsCapture(pdx::Message& message);
+
+  void OnSetViewerParams(pdx::Message& message,
+                         const ViewerParams& view_params);
+  pdx::Status<BorrowedNativeBufferHandle> OnGetNamedBuffer(
+      pdx::Message& message, const std::string& name);
+
+  // Temporary query for current VR status. Will be removed later.
+  int IsVrAppRunning(pdx::Message& message);
+
+  // 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);
+
+  DisplayService(const DisplayService&) = delete;
+  void operator=(const DisplayService&) = delete;
+
+  HardwareComposer hardware_composer_;
+  DisplayConfigurationUpdateNotifier update_notifier_;
+
+  std::unordered_map<std::string, std::unique_ptr<IonBuffer>> named_buffers_;
+};
+
+}  // 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..a7220fe
--- /dev/null
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -0,0 +1,357 @@
+#include "display_surface.h"
+
+#include <utils/Trace.h>
+
+#include <private/dvr/platform_defines.h>
+
+#include "display_service.h"
+#include "hardware_composer.h"
+
+#define LOCAL_TRACE 1
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::LocalChannelHandle;
+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, int surface_id,
+                               int process_id, int width, int height,
+                               int format, int usage, int flags)
+    : SurfaceChannel(service, surface_id, SurfaceTypeEnum::Normal,
+                     sizeof(DisplaySurfaceMetadata)),
+      process_id_(process_id),
+      acquired_buffers_(kMaxPostedBuffers),
+      video_mesh_surfaces_updated_(false),
+      width_(width),
+      height_(height),
+      format_(format),
+      usage_(usage),
+      flags_(flags),
+      client_visible_(false),
+      client_z_order_(0),
+      client_exclude_from_blur_(false),
+      client_blur_behind_(false),
+      manager_visible_(false),
+      manager_z_order_(0),
+      manager_blur_(0.0f),
+      layer_order_(0),
+      allocated_buffer_index_(0) {}
+
+DisplaySurface::~DisplaySurface() {
+  ALOGD_IF(LOCAL_TRACE,
+           "DisplaySurface::~DisplaySurface: surface_id=%d process_id=%d",
+           surface_id(), process_id_);
+}
+
+void DisplaySurface::ManagerSetVisible(bool visible) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  manager_visible_ = visible;
+}
+
+void DisplaySurface::ManagerSetZOrder(int z_order) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  manager_z_order_ = z_order;
+}
+
+void DisplaySurface::ManagerSetBlur(float blur) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  manager_blur_ = blur;
+}
+
+void DisplaySurface::ClientSetVisible(bool visible) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  client_visible_ = visible;
+}
+
+void DisplaySurface::ClientSetZOrder(int z_order) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  client_z_order_ = z_order;
+}
+
+void DisplaySurface::ClientSetExcludeFromBlur(bool exclude_from_blur) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  client_exclude_from_blur_ = exclude_from_blur;
+}
+
+void DisplaySurface::ClientSetBlurBehind(bool blur_behind) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  client_blur_behind_ = blur_behind;
+}
+
+void DisplaySurface::DequeueBuffersLocked() {
+  if (consumer_queue_ == nullptr) {
+    ALOGE(
+        "DisplaySurface::DequeueBuffersLocked: Consumer queue is not "
+        "initialized.");
+    return;
+  }
+
+  size_t slot;
+  uint64_t sequence;
+  while (true) {
+    LocalHandle acquire_fence;
+    auto buffer_consumer =
+        consumer_queue_->Dequeue(0, &slot, &sequence, &acquire_fence);
+    if (!buffer_consumer) {
+      ALOGD_IF(TRACE,
+               "DisplaySurface::DequeueBuffersLocked: We have dequeued all "
+               "available buffers.");
+      return;
+    }
+
+    // Save buffer index, associated with the buffer id so that it can be looked
+    // up later.
+    int buffer_id = buffer_consumer->id();
+    if (buffer_id_to_index_.find(buffer_id) == buffer_id_to_index_.end()) {
+      buffer_id_to_index_[buffer_id] = allocated_buffer_index_;
+      ++allocated_buffer_index_;
+    }
+
+    if (!IsVisible()) {
+      ATRACE_NAME("DropFrameOnInvisibleSurface");
+      ALOGD_IF(TRACE,
+               "DisplaySurface::DequeueBuffersLocked: Discarding buffer_id=%d "
+               "on invisible surface.",
+               buffer_consumer->id());
+      buffer_consumer->Discard();
+      continue;
+    }
+
+    if (acquired_buffers_.IsFull()) {
+      ALOGE(
+          "DisplaySurface::DequeueBuffersLocked: Posted buffers full, "
+          "overwriting.");
+      acquired_buffers_.PopBack();
+    }
+
+    acquired_buffers_.Append(
+        AcquiredBuffer(buffer_consumer, std::move(acquire_fence), sequence));
+  }
+}
+
+AcquiredBuffer DisplaySurface::AcquireCurrentBuffer() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  DequeueBuffersLocked();
+
+  if (acquired_buffers_.IsEmpty()) {
+    ALOGE(
+        "DisplaySurface::AcquireCurrentBuffer: attempt to acquire buffer when "
+        "none are posted.");
+    return AcquiredBuffer();
+  }
+  AcquiredBuffer buffer = std::move(acquired_buffers_.Front());
+  acquired_buffers_.PopFront();
+  ALOGD_IF(TRACE, "DisplaySurface::AcquireCurrentBuffer: buffer: %p",
+           buffer.buffer().get());
+  return buffer;
+}
+
+AcquiredBuffer DisplaySurface::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, "DisplaySurface::AcquireNewestAvailableBuffer: buffer: %p",
+           buffer.buffer().get());
+  return buffer;
+}
+
+uint32_t DisplaySurface::GetRenderBufferIndex(int buffer_id) {
+  std::lock_guard<std::mutex> autolock(lock_);
+
+  if (buffer_id_to_index_.find(buffer_id) == buffer_id_to_index_.end()) {
+    ALOGW("DisplaySurface::GetRenderBufferIndex: unknown buffer_id %d.",
+          buffer_id);
+    return 0;
+  }
+  return buffer_id_to_index_[buffer_id];
+}
+
+bool DisplaySurface::IsBufferAvailable() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  DequeueBuffersLocked();
+
+  return !acquired_buffers_.IsEmpty() &&
+         acquired_buffers_.Front().IsAvailable();
+}
+
+bool DisplaySurface::IsBufferPosted() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  DequeueBuffersLocked();
+
+  return !acquired_buffers_.IsEmpty();
+}
+
+pdx::Status<void> DisplaySurface::HandleMessage(pdx::Message& message) {
+  switch (message.GetOp()) {
+    case DisplayRPC::SetAttributes::Opcode:
+      DispatchRemoteMethod<DisplayRPC::SetAttributes>(
+          *this, &DisplaySurface::OnClientSetAttributes, message);
+      break;
+
+    case DisplayRPC::CreateBufferQueue::Opcode:
+      DispatchRemoteMethod<DisplayRPC::CreateBufferQueue>(
+          *this, &DisplaySurface::OnCreateBufferQueue, message);
+      break;
+
+    case DisplayRPC::CreateVideoMeshSurface::Opcode:
+      DispatchRemoteMethod<DisplayRPC::CreateVideoMeshSurface>(
+          *this, &DisplaySurface::OnCreateVideoMeshSurface, message);
+      break;
+
+    default:
+      return SurfaceChannel::HandleMessage(message);
+  }
+
+  return {};
+}
+
+int DisplaySurface::OnClientSetAttributes(
+    pdx::Message& /*message*/, const DisplaySurfaceAttributes& attributes) {
+  for (const auto& attribute : attributes) {
+    const auto& key = attribute.first;
+    const auto* variant = &attribute.second;
+    bool invalid_value = false;
+    switch (key) {
+      case DisplaySurfaceAttributeEnum::ZOrder:
+        invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
+            variant, [this](const auto& value) {
+              DisplaySurface::ClientSetZOrder(value);
+            });
+        break;
+      case DisplaySurfaceAttributeEnum::Visible:
+        invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+            variant, [this](const auto& value) {
+              DisplaySurface::ClientSetVisible(value);
+            });
+        break;
+      case DisplaySurfaceAttributeEnum::ExcludeFromBlur:
+        invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+            variant, [this](const auto& value) {
+              DisplaySurface::ClientSetExcludeFromBlur(value);
+            });
+        break;
+      case DisplaySurfaceAttributeEnum::BlurBehind:
+        invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+            variant, [this](const auto& value) {
+              DisplaySurface::ClientSetBlurBehind(value);
+            });
+        break;
+      default:
+        ALOGW(
+            "DisplaySurface::OnClientSetAttributes: Unrecognized attribute %d "
+            "surface_id=%d",
+            key, surface_id());
+        break;
+    }
+
+    if (invalid_value) {
+      ALOGW(
+          "DisplaySurface::OnClientSetAttributes: Failed to set display "
+          "surface attribute '%s' because of incompatible type: %d",
+          DisplaySurfaceAttributeEnum::ToString(key).c_str(), variant->index());
+    }
+  }
+
+  service()->NotifyDisplayConfigurationUpdate();
+  return 0;
+}
+
+LocalChannelHandle DisplaySurface::OnCreateBufferQueue(Message& message) {
+  ATRACE_NAME("DisplaySurface::OnCreateBufferQueue");
+
+  if (consumer_queue_ != nullptr) {
+    ALOGE(
+        "DisplaySurface::OnCreateBufferQueue: A ProdcuerQueue has already been "
+        "created and transported to DisplayClient.");
+    REPLY_ERROR_RETURN(message, EALREADY, {});
+  }
+
+  auto producer = ProducerQueue::Create<uint64_t>();
+  consumer_queue_ = producer->CreateConsumerQueue();
+
+  return std::move(producer->GetChannelHandle());
+}
+
+RemoteChannelHandle DisplaySurface::OnCreateVideoMeshSurface(
+    pdx::Message& message) {
+  if (flags_ & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION) {
+    ALOGE(
+        "DisplaySurface::OnCreateVideoMeshSurface: system distortion is "
+        "disabled on this display surface, cannot create VideoMeshSurface on "
+        "top of it.");
+    REPLY_ERROR_RETURN(message, EINVAL, {});
+  }
+
+  int channel_id;
+  auto status = message.PushChannel(0, nullptr, &channel_id);
+  if (!status) {
+    ALOGE(
+        "DisplaySurface::OnCreateVideoMeshSurface: failed to push channel: %s",
+        status.GetErrorMessage().c_str());
+    REPLY_ERROR_RETURN(message, status.error(), {});
+  }
+
+  auto surface = std::make_shared<VideoMeshSurface>(service(), channel_id);
+  auto channel_status = service()->SetChannel(channel_id, surface);
+  if (!channel_status) {
+    ALOGE(
+        "DisplaySurface::OnCreateVideoMeshSurface: failed to set new video "
+        "mesh surface channel: %s",
+        channel_status.GetErrorMessage().c_str());
+    REPLY_ERROR_RETURN(message, channel_status.error(), {});
+  }
+
+  {
+    std::lock_guard<std::mutex> autolock(lock_);
+    pending_video_mesh_surfaces_.push_back(surface);
+    video_mesh_surfaces_updated_ = true;
+  }
+
+  return status.take();
+}
+
+std::vector<std::shared_ptr<VideoMeshSurface>>
+DisplaySurface::GetVideoMeshSurfaces() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  std::vector<std::shared_ptr<VideoMeshSurface>> surfaces;
+
+  for (auto& surface : pending_video_mesh_surfaces_) {
+    if (auto video_surface = surface.lock()) {
+      surfaces.push_back(video_surface);
+    } else {
+      ALOGE("Unable to lock video mesh surface.");
+    }
+  }
+
+  pending_video_mesh_surfaces_.clear();
+  video_mesh_surfaces_updated_ = false;
+  return surfaces;
+}
+
+}  // 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..2e4cf75
--- /dev/null
+++ b/libs/vr/libvrflinger/display_surface.h
@@ -0,0 +1,181 @@
+#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/display_rpc.h>
+#include <private/dvr/ring_buffer.h>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "acquired_buffer.h"
+#include "surface_channel.h"
+#include "video_mesh_surface.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayService;
+
+// DisplaySurface is the service-side notion of a client display context. It is
+// responsible for managing display buffer format, geometry, and state, and
+// maintains the buffer consumers connected to the client.
+class DisplaySurface : public SurfaceChannel {
+ public:
+  DisplaySurface(DisplayService* service, int surface_id, int process_id,
+                 int width, int height, int format, int usage, int flags);
+  ~DisplaySurface() override;
+
+  int process_id() const { return process_id_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+  int format() const { return format_; }
+  int usage() const { return usage_; }
+  int flags() const { return flags_; }
+
+  bool client_visible() const { return client_visible_; }
+  int client_z_order() const { return client_z_order_; }
+  bool client_exclude_from_blur() const { return client_exclude_from_blur_; }
+  bool client_blur_behind() const { return client_blur_behind_; }
+
+  bool manager_visible() const { return manager_visible_; }
+  int manager_z_order() const { return manager_z_order_; }
+  float manager_blur() const { return manager_blur_; }
+
+  bool video_mesh_surfaces_updated() const {
+    return video_mesh_surfaces_updated_;
+  }
+
+  volatile const DisplaySurfaceMetadata* GetMetadataBufferPtr() {
+    if (EnsureMetadataBuffer()) {
+      void* addr = nullptr;
+      metadata_buffer_->GetBlobReadWritePointer(metadata_size(), &addr);
+      return static_cast<const volatile DisplaySurfaceMetadata*>(addr);
+    } else {
+      return nullptr;
+    }
+  }
+
+  uint32_t GetRenderBufferIndex(int buffer_id);
+  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);
+
+  // Display manager interface to control visibility and z order.
+  void ManagerSetVisible(bool visible);
+  void ManagerSetZOrder(int z_order);
+  void ManagerSetBlur(float blur);
+
+  // A surface must be set visible by both the client and the display manager to
+  // be visible on screen.
+  bool IsVisible() const { return client_visible_ && manager_visible_; }
+
+  // A surface is blurred if the display manager requests it.
+  bool IsBlurred() const { return manager_blur_ > 0.0f; }
+
+  // Set by HardwareComposer to the current logical layer order of this surface.
+  void SetLayerOrder(int layer_order) { layer_order_ = layer_order; }
+  // Gets the unique z-order index of this surface among other visible surfaces.
+  // This is not the same as the hardware layer index, as not all display
+  // surfaces map directly to hardware layers. Lower layer orders should be
+  // composited underneath higher layer orders.
+  int layer_order() const { return layer_order_; }
+
+  // Lock all video mesh surfaces so that VideoMeshCompositor can access them.
+  std::vector<std::shared_ptr<VideoMeshSurface>> GetVideoMeshSurfaces();
+
+ private:
+  friend class DisplayService;
+
+  // 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 kMaxPostedBuffers =
+      kSurfaceBufferMaxCount * kSurfaceViewMaxCount;
+
+  // Returns whether a frame is available without locking the mutex.
+  bool IsFrameAvailableNoLock() const;
+
+  // Dispatches display surface messages to the appropriate handlers. This
+  // handler runs on the displayd message dispatch thread.
+  pdx::Status<void> HandleMessage(pdx::Message& message) override;
+
+  // Sets display surface's client-controlled attributes.
+  int OnClientSetAttributes(pdx::Message& message,
+                            const DisplaySurfaceAttributes& attributes);
+
+  // Creates a BufferHubQueue associated with this surface and returns the PDX
+  // handle of its producer side to the client.
+  pdx::LocalChannelHandle OnCreateBufferQueue(pdx::Message& message);
+
+  // Creates a video mesh surface associated with this surface and returns its
+  // PDX handle to the client.
+  pdx::RemoteChannelHandle OnCreateVideoMeshSurface(pdx::Message& message);
+
+  // Client interface (called through IPC) to set visibility and z order.
+  void ClientSetVisible(bool visible);
+  void ClientSetZOrder(int z_order);
+  void ClientSetExcludeFromBlur(bool exclude_from_blur);
+  void ClientSetBlurBehind(bool blur_behind);
+
+  // Dequeue all available buffers from the consumer queue.
+  void DequeueBuffersLocked();
+
+  DisplaySurface(const DisplaySurface&) = delete;
+  void operator=(const DisplaySurface&) = delete;
+
+  int process_id_;
+
+  // Synchronizes access to mutable state below between message dispatch thread,
+  // epoll event thread, and frame post thread.
+  mutable std::mutex lock_;
+
+  // The consumer end of a BufferHubQueue. VrFlinger allocates and controls the
+  // buffer queue and pass producer end to the app and the consumer end to
+  // compositor.
+  // TODO(jwcai) Add support for multiple buffer queues per display surface.
+  std::shared_ptr<ConsumerQueue> consumer_queue_;
+
+  // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be
+  // posted and pending.
+  RingBuffer<AcquiredBuffer> acquired_buffers_;
+
+  // Provides access to VideoMeshSurface. Here we don't want to increase
+  // the reference count immediately on allocation, will leave it into
+  // compositor's hand.
+  std::vector<std::weak_ptr<VideoMeshSurface>> pending_video_mesh_surfaces_;
+  volatile bool video_mesh_surfaces_updated_;
+
+  // Surface parameters.
+  int width_;
+  int height_;
+  int format_;
+  int usage_;
+  int flags_;
+  bool client_visible_;
+  int client_z_order_;
+  bool client_exclude_from_blur_;
+  bool client_blur_behind_;
+  bool manager_visible_;
+  int manager_z_order_;
+  float manager_blur_;
+  int layer_order_;
+
+  // The monotonically increasing index for allocated buffers in this surface.
+  uint32_t allocated_buffer_index_;
+  // Maps from the buffer id to the corresponding allocated buffer index.
+  std::unordered_map<int, uint32_t> buffer_id_to_index_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
new file mode 100644
index 0000000..18ff4f5
--- /dev/null
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -0,0 +1,1531 @@
+#include "hardware_composer.h"
+
+#include <log/log.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <fcntl.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 <unistd.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <functional>
+#include <map>
+
+#include <dvr/performance_client_api.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sync_util.h>
+
+#include "debug_hud_data.h"
+#include "screenshot_service.h"
+
+using android::pdx::LocalHandle;
+
+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;
+
+// If we think we're going to miss vsync by more than this amount, skip the
+// frame.
+constexpr int64_t kFrameSkipThresholdNs = 4000000;  // 4ms
+
+// Counter PostLayers() deficiency by requiring apps to produce a frame at least
+// 2.5ms before vsync. See b/28881672.
+constexpr int64_t kFrameTimeEstimateMin = 2500000;  // 2.5ms
+
+constexpr size_t kDefaultDisplayConfigCount = 32;
+
+constexpr float kMetersPerInch = 0.0254f;
+
+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";
+
+// Returns our best guess for the time the compositor will spend rendering the
+// next frame.
+int64_t GuessFrameTime(int compositor_visible_layer_count) {
+  // The cost of asynchronous EDS and lens warp is currently measured at 2.5ms
+  // for one layer and 7ms for two layers, but guess a higher frame time to
+  // account for CPU overhead. This guess is only used before we've measured the
+  // actual time to render a frame for the current compositor configuration.
+  switch (compositor_visible_layer_count) {
+    case 0:
+      return 500000;  // .5ms
+    case 1:
+      return 5000000;  // 5ms
+    default:
+      return 10500000;  // 10.5ms
+  }
+}
+
+// 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;
+}
+
+}  // anonymous namespace
+
+HardwareComposer::HardwareComposer()
+  : HardwareComposer(nullptr) {
+}
+
+HardwareComposer::HardwareComposer(Hwc2::Composer* hwc2_hidl)
+    : initialized_(false),
+      hwc2_hidl_(hwc2_hidl),
+      display_transform_(HWC_TRANSFORM_NONE),
+      active_surfaces_updated_(false),
+      active_layer_count_(0),
+      gpu_layer_(nullptr),
+      post_thread_enabled_(false),
+      post_thread_running_(false),
+      post_thread_quit_requested_(false),
+      post_thread_interrupt_event_fd_(-1),
+      backlight_brightness_fd_(-1),
+      primary_display_vsync_event_fd_(-1),
+      primary_display_wait_pp_fd_(-1),
+      vsync_sleep_timer_fd_(-1),
+      last_vsync_timestamp_(0),
+      vsync_count_(0),
+      frame_skip_count_(0),
+      pose_client_(nullptr) {
+  std::transform(layer_storage_.begin(), layer_storage_.end(), layers_.begin(),
+                 [](auto& layer) { return &layer; });
+
+  callbacks_ = new ComposerCallback;
+}
+
+HardwareComposer::~HardwareComposer(void) {
+  std::unique_lock<std::mutex> lock(post_thread_mutex_);
+  if (post_thread_.joinable()) {
+    post_thread_quit_requested_ = true;
+    post_thread_cond_var_.notify_all();
+    post_thread_.join();
+  }
+}
+
+bool HardwareComposer::Initialize() {
+  if (initialized_) {
+    ALOGE("HardwareComposer::Initialize: already initialized.");
+    return false;
+  }
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  Hwc2::Config config;
+  ret = (int32_t)hwc2_hidl_->getActiveConfig(HWC_DISPLAY_PRIMARY, &config);
+
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get current display config : %d",
+          config);
+    return false;
+  }
+
+  ret =
+      GetDisplayMetrics(HWC_DISPLAY_PRIMARY, config, &native_display_metrics_);
+
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE(
+        "HardwareComposer: Failed to get display attributes for current "
+        "configuration : %d",
+        ret);
+    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_;
+
+  post_thread_interrupt_event_fd_.Reset(
+      eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  LOG_ALWAYS_FATAL_IF(
+      !post_thread_interrupt_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() {
+  std::lock_guard<std::mutex> lock(post_thread_mutex_);
+  post_thread_enabled_ = true;
+  post_thread_cond_var_.notify_all();
+}
+
+void HardwareComposer::Disable() {
+  std::unique_lock<std::mutex> lock(post_thread_mutex_);
+  post_thread_enabled_ = false;
+  if (post_thread_running_) {
+    // Write to the interrupt fd to get fast interrupt of the post thread
+    int error = eventfd_write(post_thread_interrupt_event_fd_.Get(), 1);
+    ALOGW_IF(error,
+             "HardwareComposer::Disable: could not write post "
+             "thread interrupt event fd : %s",
+             strerror(errno));
+
+    post_thread_cond_var_.wait(lock, [this] { return !post_thread_running_; });
+
+    // Read the interrupt fd to clear its state
+    uint64_t interrupt_count= 0;
+    error = eventfd_read(post_thread_interrupt_event_fd_.Get(),
+                         &interrupt_count);
+    ALOGW_IF(error,
+             "HardwareComposer::Disable: could not read post "
+             "thread interrupt event fd : %s",
+             strerror(errno));
+  }
+}
+
+bool HardwareComposer::PostThreadHasWork() {
+  return !display_surfaces_.empty() ||
+      (active_surfaces_updated_ && !active_surfaces_.empty());
+}
+
+void HardwareComposer::OnPostThreadResumed() {
+  constexpr int format = HAL_PIXEL_FORMAT_RGBA_8888;
+  constexpr int usage =
+      GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER;
+
+  framebuffer_target_ = std::make_shared<IonBuffer>(
+      native_display_metrics_.width, native_display_metrics_.height, format,
+      usage);
+
+  // Associate each Layer instance with a hardware composer layer.
+  for (auto layer : layers_) {
+    layer->Initialize(hwc2_hidl_.get(), &native_display_metrics_);
+  }
+
+  // Connect to pose service.
+  pose_client_ = dvrPoseCreate();
+  ALOGE_IF(!pose_client_, "HardwareComposer: Failed to create pose client");
+
+  EnableVsync(true);
+
+  // 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);
+
+  // Initialize the GPU compositor.
+  LOG_ALWAYS_FATAL_IF(!compositor_.Initialize(GetHmdDisplayMetrics()),
+                      "Failed to initialize the compositor");
+
+  // Trigger target-specific performance mode change.
+  property_set(kDvrPerformanceProperty, "performance");
+}
+
+void HardwareComposer::OnPostThreadPaused() {
+  retire_fence_fds_.clear();
+  gpu_layer_ = nullptr;
+
+  // We have to destroy the layers to fully clear hwc device state before
+  // handing off back to surface flinger
+  for (size_t i = 0; i < kMaxHardwareLayers; ++i) {
+    layers_[i]->Reset();
+  }
+
+  active_layer_count_ = 0;
+
+  framebuffer_target_.reset();
+
+  display_surfaces_.clear();
+  compositor_surfaces_.clear();
+
+  // Since we're clearing display_surfaces_ we'll need an update.
+  active_surfaces_updated_ = true;
+
+  if (pose_client_) {
+    dvrPoseDestroy(pose_client_);
+    pose_client_ = nullptr;
+  }
+
+  EnableVsync(false);
+
+  frame_time_history_.ResetWithSeed(GuessFrameTime(0));
+  frame_time_backlog_.clear();
+
+  compositor_.Shutdown();
+
+  // Trigger target-specific performance mode change.
+  property_set(kDvrPerformanceProperty, "idle");
+}
+
+DisplayMetrics HardwareComposer::GetHmdDisplayMetrics() const {
+  vec2i screen_size(display_metrics_.width, display_metrics_.height);
+  DisplayOrientation orientation =
+      (display_metrics_.width > display_metrics_.height
+           ? DisplayOrientation::kLandscape
+           : DisplayOrientation::kPortrait);
+  float dpi_x = static_cast<float>(display_metrics_.dpi.x) / 1000.0f;
+  float dpi_y = static_cast<float>(display_metrics_.dpi.y) / 1000.0f;
+  float meters_per_pixel_x = kMetersPerInch / dpi_x;
+  float meters_per_pixel_y = kMetersPerInch / dpi_y;
+  vec2 meters_per_pixel(meters_per_pixel_x, meters_per_pixel_y);
+  double frame_duration_s =
+      static_cast<double>(display_metrics_.vsync_period_ns) / 1000000000.0;
+  // TODO(hendrikw): Hard coding to 3mm.  The Pixel is actually 4mm, but it
+  //                 seems that their tray to lens distance is wrong too, which
+  //                 offsets this, at least for the pixel.
+  float border_size = 0.003f;
+  return DisplayMetrics(screen_size, meters_per_pixel, border_size,
+                        static_cast<float>(frame_duration_s), orientation);
+}
+
+int32_t HardwareComposer::Validate(hwc2_display_t display) {
+  uint32_t num_types;
+  uint32_t num_requests;
+  int32_t error =
+      (int32_t)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 = (int32_t)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));
+}
+
+int32_t HardwareComposer::Present(hwc2_display_t display) {
+  int32_t present_fence;
+  int32_t error = (int32_t)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 == HWC2_ERROR_NONE) {
+    ATRACE_INT("HardwareComposer: VsyncFence", present_fence);
+    retire_fence_fds_.emplace_back(present_fence);
+  } else {
+    ATRACE_INT("HardwareComposer: PresentResult", error);
+  }
+
+  return error;
+}
+
+int32_t HardwareComposer::GetDisplayAttribute(hwc2_display_t display,
+                                              hwc2_config_t config,
+                                              hwc2_attribute_t attribute,
+                                              int32_t* out_value) const {
+  return (int32_t)hwc2_hidl_->getDisplayAttribute(
+      display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value);
+}
+
+int32_t HardwareComposer::GetDisplayMetrics(
+    hwc2_display_t display, hwc2_config_t config,
+    HWCDisplayMetrics* out_metrics) const {
+  int32_t ret = HWC2_ERROR_NONE;
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_WIDTH,
+                            &out_metrics->width);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display width");
+    return ret;
+  }
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_HEIGHT,
+                            &out_metrics->height);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display height");
+    return ret;
+  }
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_VSYNC_PERIOD,
+                            &out_metrics->vsync_period_ns);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display height");
+    return ret;
+  }
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_X,
+                            &out_metrics->dpi.x);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display DPI X");
+    return ret;
+  }
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_Y,
+                            &out_metrics->dpi.y);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display DPI Y");
+    return ret;
+  }
+
+  return HWC2_ERROR_NONE;
+}
+
+void HardwareComposer::Dump(char* buffer, uint32_t* out_size) {
+  std::string debug_str = hwc2_hidl_->dumpDebugInfo();
+  ALOGI("%s", debug_str.c_str());
+
+  if (buffer == nullptr) {
+    *out_size = debug_str.size();
+  } else {
+    std::copy(debug_str.begin(), debug_str.begin() + *out_size, buffer);
+  }
+}
+
+// TODO(skiazyk): Figure out what to do with `is_geometry_changed`. There does
+// not seem to be any equivalent in the HWC2 API, but that doesn't mean its not
+// there.
+void HardwareComposer::PostLayers(bool /*is_geometry_changed*/) {
+  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();
+  }
+
+  // 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: dl[%zu] ctype=0x%08x", i,
+          layers_[i]->GetCompositionType());
+#endif
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  std::vector<Hwc2::IComposerClient::Rect> full_region(1);
+  full_region[0].left = 0;
+  full_region[0].top = 0;
+  full_region[0].right = framebuffer_target_->width();
+  full_region[0].bottom = framebuffer_target_->height();
+
+  ALOGE_IF(ret, "Error setting client target : %d", ret);
+
+  ret = Validate(HWC_DISPLAY_PRIMARY);
+  if (ret) {
+    ALOGE("HardwareComposer::Validate failed; ret=%d", ret);
+    return;
+  }
+
+  ret = Present(HWC_DISPLAY_PRIMARY);
+  if (ret) {
+    ALOGE("HardwareComposer::Present failed; ret=%d", ret);
+    return;
+  }
+
+  std::vector<Hwc2::Layer> out_layers;
+  std::vector<int> out_fences;
+  ret = (int32_t)hwc2_hidl_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers,
+                                              &out_fences);
+  uint32_t num_elements = out_layers.size();
+
+  ALOGE_IF(ret, "HardwareComposer: GetReleaseFences failed; ret=%d", ret);
+
+  // Perform post-frame bookkeeping. Unused layers are a no-op.
+  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<DisplaySurface>> surfaces) {
+  ALOGI("HardwareComposer::SetDisplaySurfaces: surface count=%zd",
+        surfaces.size());
+  std::unique_lock<std::mutex> lock(post_thread_mutex_);
+  active_surfaces_ = std::move(surfaces);
+  active_surfaces_updated_ = true;
+  if (post_thread_enabled_)
+    post_thread_cond_var_.notify_all();
+}
+
+int HardwareComposer::PostThreadPollInterruptible(int event_fd,
+                                                  int requested_events) {
+  pollfd pfd[2] = {
+      {
+          .fd = event_fd,
+          .events = static_cast<short>(requested_events),
+          .revents = 0,
+      },
+      {
+          .fd = post_thread_interrupt_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() {
+  return PostThreadPollInterruptible(primary_display_vsync_event_fd_.Get(),
+                                     // There will be a POLLPRI event on vsync
+                                     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;
+
+    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() + 1000000);
+      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(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.
+  int error = dvrSetSchedulerClass(0, "graphics:high");
+  LOG_ALWAYS_FATAL_IF(
+      error < 0,
+      "HardwareComposer::PostThread: Failed to set scheduler class: %s",
+      strerror(-error));
+  error = dvrSetCpuPartition(0, "/system/performance");
+  LOG_ALWAYS_FATAL_IF(
+      error < 0,
+      "HardwareComposer::PostThread: Failed to set cpu partition: %s",
+      strerror(-error));
+
+#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);
+
+  compositor_surfaces_.reserve(2);
+
+  constexpr int kFrameTimeBacklogMax = 2;
+  frame_time_backlog_.reserve(kFrameTimeBacklogMax);
+
+  // Storage for retrieving fence info.
+  FenceInfoBuffer fence_info_buffer;
+
+  bool was_running = false;
+
+  while (1) {
+    ATRACE_NAME("HardwareComposer::PostThread");
+
+    {
+      std::unique_lock<std::mutex> lock(post_thread_mutex_);
+      while (!post_thread_enabled_ || post_thread_quit_requested_ ||
+             !PostThreadHasWork()) {
+        if (was_running) {
+          const char* pause_reason = "unknown";
+          if (!post_thread_enabled_)
+            pause_reason = "disabled";
+          else if (post_thread_quit_requested_)
+            pause_reason = "quit requested";
+          else if (!PostThreadHasWork())
+            pause_reason = "no work";
+          ALOGI("VrHwcPost thread paused. Reason: %s.", pause_reason);
+          OnPostThreadPaused();
+          was_running = false;
+        }
+        post_thread_running_ = false;
+        post_thread_cond_var_.notify_all();
+        if (post_thread_quit_requested_)
+          return;
+        post_thread_cond_var_.wait(lock);
+      }
+      post_thread_running_ = true;
+    }
+
+    if (!was_running) {
+      ALOGI("VrHwcPost thread resumed");
+      OnPostThreadResumed();
+      was_running = true;
+    }
+
+    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());
+
+      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);
+    }
+
+    bool layer_config_changed = UpdateLayerConfig();
+
+    if (!was_running || layer_config_changed) {
+      frame_time_history_.ResetWithSeed(
+          GuessFrameTime(compositor_surfaces_.size()));
+      frame_time_backlog_.clear();
+    } else {
+      UpdateFrameTimeHistory(&frame_time_backlog_, kFrameTimeBacklogMax,
+                             &fence_info_buffer, &frame_time_history_);
+    }
+
+    // Get our current best estimate at how long the next frame will take to
+    // render, based on how long previous frames took to render. Use this
+    // estimate to decide when to wake up for EDS.
+    int64_t frame_time_estimate =
+        frame_time_history_.GetSampleCount() == 0
+            ? GuessFrameTime(compositor_surfaces_.size())
+            : frame_time_history_.GetAverage();
+    frame_time_estimate = std::max(frame_time_estimate, kFrameTimeEstimateMin);
+    DebugHudData::data.hwc_latency = frame_time_estimate;
+
+    // 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,
+                      vsync_count_);
+
+    {
+      // Sleep until async EDS wakeup time.
+      ATRACE_NAME("sleep");
+
+      int64_t display_time_est = vsync_timestamp + ns_per_frame;
+      int64_t now = GetSystemClockNs();
+      int64_t frame_finish_time_est = now + frame_time_estimate;
+      int64_t sleep_time_ns = display_time_est - now - frame_time_estimate;
+
+      ATRACE_INT64("sleep_time_ns", sleep_time_ns);
+      if (frame_finish_time_est - display_time_est >= kFrameSkipThresholdNs) {
+        ATRACE_INT("frame_skip_count", ++frame_skip_count_);
+        ALOGE(
+            "HardwareComposer::PostThread: Missed frame schedule, drop "
+            "frame. Expected frame miss: %.1fms",
+            static_cast<double>(frame_finish_time_est - display_time_est) /
+                1000000);
+
+        // There are several reasons we might skip a frame, but one possibility
+        // is we mispredicted the frame time. Clear out the frame time history.
+        frame_time_history_.ResetWithSeed(
+            GuessFrameTime(compositor_surfaces_.size()));
+        frame_time_backlog_.clear();
+        DebugHudData::data.hwc_frame_stats.SkipFrame();
+
+        continue;
+      } else {
+        // Make the transition more obvious in systrace when the frame skip
+        // happens above.
+        ATRACE_INT("frame_skip_count", 0);
+      }
+
+      if (sleep_time_ns > 0) {
+        error = SleepUntil(display_time_est - frame_time_estimate);
+        ALOGE_IF(error < 0, "HardwareComposer::PostThread: Failed to sleep: %s",
+                 strerror(-error));
+        if (error == kPostThreadInterrupted)
+          continue;
+      }
+    }
+
+    DebugHudData::data.hwc_frame_stats.AddFrame();
+
+    int64_t frame_start_time = GetSystemClockNs();
+
+    // Setup the output buffer for the compositor. This needs to happen before
+    // you draw with the compositor.
+    if (gpu_layer_ != nullptr) {
+      gpu_layer_->UpdateDirectBuffer(compositor_.GetBuffer());
+    }
+
+    // Call PostLayers now before performing the GL code for the compositor to
+    // avoid missing the deadline that can cause the lower-level hwc to get
+    // permanently backed up.
+    PostLayers(layer_config_changed);
+
+    PostCompositorBuffers();
+
+    if (gpu_layer_ != nullptr) {
+      // Note, with scanline racing, this draw is timed along with the post
+      // layers to finish just in time.
+      LocalHandle frame_fence_fd;
+      compositor_.DrawFrame(vsync_count_ + 1, &frame_fence_fd);
+      if (frame_fence_fd) {
+        LOG_ALWAYS_FATAL_IF(frame_time_backlog_.size() >= kFrameTimeBacklogMax,
+                            "Frame time backlog exceeds capacity");
+        frame_time_backlog_.push_back(
+            {frame_start_time, std::move(frame_fence_fd)});
+      }
+    } else if (!layer_config_changed) {
+      frame_time_history_.AddSample(GetSystemClockNs() - frame_start_time);
+    }
+
+    HandlePendingScreenshots();
+  }
+}
+
+bool HardwareComposer::UpdateLayerConfig() {
+  std::vector<std::shared_ptr<DisplaySurface>> old_display_surfaces;
+  {
+    std::lock_guard<std::mutex> lock(post_thread_mutex_);
+    if (!active_surfaces_updated_)
+      return false;
+    old_display_surfaces = display_surfaces_;
+    display_surfaces_ = active_surfaces_;
+    active_surfaces_updated_ = false;
+  }
+
+  DebugHudData::data.ResetLayers();
+
+  // Figure out whether we need to update hardware layers. If this surface
+  // change does not add or remove hardware layers we can avoid display hiccups
+  // by gracefully updating only the GPU compositor layers.
+  int old_gpu_layer_count = 0;
+  int new_gpu_layer_count = 0;
+  bool hardware_layers_need_update = false;
+  // Look for new hardware layers and count new GPU layers.
+  for (const auto& surface : display_surfaces_) {
+    if (!(surface->flags() &
+          DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION))
+      ++new_gpu_layer_count;
+    else if (std::find(old_display_surfaces.begin(), old_display_surfaces.end(),
+                       surface) == old_display_surfaces.end())
+      // This is a new hardware layer, we need to update.
+      hardware_layers_need_update = true;
+  }
+  // Look for deleted hardware layers or compositor layers.
+  for (const auto& surface : old_display_surfaces) {
+    if (!(surface->flags() &
+          DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION))
+      ++old_gpu_layer_count;
+    else if (std::find(display_surfaces_.begin(), display_surfaces_.end(),
+                       surface) == display_surfaces_.end())
+      // This is a deleted hardware layer, we need to update.
+      hardware_layers_need_update = true;
+  }
+  // Check for compositor hardware layer transition.
+  if ((!old_gpu_layer_count && new_gpu_layer_count) ||
+      (old_gpu_layer_count && !new_gpu_layer_count))
+    hardware_layers_need_update = true;
+
+  // Set the chosen layer order for all surfaces.
+  for (size_t i = 0; i < display_surfaces_.size(); ++i) {
+    display_surfaces_[i]->SetLayerOrder(static_cast<int>(i));
+  }
+
+  // Update compositor layers.
+  {
+    ATRACE_NAME("UpdateLayerConfig_GpuLayers");
+    compositor_.UpdateSurfaces(display_surfaces_);
+    compositor_surfaces_.clear();
+    for (size_t i = 0; i < display_surfaces_.size(); ++i) {
+      const auto& surface = display_surfaces_[i];
+      if (!(surface->flags() &
+            DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION)) {
+        compositor_surfaces_.push_back(surface);
+      }
+    }
+  }
+
+  if (!hardware_layers_need_update)
+    return true;
+
+  // Update hardware layers.
+
+  ATRACE_NAME("UpdateLayerConfig_HwLayers");
+
+  // Update the display layers in a non-destructive fashion.
+
+  // Create a map from surface id to hardware layer
+  std::map<int, Layer*> display_surface_layers;
+
+  for (size_t i = 0; i < active_layer_count_; ++i) {
+    auto layer = layers_[i];
+    int surface_id = layer->GetSurfaceId();
+
+    auto found =
+        std::find_if(display_surfaces_.begin(), display_surfaces_.end(),
+                     [surface_id](const auto& surface) {
+                       return surface->surface_id() == surface_id;
+                     });
+
+    if (found != display_surfaces_.end()) {
+      display_surface_layers[surface_id] = layer;
+    }
+  }
+
+  bool has_gpu_layer = std::any_of(
+      display_surfaces_.begin(), display_surfaces_.end(),
+      [](const auto& surface) {
+        return !(surface->flags() &
+                 DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION);
+      });
+
+  if (!has_gpu_layer) {
+    gpu_layer_ = nullptr;
+  }
+
+  auto is_layer_active = [&display_surface_layers, has_gpu_layer](auto layer) {
+    int surface_id = layer->GetSurfaceId();
+    if (surface_id >= 0) {
+      return display_surface_layers.count(surface_id) > 0;
+    } else {
+      return has_gpu_layer;
+    }
+  };
+
+  // Compress the in-use layers to the top of the list
+  auto part = std::partition(
+      layers_.begin(), layers_.begin() + active_layer_count_, is_layer_active);
+
+  size_t new_active_layer_count = part - layers_.begin();
+
+  // Clear any unused layers
+  for (size_t i = new_active_layer_count; i < active_layer_count_; ++i) {
+    layers_[i]->Reset();
+  }
+
+  active_layer_count_ = new_active_layer_count;
+
+  bool gpu_layer_applied = false;
+
+  // Create/update all of the hardware layers
+  for (size_t i = 0; i < display_surfaces_.size(); ++i) {
+    const auto& surface = display_surfaces_[i];
+    bool is_hw_surface =
+        surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION;
+    hwc2_blend_mode_t blending =
+        i == 0 ? HWC2_BLEND_MODE_NONE : HWC2_BLEND_MODE_COVERAGE;
+
+    DebugHudData::data.SetLayerInfo(
+        i, surface->width(), surface->height(),
+        !!(surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2));
+
+    if (!is_hw_surface && gpu_layer_applied) {
+      continue;
+    }
+
+    Layer* target_layer;
+    bool existing_layer = false;
+
+    if (is_hw_surface) {
+      auto it = display_surface_layers.find(surface->surface_id());
+
+      if (it != display_surface_layers.end()) {
+        target_layer = it->second;
+        existing_layer = true;
+      }
+    } else if (gpu_layer_ != nullptr) {
+      target_layer = gpu_layer_;
+      existing_layer = true;
+    }
+
+    if (!existing_layer) {
+      if (active_layer_count_ >= kMaxHardwareLayers) {
+        ALOGI("HardwareComposer: More than %d hardware layers requested.",
+              kMaxHardwareLayers);
+        break;
+      } else {
+        target_layer = layers_[active_layer_count_];
+        ++active_layer_count_;
+      }
+
+      ALOGD_IF(TRACE,
+               "HardwareComposer::UpdateLayerConfig: (new) surface_id=%d -> "
+               "layer=%zd",
+               surface->surface_id(), i);
+
+      if (is_hw_surface) {
+        target_layer->Setup(surface, blending, display_transform_,
+                            HWC2_COMPOSITION_DEVICE, i);
+      } else {
+        gpu_layer_ = target_layer;
+        target_layer->Setup(compositor_.GetBuffer(), blending,
+                            display_transform_, HWC2_COMPOSITION_DEVICE, i);
+      }
+    } else {
+      ALOGD_IF(TRACE,
+               "HardwareComposer::UpdateLayerConfig: (retained) surface_id=%d "
+               "-> layer=%zd",
+               surface->surface_id(), i);
+
+      target_layer->SetBlending(blending);
+      target_layer->SetZOrderIndex(i);
+      target_layer->UpdateLayerSettings();
+    }
+
+    gpu_layer_applied = !is_hw_surface;
+  }
+
+  ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers",
+           active_layer_count_);
+
+  return true;
+}
+
+void HardwareComposer::PostCompositorBuffers() {
+  ATRACE_NAME("PostCompositorBuffers");
+  for (const auto& surface : compositor_surfaces_) {
+    compositor_.PostBuffer(surface);
+  }
+}
+
+void HardwareComposer::UpdateFrameTimeHistory(
+    std::vector<FrameTimeMeasurementRecord>* backlog, int backlog_max,
+    FenceInfoBuffer* fence_info_buffer, FrameTimeHistory* history) {
+  while (!backlog->empty()) {
+    const auto& frame_time_record = backlog->front();
+    int64_t end_time = 0;
+    bool frame_finished = CheckFrameFinished(frame_time_record.fence.Get(),
+                                             fence_info_buffer, &end_time);
+    if (frame_finished) {
+      int64_t frame_duration = end_time - frame_time_record.start_time;
+      history->AddSample(frame_duration);
+      // Our backlog is tiny (2 elements), so erasing from the front is ok
+      backlog->erase(backlog->begin());
+    } else {
+      break;
+    }
+  }
+
+  if (backlog->size() == static_cast<size_t>(backlog_max)) {
+    // Yikes, something must've gone wrong if our oldest frame hasn't finished
+    // yet. Give up on waiting for it.
+    const auto& stale_frame_time_record = backlog->front();
+    int64_t frame_duration =
+        GetSystemClockNs() - stale_frame_time_record.start_time;
+    backlog->erase(backlog->begin());
+    history->AddSample(frame_duration);
+    ALOGW("Frame didn't finish after %.1fms",
+          static_cast<double>(frame_duration) / 1000000);
+  }
+}
+
+bool HardwareComposer::CheckFrameFinished(int frame_fence_fd,
+                                          FenceInfoBuffer* fence_info_buffer,
+                                          int64_t* timestamp) {
+  int result = -1;
+  int sync_result = sync_wait(frame_fence_fd, 0);
+  if (sync_result == 0) {
+    result =
+        GetFenceSignaledTimestamp(frame_fence_fd, fence_info_buffer, timestamp);
+    if (result < 0) {
+      ALOGE("Failed getting signaled timestamp from fence");
+    }
+  } else if (errno != ETIME) {
+    ALOGE("sync_wait on frame fence failed");
+  }
+  return result >= 0;
+}
+
+void HardwareComposer::HandlePendingScreenshots() {
+  // Take a screenshot of the requested layer, if available.
+  // TODO(eieio): Look into using virtual displays to composite the layer stack
+  // into a single output buffer that can be returned to the screenshot clients.
+  if (active_layer_count_ > 0) {
+    if (auto screenshot_service = ScreenshotService::GetInstance()) {
+      if (screenshot_service->IsScreenshotRequestPending()) {
+        ATRACE_NAME("screenshot");
+        screenshot_service->TakeIfNeeded(layers_, compositor_);
+      }
+    } else {
+      ALOGW(
+          "HardwareComposer::HandlePendingScreenshots: Failed to get "
+          "screenshot service!");
+    }
+  }
+}
+
+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);
+  }
+}
+
+Layer::Layer()
+    : hwc2_hidl_(nullptr),
+      surface_index_(-1),
+      hardware_composer_layer_(0),
+      display_metrics_(nullptr),
+      blending_(HWC2_BLEND_MODE_NONE),
+      transform_(HWC_TRANSFORM_NONE),
+      composition_type_(HWC2_COMPOSITION_DEVICE),
+      surface_rect_functions_applied_(false) {}
+
+void Layer::Initialize(Hwc2::Composer* hwc2_hidl, HWCDisplayMetrics* metrics) {
+  hwc2_hidl_ = hwc2_hidl;
+  display_metrics_ = metrics;
+}
+
+void Layer::Reset() {
+  const int ret = acquired_buffer_.Release(std::move(release_fence_));
+  ALOGE_IF(ret < 0, "Layer::Reset: failed to release buffer: %s",
+           strerror(-ret));
+
+  if (hwc2_hidl_ != nullptr && hardware_composer_layer_) {
+    hwc2_hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
+    hardware_composer_layer_ = 0;
+  }
+
+  surface_index_ = static_cast<size_t>(-1);
+  blending_ = HWC2_BLEND_MODE_NONE;
+  transform_ = HWC_TRANSFORM_NONE;
+  composition_type_ = HWC2_COMPOSITION_DEVICE;
+  direct_buffer_ = nullptr;
+  surface_ = nullptr;
+  acquire_fence_fd_.Close();
+  surface_rect_functions_applied_ = false;
+}
+
+void Layer::Setup(const std::shared_ptr<DisplaySurface>& surface,
+                  hwc2_blend_mode_t blending, hwc_transform_t transform,
+                  hwc2_composition_t composition_type, size_t index) {
+  Reset();
+  surface_index_ = index;
+  surface_ = surface;
+  blending_ = blending;
+  transform_ = transform;
+  composition_type_ = composition_type;
+  CommonLayerSetup();
+}
+
+void Layer::Setup(const std::shared_ptr<IonBuffer>& buffer,
+                  hwc2_blend_mode_t blending, hwc_transform_t transform,
+                  hwc2_composition_t composition_type, size_t z_order) {
+  Reset();
+  surface_index_ = z_order;
+  direct_buffer_ = buffer;
+  blending_ = blending;
+  transform_ = transform;
+  composition_type_ = composition_type;
+  CommonLayerSetup();
+}
+
+void Layer::UpdateDirectBuffer(const std::shared_ptr<IonBuffer>& buffer) {
+  direct_buffer_ = buffer;
+}
+
+void Layer::SetBlending(hwc2_blend_mode_t blending) { blending_ = blending; }
+
+void Layer::SetZOrderIndex(int z_index) { surface_index_ = z_index; }
+
+IonBuffer* Layer::GetBuffer() {
+  if (direct_buffer_)
+    return direct_buffer_.get();
+  else if (acquired_buffer_.IsAvailable())
+    return acquired_buffer_.buffer()->buffer();
+  else
+    return nullptr;
+}
+
+void Layer::UpdateLayerSettings() {
+  if (!IsLayerSetup()) {
+    ALOGE("HardwareComposer: Trying to update layers data on an unused layer.");
+    return;
+  }
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  hwc2_display_t display = HWC_DISPLAY_PRIMARY;
+
+  ret = (int32_t)hwc2_hidl_->setLayerCompositionType(
+      display, hardware_composer_layer_,
+      (Hwc2::IComposerClient::Composition)composition_type_);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer composition type : %d",
+           ret);
+  // ret = (int32_t) hwc2_hidl_->setLayerTransform(display,
+  // hardware_composer_layer_,
+  //                                    (Hwc2::IComposerClient::Transform)
+  //                                    transform_);
+  // ALOGE_IF(ret, "HardwareComposer: Error setting layer transform : %d", ret);
+
+  // ret = hwc2_funcs_->set_layer_blend_mode_fn_(
+  //    hardware_composer_device_, display, hardware_composer_layer_,
+  //    blending_);
+  ret = (int32_t)hwc2_hidl_->setLayerBlendMode(
+      display, hardware_composer_layer_,
+      (Hwc2::IComposerClient::BlendMode)blending_);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer blend mode : %d", ret);
+
+  Hwc2::IComposerClient::Rect display_frame;
+  display_frame.left = 0;
+  display_frame.top = 0;
+  display_frame.right = display_metrics_->width;
+  display_frame.bottom = display_metrics_->height;
+  ret = (int32_t)hwc2_hidl_->setLayerDisplayFrame(
+      display, hardware_composer_layer_, display_frame);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer display frame : %d",
+           ret);
+
+  std::vector<Hwc2::IComposerClient::Rect> visible_region(1);
+  visible_region[0] = display_frame;
+  ret = (int32_t)hwc2_hidl_->setLayerVisibleRegion(
+      display, hardware_composer_layer_, visible_region);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer visible region : %d",
+           ret);
+
+  ret = (int32_t)hwc2_hidl_->setLayerPlaneAlpha(display,
+                                                hardware_composer_layer_, 1.0f);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer plane alpha : %d", ret);
+
+  ret = (int32_t)hwc2_hidl_->setLayerZOrder(display, hardware_composer_layer_,
+                                            surface_index_);
+  ALOGE_IF(ret, "HardwareComposer: Error, setting z order index : %d", ret);
+}
+
+void Layer::CommonLayerSetup() {
+  int32_t ret = (int32_t)hwc2_hidl_->createLayer(HWC_DISPLAY_PRIMARY,
+                                                 &hardware_composer_layer_);
+
+  ALOGE_IF(ret,
+           "HardwareComposer: Failed to create layer on primary display : %d",
+           ret);
+
+  UpdateLayerSettings();
+}
+
+void Layer::Prepare() {
+  int right, bottom;
+  sp<GraphicBuffer> handle;
+
+  if (surface_) {
+    // Only update the acquired buffer when one is either available or this is
+    // the first time through.
+    if (surface_->IsBufferAvailable()) {
+      // If we previously set this to a solid color layer to stall for time,
+      // revert it to a device layer.
+      if (acquired_buffer_.IsEmpty() &&
+          composition_type_ != HWC2_COMPOSITION_DEVICE) {
+        composition_type_ = HWC2_COMPOSITION_DEVICE;
+        hwc2_hidl_->setLayerCompositionType(
+            HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+            (Hwc2::IComposerClient::Composition)HWC2_COMPOSITION_DEVICE);
+      }
+
+      DebugHudData::data.AddLayerFrame(surface_index_);
+      acquired_buffer_.Release(std::move(release_fence_));
+      acquired_buffer_ = surface_->AcquireCurrentBuffer();
+
+      // 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 a frame to catch up by checking if
+      // the next frame is also available.
+      if (surface_->IsBufferAvailable()) {
+        DebugHudData::data.SkipLayerFrame(surface_index_);
+        ATRACE_NAME("DropToCatchUp");
+        ATRACE_ASYNC_END("BufferPost", acquired_buffer_.buffer()->id());
+        acquired_buffer_ = surface_->AcquireCurrentBuffer();
+      }
+      ATRACE_ASYNC_END("BufferPost", acquired_buffer_.buffer()->id());
+    } else if (acquired_buffer_.IsEmpty()) {
+      // While we are waiting for a buffer, set this to be an empty layer
+      if (composition_type_ != HWC2_COMPOSITION_SOLID_COLOR) {
+        composition_type_ = HWC2_COMPOSITION_SOLID_COLOR;
+        hwc2_hidl_->setLayerCompositionType(
+            HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+            (Hwc2::IComposerClient::Composition)HWC2_COMPOSITION_SOLID_COLOR);
+
+        Hwc2::IComposerClient::Color layer_color = {
+            0, 0, 0, 0,
+        };
+        hwc2_hidl_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+                                  layer_color);
+      }
+      return;
+    }
+    right = acquired_buffer_.buffer()->width();
+    bottom = acquired_buffer_.buffer()->height();
+    handle = acquired_buffer_.buffer()->buffer()->buffer();
+    acquire_fence_fd_.Reset(acquired_buffer_.ClaimAcquireFence().Release());
+  } else {
+    // TODO(jwcai) Note: this is the GPU compositor's layer, and we need the
+    // mechanism to accept distorted layers from VrCore.
+    right = direct_buffer_->width();
+    bottom = direct_buffer_->height();
+    handle = direct_buffer_->buffer();
+    acquire_fence_fd_.Close();
+  }
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  if (composition_type_ == HWC2_COMPOSITION_DEVICE) {
+    ret = (int32_t)hwc2_hidl_->setLayerBuffer(HWC_DISPLAY_PRIMARY,
+                                              hardware_composer_layer_, 0,
+                                              handle,
+                                              acquire_fence_fd_.Get());
+
+    ALOGE_IF(ret, "HardwareComposer: Error setting layer buffer : %d", ret);
+  }
+
+  if (!surface_rect_functions_applied_) {
+    Hwc2::IComposerClient::FRect crop_rect = {
+        0, 0, static_cast<float>(right), static_cast<float>(bottom),
+    };
+    hwc2_hidl_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY,
+                                   hardware_composer_layer_, crop_rect);
+
+    ALOGE_IF(ret, "HardwareComposer: Error setting layer source crop : %d",
+             ret);
+
+// TODO(skiazyk): why is this ifdef'd out. Is if a driver-specific issue where
+// it must/cannot be called?
+#ifdef QCOM_BSP
+    hwc_rect_t damage_rect = {
+        0, 0, right, bottom,
+    };
+    hwc_region_t damage = {
+        1, &damage_rect,
+    };
+    // ret = hwc2_funcs_->set_layer_surface_damage(
+    //    hardware_composer_device_, HWC_DISPLAY_PRIMARY,
+    //    hardware_composer_layer_, damage);
+    // uses a std::vector as the listing
+    // hwc2_hidl_->setLayerSurfaceDamage(HWC_DISPLAY_PRIMARY,
+    // hardware_composer_layer_, vector here);
+
+    ALOGE_IF(ret, "HardwareComposer: Error settings layer surface damage : %d",
+             ret);
+#endif
+
+    surface_rect_functions_applied_ = true;
+  }
+}
+
+void Layer::Finish(int release_fence_fd) {
+  release_fence_.Reset(release_fence_fd);
+}
+
+void Layer::Drop() { acquire_fence_fd_.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..e570cb6
--- /dev/null
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -0,0 +1,468 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
+
+#include <log/log.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer2.h>
+
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/sync_util.h>
+
+#include <array>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/frame_time_history.h>
+#include <private/dvr/sync_util.h>
+
+#include "acquired_buffer.h"
+#include "compositor.h"
+#include "display_surface.h"
+
+#include "DisplayHardware/ComposerHal.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 the hardware composer layer and display metrics that this Layer should
+  // use each Prepare cycle. This class does not own either of these pointers,
+  // which MUST remain valid for its lifetime. This method MUST be called once
+  // in the life of the instance before any other method is valid to call.
+  void Initialize(Hwc2::Composer* hwc2_hidl, 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
+  // will automatically handle 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 DisplaySurface array.
+  void Setup(const std::shared_ptr<DisplaySurface>& surface,
+             hwc2_blend_mode_t blending, hwc_transform_t transform,
+             hwc2_composition_t composition_type, size_t index);
+
+  // 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,
+             hwc2_blend_mode_t blending, hwc_transform_t transform,
+             hwc2_composition_t 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 UpdateDirectBuffer(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(hwc2_blend_mode_t blending);
+
+  // Sets the Z-order of this layer
+  void SetZOrderIndex(int surface_index);
+
+  // 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();
+
+  hwc2_composition_t GetCompositionType() const { return composition_type_; }
+
+  hwc2_layer_t GetLayerHandle() const { return hardware_composer_layer_; }
+
+  bool UsesDirectBuffer() const { return direct_buffer_ != nullptr; }
+
+  bool IsLayerSetup() const {
+    return direct_buffer_ != nullptr || surface_ != nullptr;
+  }
+
+  // Applies all of the settings to this layer using the hwc functions
+  void UpdateLayerSettings();
+
+  int GetSurfaceId() const {
+    if (surface_ != nullptr) {
+      return surface_->surface_id();
+    } else {
+      return -1;
+    }
+  }
+
+ private:
+  void CommonLayerSetup();
+
+  Hwc2::Composer* hwc2_hidl_;
+
+  // Original display surface array index for tracking purposes.
+  size_t surface_index_;
+
+  // The hardware composer layer and metrics to use during the prepare cycle.
+  hwc2_layer_t hardware_composer_layer_;
+  HWCDisplayMetrics* display_metrics_;
+
+  // Layer properties used to setup the hardware composer layer during the
+  // Prepare phase.
+  hwc2_blend_mode_t blending_;
+  hwc_transform_t transform_;
+  hwc2_composition_t composition_type_;
+
+  // These two members are mutually exclusive. When direct_buffer_ is set the
+  // Layer gets its contents directly from that buffer; when surface_ is set the
+  // Layer gets it contents from the surface's buffer train.
+  std::shared_ptr<IonBuffer> direct_buffer_;
+  std::shared_ptr<DisplaySurface> surface_;
+
+  // State when associated with a display surface.
+  AcquiredBuffer acquired_buffer_;
+  pdx::LocalHandle release_fence_;
+
+  pdx::LocalHandle acquire_fence_fd_;
+  bool surface_rect_functions_applied_;
+
+  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)>;
+
+  // Since there is no universal way to query the number of hardware layers,
+  // just set it to 4 for now.
+  static constexpr int kMaxHardwareLayers = 4;
+
+  HardwareComposer();
+  HardwareComposer(Hwc2::Composer* hidl);
+  ~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.
+  DisplayMetrics GetHmdDisplayMetrics() const;
+
+  int32_t GetDisplayAttribute(hwc2_display_t display, hwc2_config_t config,
+                              hwc2_attribute_t attributes,
+                              int32_t* out_value) const;
+  int32_t GetDisplayMetrics(hwc2_display_t display, hwc2_config_t config,
+                            HWCDisplayMetrics* out_metrics) const;
+  void Dump(char* buffer, uint32_t* out_size);
+
+  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_;
+  }
+
+  // Set the display surface stack to compose to the display each frame.
+  void SetDisplaySurfaces(
+      std::vector<std::shared_ptr<DisplaySurface>> surfaces);
+
+  Compositor* GetCompositor() { return &compositor_; }
+
+  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();
+    }
+  };
+
+  int32_t Validate(hwc2_display_t display);
+  int32_t Present(hwc2_display_t display);
+
+  void SetBacklightBrightness(int brightness);
+
+  void PostLayers(bool is_geometry_changed);
+  void PostThread();
+
+  // Check to see if we have a value written to post_thread_interrupt_event_fd_,
+  // indicating a control thread interrupted the post thread. This clears the
+  // post_thread_interrupt_event_fd_ state in the process. Returns true if an
+  // interrupt was requested.
+  bool CheckPostThreadInterruptEventFd();
+  // 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(int 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; }
+
+  // Returns true if the layer config changed, false otherwise
+  bool UpdateLayerConfig();
+  void PostCompositorBuffers();
+
+  // Return true if the post thread has work to do (i.e. there are visible
+  // surfaces to post to the screen). Must be called with post_thread_mutex_
+  // locked. Called only from the post thread.
+  bool PostThreadHasWork();
+
+  // 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();
+
+  struct FrameTimeMeasurementRecord {
+    int64_t start_time;
+    pdx::LocalHandle fence;
+
+    FrameTimeMeasurementRecord(FrameTimeMeasurementRecord&&) = default;
+    FrameTimeMeasurementRecord& operator=(FrameTimeMeasurementRecord&&) =
+        default;
+    FrameTimeMeasurementRecord(const FrameTimeMeasurementRecord&) = delete;
+    FrameTimeMeasurementRecord& operator=(const FrameTimeMeasurementRecord&) =
+        delete;
+  };
+
+  void UpdateFrameTimeHistory(std::vector<FrameTimeMeasurementRecord>* backlog,
+                              int backlog_max,
+                              FenceInfoBuffer* fence_info_buffer,
+                              FrameTimeHistory* history);
+
+  // Returns true if the frame finished rendering, false otherwise. If the frame
+  // finished the frame end time is stored in timestamp. Doesn't block.
+  bool CheckFrameFinished(int frame_fence_fd,
+                          FenceInfoBuffer* fence_info_buffer,
+                          int64_t* timestamp);
+
+  void HandlePendingScreenshots();
+
+  bool initialized_;
+
+  // Hardware composer HAL device.
+  std::unique_ptr<Hwc2::Composer> hwc2_hidl_;
+  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_t display_transform_;
+
+  // Buffer for the background layer required by hardware composer.
+  std::shared_ptr<IonBuffer> framebuffer_target_;
+
+  // Protects access to variables used by the post thread and one of the control
+  // threads (either the vr flinger dispatcher thread or the surface flinger
+  // main thread). This includes active_surfaces_, active_surfaces_updated_,
+  // post_thread_enabled_, post_thread_running_, and
+  // post_thread_quit_requested_.
+  std::mutex post_thread_mutex_;
+
+  // Surfaces configured by the display manager. Written by the vr flinger
+  // dispatcher thread, read by the post thread.
+  std::vector<std::shared_ptr<DisplaySurface>> active_surfaces_;
+  // active_surfaces_updated_ is set to true by the vr flinger dispatcher thread
+  // when the list of active surfaces changes. active_surfaces_updated_ will be
+  // set back to false by the post thread when it processes the update.
+  bool active_surfaces_updated_;
+
+  // The surfaces displayed by the post thread. Used exclusively by the post
+  // thread.
+  std::vector<std::shared_ptr<DisplaySurface>> display_surfaces_;
+
+  // The surfaces rendered by the compositor. Used exclusively by the post
+  // thread.
+  std::vector<std::shared_ptr<DisplaySurface>> compositor_surfaces_;
+
+  // Layer array for handling buffer flow into hardware composer layers.
+  // Note that the first array is the actual storage for the layer objects,
+  // and the latter is an array of pointers, which can be freely re-arranged
+  // without messing up the underlying objects.
+  std::array<Layer, kMaxHardwareLayers> layer_storage_;
+  std::array<Layer*, kMaxHardwareLayers> layers_;
+  size_t active_layer_count_;
+
+  // Set by the Post thread to the index of the GPU compositing output
+  // buffer in the layers_ array.
+  Layer* gpu_layer_;
+
+  // 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 post processing and the results to hardware composer.
+  std::thread post_thread_;
+
+  // Set to true if the post thread is allowed to run. Surface flinger and vr
+  // flinger share access to the display, and vr flinger shouldn't use the
+  // display while surface flinger is using it. While surface flinger owns the
+  // display, post_thread_enabled_ will be set to false to indicate the post
+  // thread shouldn't run.
+  bool post_thread_enabled_;
+  // Set to true by the post thread if it's currently running.
+  bool post_thread_running_;
+  // Set to true if the post thread should quit. Only set when destroying the
+  // HardwareComposer instance.
+  bool post_thread_quit_requested_;
+  // Used to wake the post thread up while it's waiting for vsync or sleeping
+  // until EDS preemption, for faster transition to the paused state.
+  pdx::LocalHandle post_thread_interrupt_event_fd_;
+  // Used to communicate between the control thread and the post thread.
+  std::condition_variable post_thread_cond_var_;
+
+  // 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_;
+
+  // Vsync count since display on.
+  uint32_t vsync_count_;
+
+  // Counter tracking the number of skipped frames.
+  int frame_skip_count_;
+
+  // After construction, only accessed on post_thread_.
+  Compositor compositor_;
+
+  // 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_;
+
+  // Our history of frame times. This is used to get a better estimate of how
+  // long the next frame will take, to set a schedule for EDS.
+  FrameTimeHistory frame_time_history_;
+
+  // The backlog is used to allow us to start rendering the next frame before
+  // the previous frame has finished, and still get an accurate measurement of
+  // frame duration.
+  std::vector<FrameTimeMeasurementRecord> frame_time_backlog_;
+
+  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/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/screenshot_service.cpp b/libs/vr/libvrflinger/screenshot_service.cpp
new file mode 100644
index 0000000..d14d588
--- /dev/null
+++ b/libs/vr/libvrflinger/screenshot_service.cpp
@@ -0,0 +1,189 @@
+#include "screenshot_service.h"
+
+#include <utils/Trace.h>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/android_filesystem_config.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/trusted_uids.h>
+
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::RemoteMethodError;
+using android::pdx::rpc::RemoteMethodReturn;
+
+namespace android {
+namespace dvr {
+
+ScreenshotService::~ScreenshotService() { instance_ = nullptr; }
+
+pdx::Status<void> ScreenshotService::HandleMessage(pdx::Message& message) {
+  switch (message.GetOp()) {
+    case DisplayScreenshotRPC::GetFormat::Opcode:
+      DispatchRemoteMethod<DisplayScreenshotRPC::GetFormat>(
+          *this, &ScreenshotService::OnGetFormat, message);
+      return {};
+
+    case DisplayScreenshotRPC::TakeScreenshot::Opcode:
+      DispatchRemoteMethod<DisplayScreenshotRPC::TakeScreenshot>(
+          *this, &ScreenshotService::OnTakeScreenshot, message);
+      return {};
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+int ScreenshotService::OnGetFormat(pdx::Message&) {
+  return HAL_PIXEL_FORMAT_RGB_888;
+}
+
+ScreenshotData ScreenshotService::OnTakeScreenshot(pdx::Message& message,
+                                                   int layer_index) {
+  // Also allow AID_SHELL to support vrscreencap commands.
+  if (message.GetEffectiveUserId() != AID_SHELL &&
+      !IsTrustedUid(message.GetEffectiveUserId())) {
+    REPLY_ERROR_RETURN(message, EACCES, {});
+  }
+
+  AddWaiter(std::move(message), layer_index);
+  return {};
+}
+
+void ScreenshotService::AddWaiter(pdx::Message&& message, int layer_index) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  waiters_.emplace_back(std::move(message), layer_index);
+}
+
+void ScreenshotService::TakeIfNeeded(
+    std::array<Layer*, HardwareComposer::kMaxHardwareLayers>& hw_layers,
+    Compositor& compositor) {
+  std::lock_guard<std::mutex> lock(mutex_);
+
+  // Send the buffer contents to all of our waiting clients.
+  for (auto& waiter : waiters_) {
+    if (waiter.IsDone())
+      continue;
+
+    if (waiter.layer_index() == 0) {
+      ALOGE(
+          "ScreenshotService::TakeIfNeeded: Capturing the composited display "
+          "output is not yet supported.");
+
+      waiter.Error(EINVAL);
+      continue;
+    }
+
+    if (waiter.layer_index() > 0) {
+      // Check for hardware layer screenshot requests.
+      // Hardware layers are requested with positive indices starting at 1.
+      const size_t layer_index = static_cast<size_t>(waiter.layer_index() - 1);
+
+      if (layer_index >= hw_layers.size()) {
+        waiter.Error(EINVAL);
+        continue;
+      }
+
+      auto buffer = hw_layers[layer_index]->GetBuffer();
+      if (!buffer) {
+        waiter.Error(ENOBUFS);
+        continue;
+      }
+
+      auto data = compositor.ReadBufferPixels(buffer);
+      if (data.empty()) {
+        waiter.Error(ENOBUFS);
+        continue;
+      }
+
+      Take(&waiter, data.data(), buffer->width(), buffer->height(),
+           buffer->width());
+    } else {
+      // Check for compositor input layer screenshot requests.
+      // Prewarp surfaces are requested with negative indices starting at -1.
+      const size_t layer_index = static_cast<size_t>(-waiter.layer_index() - 1);
+
+      if (layer_index >= compositor.GetLayerCount()) {
+        waiter.Error(EINVAL);
+        continue;
+      }
+
+      int width = 0;
+      int height = 0;
+      auto data = compositor.ReadLayerPixels(layer_index, &width, &height);
+      if (data.empty()) {
+        waiter.Error(ENOBUFS);
+        continue;
+      }
+
+      Take(&waiter, data.data(), width, height, width);
+    }
+  }
+
+  // Reply with error to requests that did not match up with a source layer.
+  for (auto& waiter : waiters_) {
+    if (!waiter.IsDone())
+      waiter.Error(EAGAIN);
+  }
+  waiters_.clear();
+}
+
+void ScreenshotWaiter::Reply(const ScreenshotData& screenshot) {
+  ALOGI("Returning screenshot: size=%zu recv_size=%zu",
+        screenshot.buffer.size(), message_.GetReceiveLength());
+  RemoteMethodReturn<DisplayScreenshotRPC::TakeScreenshot>(message_,
+                                                           screenshot);
+}
+
+void ScreenshotWaiter::Error(int error) { RemoteMethodError(message_, error); }
+
+void ScreenshotService::Take(ScreenshotWaiter* waiter, const void* rgba_data,
+                             int32_t width, int32_t height, int buffer_stride) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+
+  bool is_portrait = height > width;
+  if (is_portrait) {
+    std::swap(width, height);
+  }
+  int response_stride = width;
+
+  // Convert from RGBA to RGB and if in portrait, rotates to landscape; store
+  // the result in the response buffer.
+  ScreenshotData screenshot{width, height,
+                            std::vector<uint8_t>(width * height * 3)};
+
+  const auto rgba_bytes = static_cast<const uint8_t*>(rgba_data);
+  for (int j = 0; j < height; ++j) {
+    for (int i = 0; i < width; ++i) {
+      // If the screenshot is in portrait mode, rotate into landscape mode.
+      const int response_index = is_portrait
+                                     ? (height - j - 1) * response_stride + i
+                                     : j * response_stride + i;
+      const int buffer_index =
+          is_portrait ? i * buffer_stride + j : j * buffer_stride + i;
+      screenshot.buffer[response_index * 3 + 0] =
+          rgba_bytes[buffer_index * 4 + 0];
+      screenshot.buffer[response_index * 3 + 1] =
+          rgba_bytes[buffer_index * 4 + 1];
+      screenshot.buffer[response_index * 3 + 2] =
+          rgba_bytes[buffer_index * 4 + 2];
+    }
+  }
+
+  waiter->Reply(screenshot);
+}
+
+ScreenshotService::ScreenshotService()
+    : BASE("ScreenshotService",
+           Endpoint::Create(DisplayScreenshotRPC::kClientPath)) {
+  instance_ = this;
+}
+
+ScreenshotService* ScreenshotService::GetInstance() { return instance_; }
+
+ScreenshotService* ScreenshotService::instance_ = nullptr;
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/screenshot_service.h b/libs/vr/libvrflinger/screenshot_service.h
new file mode 100644
index 0000000..f59e872
--- /dev/null
+++ b/libs/vr/libvrflinger/screenshot_service.h
@@ -0,0 +1,82 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_SCREENSHOT_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_SCREENSHOT_SERVICE_H_
+
+#include <pdx/rpc/pointer_wrapper.h>
+#include <pdx/service.h>
+#include <private/dvr/ion_buffer.h>
+
+#include <mutex>
+#include <vector>
+
+#include "hardware_composer.h"
+
+namespace android {
+namespace dvr {
+
+class ScreenshotWaiter {
+ public:
+  explicit ScreenshotWaiter(pdx::Message&& message, int layer_index)
+      : message_(std::move(message)), layer_index_(layer_index) {}
+  ScreenshotWaiter(ScreenshotWaiter&&) = default;
+
+  void Reply(const ScreenshotData& screenshot);
+  void Error(int error);
+
+  bool IsDone() const { return message_.replied(); }
+  int layer_index() const { return layer_index_; }
+
+ private:
+  pdx::Message message_;
+  int layer_index_;
+
+  ScreenshotWaiter(const ScreenshotWaiter&) = delete;
+  void operator=(const ScreenshotWaiter&) = delete;
+};
+
+// The screenshot service allows clients to obtain screenshots from displayd.
+class ScreenshotService : public pdx::ServiceBase<ScreenshotService> {
+ public:
+  ~ScreenshotService();
+
+  pdx::Status<void> HandleMessage(pdx::Message& message) override;
+
+  // Returns true if there is a pending screenshot request.
+  bool IsScreenshotRequestPending() const {
+    std::lock_guard<std::mutex> lock(mutex_);
+    return !waiters_.empty();
+  }
+
+  // If any clients are currently waiting for a screenshot, read back the
+  // contents of requested layers and send the resulting
+  // image to the clients.
+  void TakeIfNeeded(
+      std::array<Layer*, HardwareComposer::kMaxHardwareLayers>& hw_layers,
+      Compositor& compositor);
+
+  static ScreenshotService* GetInstance();
+
+ private:
+  friend BASE;
+
+  ScreenshotService();
+
+  void AddWaiter(pdx::Message&& message, int layer_index);
+
+  ScreenshotData OnTakeScreenshot(pdx::Message& message, int index);
+  int OnGetFormat(pdx::Message& message);
+
+  // Copy the given screenshot data into the message reply.
+  void Take(ScreenshotWaiter* waiter, const void* rgba_data, int32_t width,
+            int32_t height, int buffer_stride);
+
+  static ScreenshotService* instance_;
+
+  // Protects access to subsequent member variables.
+  mutable std::mutex mutex_;
+  std::vector<ScreenshotWaiter> waiters_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_SCREENSHOT_SERVICE_H_
diff --git a/libs/vr/libvrflinger/surface_channel.cpp b/libs/vr/libvrflinger/surface_channel.cpp
new file mode 100644
index 0000000..263b382
--- /dev/null
+++ b/libs/vr/libvrflinger/surface_channel.cpp
@@ -0,0 +1,44 @@
+#include "surface_channel.h"
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::Message;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+pdx::Status<void> SurfaceChannel::HandleMessage(Message& message) {
+  switch (message.GetOp()) {
+    case DisplayRPC::GetMetadataBuffer::Opcode:
+      DispatchRemoteMethod<DisplayRPC::GetMetadataBuffer>(
+          *this, &SurfaceChannel::OnGetMetadataBuffer, message);
+      break;
+  }
+
+  return {};
+}
+
+BorrowedChannelHandle SurfaceChannel::OnGetMetadataBuffer(Message& message) {
+  if (EnsureMetadataBuffer()) {
+    return metadata_buffer_->GetChannelHandle().Borrow();
+  } else {
+    REPLY_ERROR_RETURN(message, -ENOMEM, {});
+  }
+}
+
+bool SurfaceChannel::EnsureMetadataBuffer() {
+  if (!metadata_buffer_) {
+    metadata_buffer_ =
+        BufferProducer::CreateUncachedBlob(metadata_size());
+    if (!metadata_buffer_) {
+      ALOGE(
+          "DisplaySurface::EnsureMetadataBuffer: could not allocate metadata "
+          "buffer");
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/surface_channel.h b/libs/vr/libvrflinger/surface_channel.h
new file mode 100644
index 0000000..bb6b1c5
--- /dev/null
+++ b/libs/vr/libvrflinger/surface_channel.h
@@ -0,0 +1,63 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_SURFACE_CHANNEL_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_SURFACE_CHANNEL_H_
+
+#include <pdx/service.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/display_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class DisplayService;
+
+class SurfaceChannel : public pdx::Channel {
+ public:
+  SurfaceChannel(DisplayService* service, int channel_id, SurfaceType type,
+                 size_t metadata_size)
+      : service_(service),
+        surface_id_(channel_id),
+        type_(type),
+        metadata_size_(metadata_size) {}
+
+  ~SurfaceChannel() override = default;
+
+  DisplayService* service() const { return service_; }
+  int surface_id() const { return surface_id_; }
+  SurfaceType type() const { return type_; }
+  size_t metadata_size() const { return metadata_size_; }
+
+  pdx::LocalHandle GetMetadataBufferFd() {
+    return EnsureMetadataBuffer() ? metadata_buffer_->GetBlobFd()
+                                  : pdx::LocalHandle{};
+  }
+
+  // Dispatches surface channel messages to the appropriate handlers. This
+  // handler runs on the displayd message dispatch thread.
+  virtual pdx::Status<void> HandleMessage(pdx::Message& message);
+
+ protected:
+  // Contains the surface metadata.
+  std::shared_ptr<BufferProducer> metadata_buffer_;
+
+  // Returns the metadata buffer for this surface. The first call allocates the
+  // buffer, while subsequent calls return the same buffer.
+  pdx::BorrowedChannelHandle OnGetMetadataBuffer(pdx::Message& message);
+
+  // Allocates the single metadata buffer for this surface unless it is already
+  // allocated. Idempotent when called multiple times.
+  bool EnsureMetadataBuffer();
+
+ private:
+  DisplayService* service_;
+  int surface_id_;
+  SurfaceType type_;
+  size_t metadata_size_;
+
+  SurfaceChannel(const SurfaceChannel&) = delete;
+  void operator=(const SurfaceChannel&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_SURFACE_CHANNEL_H_
diff --git a/libs/vr/libvrflinger/video_compositor.cpp b/libs/vr/libvrflinger/video_compositor.cpp
new file mode 100644
index 0000000..411e3a3
--- /dev/null
+++ b/libs/vr/libvrflinger/video_compositor.cpp
@@ -0,0 +1,131 @@
+#include "video_compositor.h"
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2ext.h>
+
+#include <private/dvr/debug.h>
+#include <private/dvr/display_rpc.h>
+
+namespace android {
+namespace dvr {
+
+VideoCompositor::Texture::Texture(
+    EGLDisplay display, const std::shared_ptr<BufferConsumer>& buffer_consumer)
+    : display_(display),
+      image_(EGL_NO_IMAGE_KHR),
+      texture_id_(0),
+      buffer_consumer_(buffer_consumer) {}
+
+VideoCompositor::Texture::~Texture() {
+  if (image_ != EGL_NO_IMAGE_KHR)
+    eglDestroyImageKHR(display_, image_);
+  if (texture_id_ != 0)
+    glDeleteTextures(1, &texture_id_);
+}
+
+GLuint VideoCompositor::Texture::EnsureTextureReady() {
+  if (!image_) {
+    native_buffer_ = new NativeBuffer(buffer_consumer_);
+    CHECK_GL();
+
+    image_ = eglCreateImageKHR(
+        display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+        static_cast<ANativeWindowBuffer*>(native_buffer_.get()), nullptr);
+    if (!image_) {
+      ALOGE("Failed to create EGLImage.");
+      return 0;
+    }
+
+    glGenTextures(1, &texture_id_);
+    glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_);
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image_);
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
+                    GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
+                    GL_CLAMP_TO_EDGE);
+    CHECK_GL();
+  }
+
+  return texture_id_;
+}
+
+void VideoCompositor::Texture::Release() {
+  const int ret = buffer_consumer_->Release({});
+  if (ret < 0) {
+    ALOGE(
+        "VideoCompositor::Texture::Release: Failed to release buffer, error: "
+        "%s",
+        strerror(-ret));
+  }
+}
+
+VideoCompositor::VideoCompositor(
+    const std::shared_ptr<VideoMeshSurface>& surface,
+    const volatile DisplaySurfaceMetadata* display_surface_metadata)
+    : surface_(surface),
+      consumer_queue_(surface->GetConsumerQueue()),
+      transform_metadata_(display_surface_metadata),
+      active_texture_slot_(-1) {}
+
+GLuint VideoCompositor::GetActiveTextureId(EGLDisplay display) {
+  size_t slot;
+  VideoMeshSurfaceBufferMetadata metadata;
+
+  while (true) {
+    // A native way to pick the active texture: always dequeue all buffers from
+    // the queue until it's empty. This works well as long as video frames are
+    // queued in order from the producer side.
+    // TODO(jwcai) Use |metadata.timestamp_ns| to schedule video frames
+    // accurately.
+    LocalHandle acquire_fence;
+    auto buffer_consumer =
+        consumer_queue_->Dequeue(0, &slot, &metadata, &acquire_fence);
+
+    if (buffer_consumer) {
+      // Create a new texture if it hasn't been created yet, or the same slot
+      // has a new |buffer_consumer|.
+      if (textures_[slot] == nullptr ||
+          textures_[slot]->event_fd() != buffer_consumer->event_fd()) {
+        textures_[slot] =
+            std::unique_ptr<Texture>(new Texture(display, buffer_consumer));
+      }
+
+      if (active_texture_slot_ != static_cast<int>(slot)) {
+        if (active_texture_slot_ >= 0) {
+          // Release the last active texture and move on to use the new one.
+          textures_[active_texture_slot_]->Release();
+        }
+        active_texture_slot_ = slot;
+      }
+    } else {
+      break;
+    }
+  }
+
+  if (active_texture_slot_ < 0) {
+    // No texture is active yet.
+    return 0;
+  }
+
+  return textures_[active_texture_slot_]->EnsureTextureReady();
+}
+
+mat4 VideoCompositor::GetTransform(int eye, size_t render_buffer_index) {
+  volatile const VideoMeshSurfaceMetadata* transform_metadata =
+      surface_->GetMetadataBufferPtr();
+
+  mat4 screen_transform;
+  for (int i = 0; i < 4; ++i) {
+    for (int j = 0; j < 4; ++j) {
+      screen_transform(i, j) =
+          transform_metadata->transform[render_buffer_index][eye].val[i][j];
+    }
+  }
+
+  return screen_transform;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/video_compositor.h b/libs/vr/libvrflinger/video_compositor.h
new file mode 100644
index 0000000..d0e72e1
--- /dev/null
+++ b/libs/vr/libvrflinger/video_compositor.h
@@ -0,0 +1,84 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_COMPOSITOR_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_COMPOSITOR_H_
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <private/dvr/buffer_hub_queue_core.h>
+#include <private/dvr/types.h>
+
+#include <vector>
+
+#include "display_surface.h"
+#include "video_mesh_surface.h"
+
+namespace android {
+namespace dvr {
+
+using pdx::LocalHandle;
+
+// Manages video buffer consumers, texture mapping, and playback timing.
+class VideoCompositor {
+ public:
+  VideoCompositor(
+      const std::shared_ptr<VideoMeshSurface>& surface,
+      const volatile DisplaySurfaceMetadata* display_surface_metadata);
+
+  int surface_id() const { return surface_ ? surface_->surface_id() : -1; }
+
+  // Returns a GL texture id that should be composited by displayd during the
+  // current rendering loop. Note that this function must be called in
+  // displayd's GL context.
+  GLuint GetActiveTextureId(EGLDisplay display);
+
+  // Returns a basic video mesh tranform.
+  mat4 GetTransform(int eye, size_t render_buffer_index);
+
+ private:
+  class Texture {
+   public:
+    Texture(EGLDisplay display,
+            const std::shared_ptr<BufferConsumer>& buffer_consumer);
+    ~Texture();
+
+    // Returns the |event_fd| of the underlying buffer consumer. Caller can use
+    // this to decided whether the Texture need to be recreated for a different
+    // buffer consumer.
+    int event_fd() const { return buffer_consumer_->event_fd(); }
+
+    // Method to map a dvr::BufferConsumer to a GL texture within the current GL
+    // context. If the current Texture object's |image_| hasn't been
+    // initialized, the method will do so based on the |buffer_consumer| and a
+    // new GL texture will be generated, cached, and returned. Otherwise, the
+    // cached |texture_id_| will be returned directly.
+    GLuint EnsureTextureReady();
+
+    // Signal bufferhub that the texture is done rendering so that the buffer
+    // can be re-gained by the producer for future use.
+    void Release();
+
+   private:
+    using NativeBuffer = BufferHubQueueCore::NativeBuffer;
+
+    EGLDisplay display_;
+    EGLImageKHR image_;
+    GLuint texture_id_;
+    sp<NativeBuffer> native_buffer_;
+    std::shared_ptr<BufferConsumer> buffer_consumer_;
+  };
+
+  std::shared_ptr<VideoMeshSurface> surface_;
+  std::shared_ptr<ConsumerQueue> consumer_queue_;
+  std::array<std::unique_ptr<Texture>, BufferHubQueue::kMaxQueueCapacity>
+      textures_;
+
+  const volatile DisplaySurfaceMetadata* transform_metadata_;
+  int active_texture_slot_;
+
+  VideoCompositor(const VideoCompositor&) = delete;
+  void operator=(const VideoCompositor&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_COMPOSITOR_H_
diff --git a/libs/vr/libvrflinger/video_mesh_surface.cpp b/libs/vr/libvrflinger/video_mesh_surface.cpp
new file mode 100644
index 0000000..d915a4a
--- /dev/null
+++ b/libs/vr/libvrflinger/video_mesh_surface.cpp
@@ -0,0 +1,59 @@
+#include "video_mesh_surface.h"
+
+#include <private/dvr/display_rpc.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+VideoMeshSurface::VideoMeshSurface(DisplayService* service, int surface_id)
+    : SurfaceChannel(service, surface_id, SurfaceTypeEnum::VideoMesh,
+                     sizeof(VideoMeshSurfaceMetadata)) {}
+
+VideoMeshSurface::~VideoMeshSurface() {}
+
+pdx::Status<void> VideoMeshSurface::HandleMessage(Message& message) {
+  ATRACE_NAME("VideoMeshSurface::HandleMessage");
+
+  switch (message.GetOp()) {
+    case DisplayRPC::VideoMeshSurfaceCreateProducerQueue::Opcode:
+      DispatchRemoteMethod<DisplayRPC::VideoMeshSurfaceCreateProducerQueue>(
+          *this, &VideoMeshSurface::OnCreateProducerQueue, message);
+      break;
+
+    default:
+      return SurfaceChannel::HandleMessage(message);
+  }
+
+  return {};
+}
+
+std::shared_ptr<ConsumerQueue> VideoMeshSurface::GetConsumerQueue() {
+  if (!consumer_queue_) {
+    ALOGE(
+        "VideoMeshSurface::GetConsumerQueue: consumer_queue is uninitialized.");
+  }
+
+  return consumer_queue_;
+}
+
+LocalChannelHandle VideoMeshSurface::OnCreateProducerQueue(Message& message) {
+  ATRACE_NAME("VideoMeshSurface::OnCreateProducerQueue");
+
+  if (consumer_queue_ != nullptr) {
+    ALOGE(
+        "VideoMeshSurface::OnCreateProducerQueue: A ProdcuerQueue has already "
+        "been created and transported to VideoMeshSurfaceClient.");
+    REPLY_ERROR_RETURN(message, EALREADY, {});
+  }
+
+  auto producer = ProducerQueue::Create<VideoMeshSurfaceBufferMetadata>();
+  consumer_queue_ = producer->CreateConsumerQueue();
+
+  return std::move(producer->GetChannelHandle());
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/video_mesh_surface.h b/libs/vr/libvrflinger/video_mesh_surface.h
new file mode 100644
index 0000000..2c9f3e8
--- /dev/null
+++ b/libs/vr/libvrflinger/video_mesh_surface.h
@@ -0,0 +1,52 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_MESH_SURFACE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_MESH_SURFACE_H_
+
+#include <private/dvr/buffer_hub_queue_client.h>
+
+#include "surface_channel.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayService;
+
+// VideoMeshSurface takes three inputs: 1) buffers filled by Android system
+// components (e.g. MediaCodec or camera stack) other than applications' GL
+// context; 2) a 3D mesh chosen by application to define the shape of the
+// surface; 3) a transformation matrix from application to define the rotation,
+// position, and scaling of the video surface.
+class VideoMeshSurface : public SurfaceChannel {
+ public:
+  using Message = pdx::Message;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+
+  VideoMeshSurface(DisplayService* service, int channel_id);
+  ~VideoMeshSurface() override;
+
+  volatile const VideoMeshSurfaceMetadata* GetMetadataBufferPtr() {
+    if (EnsureMetadataBuffer()) {
+      void* addr = nullptr;
+      metadata_buffer_->GetBlobReadWritePointer(metadata_size(), &addr);
+      return static_cast<const volatile VideoMeshSurfaceMetadata*>(addr);
+    } else {
+      return nullptr;
+    }
+  }
+
+  pdx::Status<void> HandleMessage(Message& message) override;
+
+  std::shared_ptr<ConsumerQueue> GetConsumerQueue();
+
+ private:
+  LocalChannelHandle OnCreateProducerQueue(Message& message);
+
+  std::shared_ptr<ConsumerQueue> consumer_queue_;
+
+  VideoMeshSurface(const VideoMeshSurface&) = delete;
+  void operator=(const VideoMeshSurface&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_MESH_SURFACE_H_
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
new file mode 100644
index 0000000..21226db
--- /dev/null
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -0,0 +1,154 @@
+#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 <log/log.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.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 "screenshot_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);
+  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::ScreenshotService::Create();
+  CHECK_ERROR(!service, error, "Failed to create screenshot 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");
+  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..612b9b2
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.cpp
@@ -0,0 +1,208 @@
+#include "vsync_service.h"
+
+#include <log/log.h>
+#include <hardware/hwcomposer.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <utils/Trace.h>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/display_types.h>
+
+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(DisplayVSyncRPC::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 DisplayVSyncRPC::Wait::Opcode:
+      AddWaiter(message);
+      return {};
+
+    case DisplayVSyncRPC::GetLastTimestamp::Opcode:
+      DispatchRemoteMethod<DisplayVSyncRPC::GetLastTimestamp>(
+          *this, &VSyncService::OnGetLastTimestamp, message);
+      return {};
+
+    case DisplayVSyncRPC::GetSchedInfo::Opcode:
+      DispatchRemoteMethod<DisplayVSyncRPC::GetSchedInfo>(
+          *this, &VSyncService::OnGetSchedInfo, message);
+      return {};
+
+    case DisplayVSyncRPC::Acknowledge::Opcode:
+      DispatchRemoteMethod<DisplayVSyncRPC::Acknowledge>(
+          *this, &VSyncService::OnAcknowledge, message);
+      return {};
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+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_;
+}
+
+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};
+}
+
+int VSyncService::OnAcknowledge(pdx::Message& message) {
+  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+  std::lock_guard<std::mutex> autolock(mutex_);
+  client->Ack();
+  return 0;
+}
+
+void VSyncWaiter::Notify(int64_t timestamp) {
+  timestamp_ = timestamp;
+  DispatchRemoteMethod<DisplayVSyncRPC::Wait>(*this, &VSyncWaiter::OnWait,
+                                              message_);
+}
+
+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..1c86d42
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.h
@@ -0,0 +1,107 @@
+#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:
+  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();
+
+  int64_t OnGetLastTimestamp(pdx::Message& message);
+  VSyncSchedInfo OnGetSchedInfo(pdx::Message& message);
+  int 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..d59182e
--- /dev/null
+++ b/libs/vr/libvrsensor/Android.bp
@@ -0,0 +1,74 @@
+// 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",
+}
+
+testFiles = ["tests/sensor_app_tests.cpp"]
+
+cc_test {
+    name: "sensor_app_tests",
+    tags: ["optional"],
+
+    srcs: testFiles,
+
+    shared_libs: [
+        "libEGL",
+        "libGLESv1_CM",
+        "libGLESv2",
+        "libvulkan",
+        "libsync",
+    ] + sharedLibraries,
+
+    static_libs: [
+        "libgmock_main",
+        "libgmock",
+        "libdisplay",
+        "libeds",
+        "libvrsensor",
+        "libdvrgraphics",
+    ] + staticLibraries,
+
+}
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/libs/vr/libvrsensor/tests/sensor_app_tests.cpp b/libs/vr/libvrsensor/tests/sensor_app_tests.cpp
new file mode 100644
index 0000000..64c9864
--- /dev/null
+++ b/libs/vr/libvrsensor/tests/sensor_app_tests.cpp
@@ -0,0 +1,132 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <private/dvr/types.h>
+
+using android::dvr::vec4;
+
+namespace {
+
+vec4 ToVec4(float32x4_t rhs) { return vec4(rhs[0], rhs[1], rhs[2], rhs[3]); }
+
+}
+
+DvrGraphicsContext* CreateContext() {
+  DvrGraphicsContext* context = nullptr;
+  int display_width = 0, display_height = 0;
+  int surface_width = 0, surface_height = 0;
+  float inter_lens_meters = 0.0f;
+  float left_fov[4] = {0.0f};
+  float right_fov[4] = {0.0f};
+  int disable_warp = 0;
+  DvrSurfaceParameter surface_params[] = {
+      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+      DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+      DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+      DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+  dvrGraphicsContextCreate(surface_params, &context);
+  return context;
+}
+
+TEST(SensorAppTests, GetPose) {
+  DvrGraphicsContext* context = CreateContext();
+  ASSERT_NE(nullptr, context);
+  DvrPose* client = dvrPoseCreate();
+  ASSERT_NE(nullptr, client);
+
+  DvrPoseAsync last_pose;
+  uint32_t last_vsync_count = 0;
+  for (int i = 0; i < 10; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+    DvrPoseAsync pose;
+    int ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+    ASSERT_EQ(0, ret);
+
+    // Check for unit-length quaternion to verify valid pose.
+    vec4 quaternion = ToVec4(pose.orientation);
+    float length = quaternion.norm();
+    EXPECT_GT(0.001, fabs(1.0f - length));
+
+    // Check for different data each frame, but skip first few to allow
+    // startup anomalies.
+    if (i > 0) {
+      if (last_vsync_count == schedule.vsync_count)
+        ALOGE("vsync did not increment: %u", schedule.vsync_count);
+      if (pose.timestamp_ns == last_pose.timestamp_ns)
+        ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
+      // TODO(jbates) figure out why the bots are not passing this check.
+      // EXPECT_NE(last_vsync_count, schedule.vsync_count);
+      // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
+    }
+    last_pose = pose;
+    last_vsync_count = schedule.vsync_count;
+    dvrBeginRenderFrame(context);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    dvrPresent(context);
+  }
+
+  dvrPoseDestroy(client);
+  dvrGraphicsContextDestroy(context);
+}
+
+TEST(SensorAppTests, PoseRingBuffer) {
+  DvrGraphicsContext* context = CreateContext();
+  ASSERT_NE(nullptr, context);
+  DvrPose* client = dvrPoseCreate();
+  ASSERT_NE(nullptr, client);
+
+  DvrPoseRingBufferInfo info;
+  int ret = dvrPoseGetRingBuffer(client, &info);
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, info.buffer);
+  EXPECT_LE(2u, info.min_future_count);
+  EXPECT_LE(8u, info.total_count);
+
+  DvrPoseAsync last_pose;
+  uint32_t last_vsync_count = 0;
+  for (int i = 0; i < 10; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+    DvrPoseAsync pose;
+    ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+    ASSERT_EQ(0, ret);
+
+    // Check for unit-length quaternion to verify valid pose.
+    vec4 quaternion = ToVec4(pose.orientation);
+    float length = quaternion.norm();
+    EXPECT_GT(0.001, fabs(1.0f - length));
+
+    // Check for different data each frame, but skip first few to allow
+    // startup anomalies.
+    if (i > 0) {
+      if (last_vsync_count == schedule.vsync_count)
+        ALOGE("vsync did not increment: %u", schedule.vsync_count);
+      if (pose.timestamp_ns == last_pose.timestamp_ns)
+        ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
+      // TODO(jbates) figure out why the bots are not passing this check.
+      // EXPECT_NE(last_vsync_count, schedule.vsync_count);
+      // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
+    }
+    last_pose = pose;
+    last_vsync_count = schedule.vsync_count;
+    dvrBeginRenderFrame(context);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    dvrPresent(context);
+  }
+
+  dvrPoseDestroy(client);
+  dvrGraphicsContextDestroy(context);
+}
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 8b48032..d0996f0 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -605,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 EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDROID) (const EGLint *attrib_list);
+typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLGETNATIVECLIENTBUFFERANDROID) (const struct AHardwareBuffer *buffer);
 #endif
 #endif
 
@@ -630,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 EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
-typedef 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/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 48bf676..04f6d6d 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -33,6 +33,7 @@
 #include <ui/ANativeObjectBase.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBufferMapper.h>
+#include <ui/Rect.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index c1056a7..a895e63 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -58,6 +58,8 @@
         "-fvisibility=hidden",
     ],
     shared_libs: [
+        // ***** DO NOT ADD NEW DEPENDENCIES HERE *****
+        // In particular, DO NOT add libutils or anything "above" libcutils
         "libcutils",
         "liblog",
         "libdl",
@@ -77,9 +79,11 @@
         "-DLOG_TAG=\"libEGL\"",
     ],
     shared_libs: [
-        "libbinder",
-        "libutils",
+        // ***** DO NOT ADD NEW DEPENDENCIES HERE *****
+        // In particular, DO NOT add libutils nor anything "above" libui
         "libui",
+        "libnativewindow",
+        "libbacktrace",
     ],
 }
 
@@ -105,11 +109,20 @@
         "EGL/egl.cpp",
         "EGL/eglApi.cpp",
         "EGL/Loader.cpp",
+        "EGL/BlobCache.cpp",
     ],
     static_libs: ["libEGL_getProcAddress"],
     ldflags: ["-Wl,--exclude-libs=ALL"],
+    export_include_dirs: ["EGL/include"],
+}
 
-    required: ["egl.cfg"],
+cc_test {
+    name: "libEGL_test",
+    defaults: ["egl_libs_defaults"],
+    srcs: [
+        "EGL/BlobCache.cpp",
+        "EGL/BlobCache_test.cpp",
+    ],
 }
 
 cc_defaults {
@@ -133,7 +146,6 @@
     name: "libGLESv1_CM",
     defaults: ["gles_libs_defaults"],
     srcs: ["GLES_CM/gl.cpp"],
-
     cflags: ["-DLOG_TAG=\"libGLESv1\""],
 }
 
@@ -144,9 +156,6 @@
     name: "libGLESv2",
     defaults: ["gles_libs_defaults"],
     srcs: ["GLES2/gl2.cpp"],
-
-    shared_libs: ["libutils"],
-
     cflags: ["-DLOG_TAG=\"libGLESv2\""],
 }
 
@@ -157,8 +166,5 @@
     name: "libGLESv3",
     defaults: ["gles_libs_defaults"],
     srcs: ["GLES2/gl2.cpp"],
-
-    shared_libs: ["libutils"],
-
     cflags: ["-DLOG_TAG=\"libGLESv3\""],
 }
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 21e76f5..2f42ab6 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -1,12 +1 @@
 LOCAL_PATH:= $(call my-dir)
-
-# 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)
-endif
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 69e3c13..7d20ba1 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,25 +17,25 @@
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <array>
-#include <ctype.h>
+#include "Loader.h"
+
+#include <string>
+
 #include <dirent.h>
 #include <dlfcn.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 
 #include <android/dlext.h>
 #include <cutils/properties.h>
 #include <log/log.h>
-#include <utils/Trace.h>
 
-#include <EGL/egl.h>
+#include <ui/GraphicsEnv.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 +65,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 +105,16 @@
     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);
+}
+
 // ----------------------------------------------------------------------------
 
 Loader::driver_t::driver_t(void* gles)
@@ -144,7 +134,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 +147,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 +245,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 +313,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 +337,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 +378,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 +401,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 +416,38 @@
     };
 
 
-    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();
+
+    // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to
+    // the original routine when the namespace does not exist or the load from
+    // the namespace fails.
+    // See /system/core/rootdir/etc/ld.config.txt for the configuration of the
+    // sphal namespace.
+    android_namespace_t* sphal_namespace = android_get_exported_namespace("sphal");
+    if (sphal_namespace != NULL) {
+        const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE,
+            .library_namespace = sphal_namespace,
+        };
+        void* dso = do_android_dlopen_ext(driver_absolute_path, RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+        if (dso) {
+            ALOGD("loaded %s from sphal namespace", driver_absolute_path);
+            return dso;
+        }
+        else {
+            ALOGW("failed to load %s from sphal namespace: %s", driver_absolute_path, dlerror());
+        }
+    }
 
     void* dso = do_dlopen(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 +456,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 +471,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 +487,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 d0435e7..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,
@@ -50,20 +38,19 @@
     struct driver_t {
         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 ee83ada..f53cf3f 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,30 +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/atomic.h>
 #include <cutils/properties.h>
+
 #include <log/log.h>
-#include <utils/CallStack.h>
-#include <utils/String8.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;
 
@@ -70,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;
@@ -132,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) {
@@ -155,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() {
@@ -212,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;
     }
@@ -229,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 f8e25b4..9de15d0 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -21,47 +21,40 @@
 #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/atomic.h>
+#include <android/hardware_buffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
 #include <cutils/compiler.h>
-#include <cutils/memory.h>
 #include <cutils/properties.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;
 
-#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0
-
 // ----------------------------------------------------------------------------
 
 namespace android {
 
+using nsecs_t = int64_t;
+
 struct extention_map_t {
     const char* name;
     __eglMustCastToProperFunctionPointerType address;
@@ -82,17 +75,20 @@
  *
  * 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 "
@@ -118,12 +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
@@ -181,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",
@@ -215,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 },
 };
 
 /*
@@ -228,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;
 
@@ -292,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);
 
@@ -308,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();
 
@@ -329,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;
@@ -354,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;
@@ -386,6 +394,8 @@
                         case EGL_CONFIG_CAVEAT:
                             attribCaveat = &attrib_list[attribCount];
                             break;
+                        default:
+                            break;
                     }
                     attribCount++;
                 }
@@ -463,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);
@@ -477,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;
             }
         }
 
@@ -597,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);
@@ -617,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(
@@ -636,7 +674,6 @@
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
         setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return;
     }
 }
 
@@ -695,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);
@@ -711,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
@@ -729,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
@@ -752,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
@@ -763,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;
     }
@@ -794,7 +831,7 @@
     } else {
         // 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;
 }
@@ -809,7 +846,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(
@@ -870,7 +907,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();
 }
@@ -881,7 +918,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);
 }
@@ -960,8 +997,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,
@@ -983,7 +1023,7 @@
 
             if (found) {
                 addr = gExtensionForwarders[slot];
-                sGLExtentionMap.add(name, addr);
+                extentionMap[name] = addr;
                 sGLExtentionSlot++;
             }
         }
@@ -992,45 +1032,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());
@@ -1040,19 +1092,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,
@@ -1066,7 +1117,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);
 
@@ -1091,7 +1142,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];
@@ -1105,8 +1156,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,
@@ -1131,7 +1181,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);
@@ -1141,6 +1191,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;
 
@@ -1153,6 +1213,8 @@
             return dp->getExtensionString();
         case EGL_CLIENT_APIS:
             return dp->getClientApiString();
+        default:
+            break;
     }
     return setError(EGL_BAD_PARAMETER, (const char *)0);
 }
@@ -1173,6 +1235,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);
 }
@@ -1191,29 +1255,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(
@@ -1226,14 +1292,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(
@@ -1246,14 +1312,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)
@@ -1283,7 +1349,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) {
@@ -1299,7 +1365,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
@@ -1316,7 +1382,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;
@@ -1374,14 +1440,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)
@@ -1393,13 +1459,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,
@@ -1497,7 +1563,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(
@@ -1781,149 +1847,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));
 }
 
 // ----------------------------------------------------------------------------
@@ -1934,7 +1866,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;
@@ -1944,7 +1876,7 @@
         return cnx->egl.eglGetSystemTimeFrequencyNV();
     }
 
-    return setErrorQuiet(EGL_BAD_DISPLAY, 0);
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
 }
 
 EGLuint64NV eglGetSystemTimeNV()
@@ -1952,7 +1884,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;
@@ -1962,7 +1894,7 @@
         return cnx->egl.eglGetSystemTimeNV();
     }
 
-    return setErrorQuiet(EGL_BAD_DISPLAY, 0);
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
 }
 
 // ----------------------------------------------------------------------------
@@ -1994,103 +1926,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 1fe322d..dc1a4af 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -14,18 +14,22 @@
  ** 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>
+
+#include <thread>
+
+#include <log/log.h>
 
 // Cache size limits.
 static const size_t maxKeySize = 12 * 1024;
@@ -45,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.
 //
@@ -62,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() {
@@ -76,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) {
@@ -87,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 =
@@ -114,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");
@@ -129,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");
@@ -168,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) {
@@ -207,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.
@@ -242,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;
@@ -276,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;
@@ -324,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 d7df40c..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.
@@ -220,10 +227,10 @@
             *minor = VERSION_MINOR;
     }
 
-    {
-        Mutex::Autolock _rf(refLock);
+    { // scope for refLock
+        std::unique_lock<std::mutex> _l(refLock);
         eglIsInitialized = true;
-        refCond.broadcast();
+        refCond.notify_all();
     }
 
     return EGL_TRUE;
@@ -231,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):
@@ -252,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,15 +275,14 @@
 
         // 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();
         }
 
@@ -284,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;
@@ -312,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();
 
     }
@@ -338,7 +344,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);
         if (c) {
             result = c->cnx->egl.eglMakeCurrent(
                     disp.dpy, impl_draw, impl_read, impl_ctx);
@@ -370,7 +376,7 @@
     if (!nameLen) {
         nameLen = strlen(name);
     }
-    return findExtension(mExtensionString.string(), name, nameLen);
+    return findExtension(mExtensionString.c_str(), name, nameLen);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index e17558c..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"
@@ -80,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;
 
@@ -116,13 +117,14 @@
 
             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;
+    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;
 };
 
 // ----------------------------------------------------------------------------
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 6a76737..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,22 +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) {
+        win->incStrong(this);
+    }
+}
 
 egl_surface_t::~egl_surface_t() {
-    ANativeWindow* const window = win.get();
-    if (window != NULL) {
+    if (win != NULL) {
         disconnect();
+        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;
     }
@@ -116,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 3150ba6..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"
 
 // ----------------------------------------------------------------------------
@@ -62,8 +62,8 @@
     template <typename N, typename T>
     class LocalRef {
         egl_object_t* ref;
-        LocalRef();
-        explicit 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 6de5f27..8508c5f 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -14,16 +14,13 @@
  ** limitations under the License.
  */
 
-#include <pthread.h>
+#include "egl_tls.h"
+
 #include <stdlib.h>
 
 #include <cutils/properties.h>
 #include <log/log.h>
-#include <utils/CallStack.h>
-
-#include <EGL/egl.h>
-
-#include "egl_tls.h"
+#include "CallStack.h"
 
 namespace android {
 
@@ -31,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) {
@@ -76,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;
@@ -85,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 450c402..c05e840 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -18,7 +18,7 @@
 #include <errno.h>
 #include <stdlib.h>
 
-#include <android/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 c206041..f7fde96 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -19,7 +19,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <cutils/properties.h>
 
 #include "../hooks.h"
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index e698fcd..bacd4b4 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -19,7 +19,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 
-#include <android/log.h>
+#include <log/log.h>
 #include <cutils/properties.h>
 
 #include <GLES/gl.h>
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/libEGL.map.txt b/opengl/libs/libEGL.map.txt
index c8b83f5..89269a0 100644
--- a/opengl/libs/libEGL.map.txt
+++ b/opengl/libs/libEGL.map.txt
@@ -28,6 +28,7 @@
     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
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..94de5af 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -16,14 +16,43 @@
 
 #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;
+
+static bool hasWideColorDisplay =
+        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+
 class EGLTest : public ::testing::Test {
 protected:
     EGLDisplay mEglDisplay;
@@ -48,7 +77,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 +94,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 +130,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 +168,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 +193,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/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/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/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
index 79db871..e441bda 100644
--- a/services/batteryservice/Android.bp
+++ b/services/batteryservice/Android.bp
@@ -1,4 +1,4 @@
-cc_library_static {
+cc_library {
     name: "libbatteryservice",
 
     srcs: [
@@ -8,7 +8,7 @@
         "IBatteryPropertiesRegistrar.cpp",
     ],
 
-    static_libs: [
+    shared_libs: [
         "libutils",
         "libbinder",
     ],
@@ -19,4 +19,4 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-}
+}
\ No newline at end of file
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 7555f4b..6e5bcfe 100644
--- a/services/batteryservice/IBatteryPropertiesListener.cpp
+++ b/services/batteryservice/IBatteryPropertiesListener.cpp
@@ -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/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 89475e9..22a4616 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -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
 }
@@ -1224,15 +1222,8 @@
 
                 if (maskedAction == AMOTION_EVENT_ACTION_DOWN
                         && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
-                    int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
-                    if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
-                        outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-                    } else if (isWindowObscuredLocked(windowHandle)) {
-                        outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
-                    }
-
                     mTempTouchState.addOrUpdateWindow(
-                            windowHandle, outsideTargetFlags, BitSet32(0));
+                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));
                 }
             }
         }
@@ -2425,7 +2416,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
@@ -2437,7 +2428,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/InputReader.cpp b/services/inputflinger/InputReader.cpp
index c1e6365..2705e13 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -2322,10 +2322,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);
@@ -2478,6 +2474,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;
@@ -2499,6 +2500,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);
@@ -2550,6 +2576,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;
@@ -2636,7 +2665,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);
@@ -2687,7 +2716,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;
@@ -2732,7 +2761,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,
@@ -5422,8 +5451,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) {
@@ -5582,10 +5609,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();
@@ -5832,10 +5855,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.
@@ -5932,10 +5951,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) {
@@ -6058,8 +6073,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;
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 8e2fe95..3171526 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -144,6 +144,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 +234,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),
@@ -1200,6 +1206,7 @@
     struct Parameters {
         enum Mode {
             MODE_POINTER,
+            MODE_POINTER_RELATIVE,
             MODE_NAVIGATION,
         };
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index f12320d..2e0bcd1 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -182,6 +182,10 @@
         transform = t;
     }
 
+    void setPointerCapture(bool enabled) {
+        mConfig.pointerCapture = enabled;
+    }
+
 private:
     virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
         *outConfig = mConfig;
@@ -744,6 +748,10 @@
         mGlobalMetaState = state;
     }
 
+    uint32_t getGeneration() {
+        return mGeneration;
+    }
+
 private:
     virtual void updateGlobalMetaState() {
         mUpdateGlobalMetaStateWasCalled = true;
@@ -1425,17 +1433,20 @@
         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);
+        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
     static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type,
@@ -2078,6 +2089,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 +2116,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 +2189,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 +2226,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 +2246,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 +2347,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 +2390,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 +2454,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 +2495,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 +2536,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 +2577,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 +2628,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));
@@ -3312,11 +3584,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 +3607,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 +3642,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 +3671,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 +3700,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 +3729,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 +5080,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 +5103,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 +5138,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 +5167,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 +5196,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 +5225,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/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/RotationVectorSensor.h b/services/sensorservice/RotationVectorSensor.h
index 265b4c4..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"
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..91923b3
--- /dev/null
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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>
+
+#include <sys/stat.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: {
+                struct stat s1, s2;
+                int fd1, fd2;
+                fd1 = mMem.handle->data[0];
+                fd2 = mem->handle->data[0];
+                if (fstat(fd1, &s1) < 0 || fstat(fd2, &s2) < 0 || s1.st_ino == s2.st_ino) {
+                    ret = true;
+                }
+                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/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 0867dc2..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>
 
 // ---------------------------------------------------------------------------
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 7b47709..cd1d1b0 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 = 0,
+            .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();
@@ -1002,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) {
@@ -1020,7 +1193,7 @@
     }
 
     Mutex::Autolock _l(mLock);
-    if ((mCurrentOperatingMode == RESTRICTED || mCurrentOperatingMode == DATA_INJECTION)
+    if (mCurrentOperatingMode != NORMAL
            && !isWhiteListedPackage(connection->getPackageName())) {
         return INVALID_OPERATION;
     }
@@ -1076,6 +1249,12 @@
             handle, connection.get());
     }
 
+    // Check maximum delay for the sensor.
+    nsecs_t maxDelayNs = sensor->getSensor().getMaxDelay() * 1000;
+    if (maxDelayNs > 0 && (samplingPeriodNs > maxDelayNs)) {
+        samplingPeriodNs = maxDelayNs;
+    }
+
     nsecs_t minDelayNs = sensor->getSensor().getMinDelayNs();
     if (samplingPeriodNs < minDelayNs) {
         samplingPeriodNs = minDelayNs;
@@ -1114,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;
     }
 
@@ -1148,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;
@@ -1338,5 +1501,14 @@
     return (packageName.contains(mWhiteListedPackage.string()));
 }
 
+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 e969d8a..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>
@@ -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..06ff95c
--- /dev/null
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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;
+
+SensorManager::SensorManager()
+        : mInternalManager{::android::SensorManager::getInstanceForPackage(
+            String16(ISensorManager::descriptor))} {
+}
+
+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 = mInternalManager.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 = mInternalManager.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(mInternalManager, 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(mInternalManager, 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] {
+
+            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);
+            looper = Looper::prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS /* opts */);
+            lock.unlock();
+
+            looperSet.notify_one();
+            int pollResult = looper->pollAll(-1 /* timeout */);
+            if (pollResult != ALOOPER_POLL_WAKE) {
+                LOG(ERROR) << "Looper::pollAll returns unexpected " << pollResult;
+            }
+            LOG(INFO) << "Looper thread is terminated.";
+        }}.detach();
+        looperSet.wait(lock, [this]{ return this->mLooper != nullptr; });
+    }
+    return mLooper;
+}
+
+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 = mInternalManager.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..a2372df
--- /dev/null
+++ b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
@@ -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.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_SENSORMANAGER_H
+#define ANDROID_FRAMEWORKS_SENSORSERVICE_V1_0_SENSORMANAGER_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();
+    ~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:
+    sp<::android::Looper> getLooper();
+
+    ::android::SensorManager& mInternalManager;
+    std::mutex mLooperMutex;
+    sp<::android::Looper> mLooper;
+};
+
+}  // 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/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/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 be537dc..7bb20ba 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
@@ -50,87 +54,40 @@
     LOCAL_SRC_FILES += \
         SurfaceFlinger.cpp \
         DisplayHardware/HWComposer.cpp
+    ifeq ($(TARGET_USES_HWC2ON1ADAPTER), true)
+        LOCAL_CFLAGS += -DBYPASS_IHWC
+    endif
 else
+    LOCAL_CFLAGS += -DBYPASS_IHWC
     LOCAL_SRC_FILES += \
         SurfaceFlinger_hwc1.cpp \
         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_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 \
+    libhwc2on1adapter \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
     libutils \
     libEGL \
     libGLESv1_CM \
@@ -139,9 +96,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
 
@@ -167,14 +133,22 @@
     main_surfaceflinger.cpp
 
 LOCAL_SHARED_LIBRARIES := \
+    android.hardware.configstore@1.0 \
+    android.hardware.configstore-utils \
+    android.hardware.graphics.allocator@2.0 \
     libsurfaceflinger \
     libcutils \
     liblog \
     libbinder \
+    libhidlbase \
+    libhidltransport \
     libutils \
+    libui \
+    libgui \
     libdl
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_STATIC_LIBRARIES := libtrace_proto
 
 LOCAL_MODULE := surfaceflinger
 
@@ -209,3 +183,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..e9a2513 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,17 @@
 {
     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) {
+    mParentLayer = parentLayer;
+}
+
 status_t Client::initCheck() const {
     return NO_ERROR;
 }
@@ -90,12 +103,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)
+             && (mParentLayer.promote() == 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 +124,30 @@
 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 && mParentLayer != nullptr) {
+        parent = mParentLayer.promote();
+        // If we had a parent, but it died, we've lost all
+        // our capabilities.
+        if (parent == nullptr) {
+            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 +158,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 +210,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 9c7d050..b5f98b8 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -38,8 +38,9 @@
 class Client : public BnSurfaceComposerClient
 {
 public:
-        explicit 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,8 +67,6 @@
     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);
@@ -75,6 +77,8 @@
     // 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 f2e6491..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 {
 
 // ---------------------------------------------------------------------------
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 52c2b84..bd9b8aa 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -33,6 +33,7 @@
 #include <ui/Fence.h>
 
 #include "DispSync.h"
+#include "SurfaceFlinger.h"
 #include "EventLog/EventLog.h"
 
 using std::max;
@@ -54,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),
@@ -219,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);
@@ -379,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};
@@ -399,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());
         }
     }
@@ -433,7 +433,7 @@
             nsecs_t t = f->getSignalTime();
             if (t < INT64_MAX) {
                 mPresentFences[i].clear();
-                mPresentTimes[i] = t + kPresentTimeOffset;
+                mPresentTimes[i] = t + mPresentTimeOffset;
             }
         }
     }
@@ -478,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
@@ -643,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 2763e59..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;
@@ -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..262ab62
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -0,0 +1,1052 @@
+/*
+ * 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");
+    }
+}
+
+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::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) {
+                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;
+        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;
+}
+
+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;
+}
+
+} // namespace Hwc2
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
new file mode 100644
index 0000000..37b7766
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -0,0 +1,271 @@
+/*
+ * 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);
+
+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);
+
+    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;
+    };
+
+    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);
+
+    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 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 151eaaa..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)  // NOLINT(implicit)
-        : 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 cdfe34a..5b869e1 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -27,22 +27,19 @@
 #include <utils/String8.h>
 #include <log/log.h>
 
-#include <ui/Rect.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 {
@@ -87,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*/) {
@@ -100,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);
     }
@@ -123,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
@@ -133,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);
@@ -169,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;
 }
@@ -204,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);
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..8270c39 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,11 +79,16 @@
 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
 
+#ifdef BYPASS_IHWC
 Device::Device(hwc2_device_t* device)
   : mHwcDevice(device),
     mCreateVirtualDisplay(nullptr),
@@ -128,6 +133,10 @@
     mSetLayerTransform(nullptr),
     mSetLayerVisibleRegion(nullptr),
     mSetLayerZOrder(nullptr),
+#else
+Device::Device(bool useVrComposer)
+  : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)),
+#endif // BYPASS_IHWC
     mCapabilities(),
     mDisplays(),
     mHotplug(),
@@ -144,9 +153,11 @@
 
 Device::~Device()
 {
+#ifdef BYPASS_IHWC
     if (mHwcDevice == nullptr) {
         return;
     }
+#endif
 
     for (auto element : mDisplays) {
         auto display = element.second.lock();
@@ -175,13 +186,16 @@
         }
     }
 
+#ifdef BYPASS_IHWC
     hwc2_close(mHwcDevice);
+#endif
 }
 
 // Required by HWC2 device
 
 std::string Device::dump() const
 {
+#ifdef BYPASS_IHWC
     uint32_t numBytes = 0;
     mDump(mHwcDevice, &numBytes, nullptr);
 
@@ -189,11 +203,18 @@
     mDump(mHwcDevice, &numBytes, buffer.data());
 
     return std::string(buffer.data(), buffer.size());
+#else
+    return mComposer->dumpDebugInfo();
+#endif
 }
 
 uint32_t Device::getMaxVirtualDisplayCount() const
 {
+#ifdef BYPASS_IHWC
     return mGetMaxVirtualDisplayCount(mHwcDevice);
+#else
+    return mComposer->getMaxVirtualDisplayCount();
+#endif
 }
 
 Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
@@ -202,9 +223,15 @@
     ALOGI("Creating virtual display");
 
     hwc2_display_t displayId = 0;
+#ifdef BYPASS_IHWC
     int32_t intFormat = static_cast<int32_t>(*format);
     int32_t intError = mCreateVirtualDisplay(mHwcDevice, width, height,
             &intFormat, &displayId);
+#else
+    auto intFormat = static_cast<Hwc2::PixelFormat>(*format);
+    auto intError = mComposer->createVirtualDisplay(width, height,
+            &intFormat, &displayId);
+#endif
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -217,7 +244,7 @@
         ALOGE("Failed to get display by id");
         return Error::BadDisplay;
     }
-    (*outDisplay)->setVirtual();
+    (*outDisplay)->setConnected(true);
     return Error::None;
 }
 
@@ -258,6 +285,9 @@
 {
     if (connected == Connection::Connected) {
         if (!display->isConnected()) {
+#ifndef BYPASS_IHWC
+            mComposer->setClientTargetSlotCount(display->getId());
+#endif
             display->loadConfigs();
             display->setConnected(true);
         }
@@ -315,6 +345,7 @@
 {
     static_assert(sizeof(Capability) == sizeof(int32_t),
             "Capability size has changed");
+#ifdef BYPASS_IHWC
     uint32_t numCapabilities = 0;
     mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
     std::vector<Capability> capabilities(numCapabilities);
@@ -323,6 +354,12 @@
     for (auto capability : capabilities) {
         mCapabilities.emplace(capability);
     }
+#else
+    auto capabilities = mComposer->getCapabilities();
+    for (auto capability : capabilities) {
+        mCapabilities.emplace(static_cast<Capability>(capability));
+    }
+#endif
 }
 
 bool Device::hasCapability(HWC2::Capability capability) const
@@ -333,6 +370,7 @@
 
 void Device::loadFunctionPointers()
 {
+#ifdef BYPASS_IHWC
     // For all of these early returns, we log an error message inside
     // loadFunctionPointer specifying which function failed to load
 
@@ -426,13 +464,48 @@
             mSetLayerVisibleRegion)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder,
             mSetLayerZOrder)) return;
+#endif // BYPASS_IHWC
 }
 
+namespace {
+class ComposerCallback : public Hwc2::IComposerCallback {
+public:
+    ComposerCallback(Device* device) : mDevice(device) {}
+
+    Return<void> onHotplug(Hwc2::Display display,
+            Connection connected) override
+    {
+        hotplug_hook(mDevice, display, static_cast<int32_t>(connected));
+        return Void();
+    }
+
+    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()
 {
+#ifdef BYPASS_IHWC
     registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);
     registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);
     registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
+#else
+    sp<ComposerCallback> callback = new ComposerCallback(this);
+    mComposer->registerCallback(callback);
+#endif
 }
 
 
@@ -441,7 +514,11 @@
 void Device::destroyVirtualDisplay(hwc2_display_t display)
 {
     ALOGI("Destroying virtual display");
+#ifdef BYPASS_IHWC
     int32_t intError = mDestroyVirtualDisplay(mHwcDevice, display);
+#else
+    auto intError = mComposer->destroyVirtualDisplay(display);
+#endif
     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 +531,28 @@
   : mDevice(device),
     mId(id),
     mIsConnected(false),
-    mIsVirtual(false)
+    mType(DisplayType::Invalid)
 {
     ALOGV("Created display %" PRIu64, id);
+
+#ifdef BYPASS_IHWC
+    int32_t intError = mDevice.mGetDisplayType(mDevice.mHwcDevice, mId,
+            reinterpret_cast<int32_t *>(&mType));
+#else
+    auto intError = mDevice.mComposer->getDisplayType(mId,
+            reinterpret_cast<Hwc2::IComposerClient::DisplayType *>(&mType));
+#endif
+    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 +588,22 @@
 
 Error Display::acceptChanges()
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mAcceptDisplayChanges(mDevice.mHwcDevice, mId);
+#else
+    auto intError = mDevice.mComposer->acceptDisplayChanges(mId);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
 {
     hwc2_layer_t layerId = 0;
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);
+#else
+    auto intError = mDevice.mComposer->createLayer(mId, &layerId);
+#endif
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -522,11 +620,17 @@
 {
     ALOGV("[%" PRIu64 "] getActiveConfig", mId);
     hwc2_config_t configId = 0;
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mGetActiveConfig(mDevice.mHwcDevice, mId,
             &configId);
+#else
+    auto intError = mDevice.mComposer->getActiveConfig(mId, &configId);
+#endif
     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,6 +650,7 @@
 Error Display::getChangedCompositionTypes(
         std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes)
 {
+#ifdef BYPASS_IHWC
     uint32_t numElements = 0;
     int32_t intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice,
             mId, &numElements, nullptr, nullptr);
@@ -558,6 +663,14 @@
     std::vector<int32_t> types(numElements);
     intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice, mId,
             &numElements, layerIds.data(), types.data());
+#else
+    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);
+#endif
     error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -583,6 +696,7 @@
 
 Error Display::getColorModes(std::vector<android_color_mode_t>* outModes) const
 {
+#ifdef BYPASS_IHWC
     uint32_t numModes = 0;
     int32_t intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId,
             &numModes, nullptr);
@@ -595,6 +709,12 @@
     intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId, &numModes,
             modes.data());
     error = static_cast<Error>(intError);
+#else
+    std::vector<Hwc2::ColorMode> modes;
+    auto intError = mDevice.mComposer->getColorModes(mId, &modes);
+    uint32_t numModes = modes.size();
+    auto error = static_cast<Error>(intError);
+#endif
     if (error != Error::None) {
         return error;
     }
@@ -617,6 +737,7 @@
 
 Error Display::getName(std::string* outName) const
 {
+#ifdef BYPASS_IHWC
     uint32_t size;
     int32_t intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size,
             nullptr);
@@ -635,12 +756,17 @@
 
     *outName = std::string(rawName.cbegin(), rawName.cend());
     return Error::None;
+#else
+    auto intError = mDevice.mComposer->getDisplayName(mId, outName);
+    return static_cast<Error>(intError);
+#endif
 }
 
 Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests,
         std::unordered_map<std::shared_ptr<Layer>, LayerRequest>*
                 outLayerRequests)
 {
+#ifdef BYPASS_IHWC
     int32_t intDisplayRequests = 0;
     uint32_t numElements = 0;
     int32_t intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId,
@@ -656,6 +782,15 @@
             &intDisplayRequests, &numElements, layerIds.data(),
             layerRequests.data());
     error = static_cast<Error>(intError);
+#else
+    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);
+#endif
     if (error != Error::None) {
         return error;
     }
@@ -680,23 +815,20 @@
 
 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
 {
+#ifdef BYPASS_IHWC
     int32_t intSupport = 0;
     int32_t intError = mDevice.mGetDozeSupport(mDevice.mHwcDevice, mId,
             &intSupport);
+#else
+    bool intSupport = false;
+    auto intError = mDevice.mComposer->getDozeSupport(mId, &intSupport);
+#endif
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -712,6 +844,7 @@
     float maxLuminance = -1.0f;
     float maxAverageLuminance = -1.0f;
     float minLuminance = -1.0f;
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId,
             &numTypes, nullptr, &maxLuminance, &maxAverageLuminance,
             &minLuminance);
@@ -724,6 +857,18 @@
     intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId, &numTypes,
             types.data(), &maxLuminance, &maxAverageLuminance, &minLuminance);
     error = static_cast<HWC2::Error>(intError);
+#else
+    std::vector<Hwc2::Hdr> intTypes;
+    auto intError = mDevice.mComposer->getHdrCapabilities(mId, &intTypes,
+            &maxLuminance, &maxAverageLuminance, &minLuminance);
+    auto 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();
+#endif
     if (error != Error::None) {
         return error;
     }
@@ -736,6 +881,7 @@
 Error Display::getReleaseFences(
         std::unordered_map<std::shared_ptr<Layer>, sp<Fence>>* outFences) const
 {
+#ifdef BYPASS_IHWC
     uint32_t numElements = 0;
     int32_t intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId,
             &numElements, nullptr, nullptr);
@@ -749,6 +895,14 @@
     intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId, &numElements,
             layerIds.data(), fenceFds.data());
     error = static_cast<Error>(intError);
+#else
+    std::vector<Hwc2::Layer> layerIds;
+    std::vector<int> fenceFds;
+    auto intError = mDevice.mComposer->getReleaseFences(mId,
+            &layerIds, &fenceFds);
+    auto error = static_cast<Error>(intError);
+    uint32_t numElements = layerIds.size();
+#endif
     if (error != Error::None) {
         return error;
     }
@@ -771,17 +925,21 @@
     return Error::None;
 }
 
-Error Display::present(sp<Fence>* outRetireFence)
+Error Display::present(sp<Fence>* outPresentFence)
 {
-    int32_t retireFenceFd = 0;
+    int32_t presentFenceFd = -1;
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
-            &retireFenceFd);
+            &presentFenceFd);
+#else
+    auto intError = mDevice.mComposer->presentDisplay(mId, &presentFenceFd);
+#endif
     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 +951,58 @@
                 config->getDisplayId(), mId);
         return Error::BadConfig;
     }
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetActiveConfig(mDevice.mHwcDevice, mId,
             config->getId());
+#else
+    auto intError = mDevice.mComposer->setActiveConfig(mId, config->getId());
+#endif
     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,
+#ifdef BYPASS_IHWC
+    (void) slot;
+    buffer_handle_t handle = nullptr;
+    if (target.get() && target->getNativeBuffer()) {
+        handle = target->getNativeBuffer()->handle;
+    }
+
+    int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, handle,
             fenceFd, static_cast<int32_t>(dataspace), {0, nullptr});
+#else
+    auto intError = mDevice.mComposer->setClientTarget(mId, slot, target,
+            fenceFd, static_cast<Hwc2::Dataspace>(dataspace),
+            std::vector<Hwc2::IComposerClient::Rect>());
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Display::setColorMode(android_color_mode_t mode)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetColorMode(mDevice.mHwcDevice, mId, mode);
+#else
+    auto intError = mDevice.mComposer->setColorMode(mId,
+            static_cast<Hwc2::ColorMode>(mode));
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Display::setColorTransform(const android::mat4& matrix,
         android_color_transform_t hint)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetColorTransform(mDevice.mHwcDevice, mId,
             matrix.asArray(), static_cast<int32_t>(hint));
+#else
+    auto intError = mDevice.mComposer->setColorTransform(mId,
+            matrix.asArray(), static_cast<Hwc2::ColorTransform>(hint));
+#endif
     return static_cast<Error>(intError);
 }
 
@@ -827,24 +1011,38 @@
 {
     int32_t fenceFd = releaseFence->dup();
     auto handle = buffer->getNativeBuffer()->handle;
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetOutputBuffer(mDevice.mHwcDevice, mId, handle,
             fenceFd);
+#else
+    auto intError = mDevice.mComposer->setOutputBuffer(mId, handle, fenceFd);
+#endif
     close(fenceFd);
     return static_cast<Error>(intError);
 }
 
 Error Display::setPowerMode(PowerMode mode)
 {
+#ifdef BYPASS_IHWC
     auto intMode = static_cast<int32_t>(mode);
     int32_t intError = mDevice.mSetPowerMode(mDevice.mHwcDevice, mId, intMode);
+#else
+    auto intMode = static_cast<Hwc2::IComposerClient::PowerMode>(mode);
+    auto intError = mDevice.mComposer->setPowerMode(mId, intMode);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Display::setVsyncEnabled(Vsync enabled)
 {
+#ifdef BYPASS_IHWC
     auto intEnabled = static_cast<int32_t>(enabled);
     int32_t intError = mDevice.mSetVsyncEnabled(mDevice.mHwcDevice, mId,
             intEnabled);
+#else
+    auto intEnabled = static_cast<Hwc2::IComposerClient::Vsync>(enabled);
+    auto intError = mDevice.mComposer->setVsyncEnabled(mId, intEnabled);
+#endif
     return static_cast<Error>(intError);
 }
 
@@ -852,8 +1050,13 @@
 {
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mValidateDisplay(mDevice.mHwcDevice, mId,
             &numTypes, &numRequests);
+#else
+    auto intError = mDevice.mComposer->validateDisplay(mId,
+            &numTypes, &numRequests);
+#endif
     auto error = static_cast<Error>(intError);
     if (error != Error::None && error != Error::HasChanges) {
         return error;
@@ -869,8 +1072,14 @@
 int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
 {
     int32_t value = 0;
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mGetDisplayAttribute(mDevice.mHwcDevice, mId,
             configId, static_cast<int32_t>(attribute), &value);
+#else
+    auto intError = mDevice.mComposer->getDisplayAttribute(mId, configId,
+            static_cast<Hwc2::IComposerClient::Attribute>(attribute),
+            &value);
+#endif
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId,
@@ -899,6 +1108,7 @@
 {
     ALOGV("[%" PRIu64 "] loadConfigs", mId);
 
+#ifdef BYPASS_IHWC
     uint32_t numConfigs = 0;
     int32_t intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId,
             &numConfigs, nullptr);
@@ -913,6 +1123,11 @@
     intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId, &numConfigs,
             configIds.data());
     error = static_cast<Error>(intError);
+#else
+    std::vector<Hwc2::Config> configIds;
+    auto intError = mDevice.mComposer->getDisplayConfigs(mId, &configIds);
+    auto error = static_cast<Error>(intError);
+#endif
     if (error != Error::None) {
         ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId,
                 to_string(error).c_str(), intError);
@@ -928,7 +1143,11 @@
 
 void Display::destroyLayer(hwc2_layer_t layerId)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mDestroyLayer(mDevice.mHwcDevice, mId, layerId);
+#else
+    auto intError =mDevice.mComposer->destroyLayer(mId, layerId);
+#endif
     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 +1189,33 @@
 
 Error Layer::setCursorPosition(int32_t x, int32_t y)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetCursorPosition(mDevice.mHwcDevice,
             mDisplayId, mId, x, y);
+#else
+    auto intError = mDevice.mComposer->setCursorPosition(mDisplayId,
+            mId, x, y);
+#endif
     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();
+#ifdef BYPASS_IHWC
+    (void) slot;
+    buffer_handle_t handle = nullptr;
+    if (buffer.get() && buffer->getNativeBuffer()) {
+        handle = buffer->getNativeBuffer()->handle;
+    }
+
     int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId,
-            mId, buffer, fenceFd);
+            mId, handle, fenceFd);
+#else
+    auto intError = mDevice.mComposer->setLayerBuffer(mDisplayId,
+            mId, slot, buffer, fenceFd);
+#endif
     return static_cast<Error>(intError);
 }
 
@@ -988,26 +1223,44 @@
 {
     // We encode default full-screen damage as INVALID_RECT upstream, but as 0
     // rects for HWC
+#ifdef BYPASS_IHWC
     int32_t intError = 0;
+#else
+    Hwc2::Error intError = Hwc2::Error::NONE;
+#endif
     if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) {
+#ifdef BYPASS_IHWC
         intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
                 mDisplayId, mId, {0, nullptr});
+#else
+        intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId,
+                mId, std::vector<Hwc2::IComposerClient::Rect>());
+#endif
     } else {
         size_t rectCount = 0;
         auto rectArray = damage.getArray(&rectCount);
 
+#ifdef BYPASS_IHWC
         std::vector<hwc_rect_t> hwcRects;
+#else
+        std::vector<Hwc2::IComposerClient::Rect> hwcRects;
+#endif
         for (size_t rect = 0; rect < rectCount; ++rect) {
             hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
                     rectArray[rect].right, rectArray[rect].bottom});
         }
 
+#ifdef BYPASS_IHWC
         hwc_region_t hwcRegion = {};
         hwcRegion.numRects = rectCount;
         hwcRegion.rects = hwcRects.data();
 
         intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
                 mDisplayId, mId, hwcRegion);
+#else
+        intError = mDevice.mComposer->setLayerSurfaceDamage(mDisplayId,
+                mId, hwcRects);
+#endif
     }
 
     return static_cast<Error>(intError);
@@ -1015,47 +1268,83 @@
 
 Error Layer::setBlendMode(BlendMode mode)
 {
+#ifdef BYPASS_IHWC
     auto intMode = static_cast<int32_t>(mode);
     int32_t intError = mDevice.mSetLayerBlendMode(mDevice.mHwcDevice,
             mDisplayId, mId, intMode);
+#else
+    auto intMode = static_cast<Hwc2::IComposerClient::BlendMode>(mode);
+    auto intError = mDevice.mComposer->setLayerBlendMode(mDisplayId,
+            mId, intMode);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Layer::setColor(hwc_color_t color)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetLayerColor(mDevice.mHwcDevice, mDisplayId,
             mId, color);
+#else
+    Hwc2::IComposerClient::Color hwcColor{color.r, color.g, color.b, color.a};
+    auto intError = mDevice.mComposer->setLayerColor(mDisplayId,
+            mId, hwcColor);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Layer::setCompositionType(Composition type)
 {
+#ifdef BYPASS_IHWC
     auto intType = static_cast<int32_t>(type);
     int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice,
             mDisplayId, mId, intType);
+#else
+    auto intType = static_cast<Hwc2::IComposerClient::Composition>(type);
+    auto intError = mDevice.mComposer->setLayerCompositionType(mDisplayId,
+            mId, intType);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Layer::setDataspace(android_dataspace_t dataspace)
 {
+#ifdef BYPASS_IHWC
     auto intDataspace = static_cast<int32_t>(dataspace);
     int32_t intError = mDevice.mSetLayerDataspace(mDevice.mHwcDevice,
             mDisplayId, mId, intDataspace);
+#else
+    auto intDataspace = static_cast<Hwc2::Dataspace>(dataspace);
+    auto intError = mDevice.mComposer->setLayerDataspace(mDisplayId,
+            mId, intDataspace);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Layer::setDisplayFrame(const Rect& frame)
 {
+#ifdef BYPASS_IHWC
     hwc_rect_t hwcRect{frame.left, frame.top, frame.right, frame.bottom};
     int32_t intError = mDevice.mSetLayerDisplayFrame(mDevice.mHwcDevice,
             mDisplayId, mId, hwcRect);
+#else
+    Hwc2::IComposerClient::Rect hwcRect{frame.left, frame.top,
+        frame.right, frame.bottom};
+    auto intError = mDevice.mComposer->setLayerDisplayFrame(mDisplayId,
+            mId, hwcRect);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Layer::setPlaneAlpha(float alpha)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetLayerPlaneAlpha(mDevice.mHwcDevice,
             mDisplayId, mId, alpha);
+#else
+    auto intError = mDevice.mComposer->setLayerPlaneAlpha(mDisplayId,
+            mId, alpha);
+#endif
     return static_cast<Error>(intError);
 }
 
@@ -1066,24 +1355,42 @@
                 "device supports sideband streams");
         return Error::Unsupported;
     }
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetLayerSidebandStream(mDevice.mHwcDevice,
             mDisplayId, mId, stream);
+#else
+    auto intError = mDevice.mComposer->setLayerSidebandStream(mDisplayId,
+            mId, stream);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Layer::setSourceCrop(const FloatRect& crop)
 {
+#ifdef BYPASS_IHWC
     hwc_frect_t hwcRect{crop.left, crop.top, crop.right, crop.bottom};
     int32_t intError = mDevice.mSetLayerSourceCrop(mDevice.mHwcDevice,
             mDisplayId, mId, hwcRect);
+#else
+    Hwc2::IComposerClient::FRect hwcRect{
+        crop.left, crop.top, crop.right, crop.bottom};
+    auto intError = mDevice.mComposer->setLayerSourceCrop(mDisplayId,
+            mId, hwcRect);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Layer::setTransform(Transform transform)
 {
+#ifdef BYPASS_IHWC
     auto intTransform = static_cast<int32_t>(transform);
     int32_t intError = mDevice.mSetLayerTransform(mDevice.mHwcDevice,
             mDisplayId, mId, intTransform);
+#else
+    auto intTransform = static_cast<Hwc2::Transform>(transform);
+    auto intError = mDevice.mComposer->setLayerTransform(mDisplayId,
+            mId, intTransform);
+#endif
     return static_cast<Error>(intError);
 }
 
@@ -1092,26 +1399,51 @@
     size_t rectCount = 0;
     auto rectArray = region.getArray(&rectCount);
 
+#ifdef BYPASS_IHWC
     std::vector<hwc_rect_t> hwcRects;
+#else
+    std::vector<Hwc2::IComposerClient::Rect> hwcRects;
+#endif
     for (size_t rect = 0; rect < rectCount; ++rect) {
         hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
                 rectArray[rect].right, rectArray[rect].bottom});
     }
 
+#ifdef BYPASS_IHWC
     hwc_region_t hwcRegion = {};
     hwcRegion.numRects = rectCount;
     hwcRegion.rects = hwcRects.data();
 
     int32_t intError = mDevice.mSetLayerVisibleRegion(mDevice.mHwcDevice,
             mDisplayId, mId, hwcRegion);
+#else
+    auto intError = mDevice.mComposer->setLayerVisibleRegion(mDisplayId,
+            mId, hwcRects);
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Layer::setZOrder(uint32_t z)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetLayerZOrder(mDevice.mHwcDevice, mDisplayId,
             mId, z);
+#else
+    auto intError = mDevice.mComposer->setLayerZOrder(mDisplayId, mId, z);
+#endif
     return static_cast<Error>(intError);
 }
 
+Error Layer::setInfo(uint32_t type, uint32_t appId)
+{
+#ifdef BYPASS_IHWC
+  (void)type;
+  (void)appId;
+  int32_t intError = 0;
+#else
+  auto intError = mDevice.mComposer->setLayerInfo(mDisplayId, mId, type, appId);
+#endif
+  return static_cast<Error>(intError);
+}
+
 } // namespace HWC2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 32a9de0..643b1e0 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>
@@ -42,6 +42,9 @@
     class GraphicBuffer;
     class Rect;
     class Region;
+    namespace Hwc2 {
+        class Composer;
+    }
 }
 
 namespace HWC2 {
@@ -54,10 +57,19 @@
 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:
+#ifdef BYPASS_IHWC
     explicit Device(hwc2_device_t* device);
+#else
+    // 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);
+#endif
     ~Device();
 
     friend class HWC2::Display;
@@ -95,9 +107,16 @@
 
     bool hasCapability(HWC2::Capability capability) const;
 
+#ifdef BYPASS_IHWC
+    android::Hwc2::Composer* getComposer() { return nullptr; }
+#else
+    android::Hwc2::Composer* getComposer() { return mComposer.get(); }
+#endif
+
 private:
     // Initialization methods
 
+#ifdef BYPASS_IHWC
     template <typename PFN>
     [[clang::warn_unused_result]] bool loadFunctionPointer(
             FunctionDescriptor desc, PFN& outPFN) {
@@ -121,6 +140,7 @@
         auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
         mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
     }
+#endif
 
     void loadCapabilities();
     void loadFunctionPointers();
@@ -132,6 +152,7 @@
 
     // Member variables
 
+#ifdef BYPASS_IHWC
     hwc2_device_t* mHwcDevice;
 
     // Device function pointers
@@ -181,6 +202,9 @@
     HWC2_PFN_SET_LAYER_TRANSFORM mSetLayerTransform;
     HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
     HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
+#else
+    std::unique_ptr<android::Hwc2::Composer> mComposer;
+#endif // BYPASS_IHWC
 
     std::unordered_set<Capability> mCapabilities;
     std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays;
@@ -194,6 +218,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 +325,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);
@@ -327,12 +352,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 +369,12 @@
     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;
 };
 
+// Convenience C++ class to access hwc2_device_t Layer functions directly.
 class Layer
 {
 public:
@@ -365,7 +385,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 +407,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 617ea9f..0000000
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ /dev/null
@@ -1,2667 +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 <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 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 962361e..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:
-    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);
-    }
-
-    // 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:
-            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; }
-
-            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 f0ded39..40979c9 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -47,8 +47,9 @@
 #include <log/log.h>
 
 #include "HWComposer.h"
-#include "HWC2On1Adapter.h"
+#include "hwc2on1adapter/HWC2On1Adapter.h"
 #include "HWC2.h"
+#include "ComposerHal.h"
 
 #include "../Layer.h"           // needed only for debugging
 #include "../SurfaceFlinger.h"
@@ -59,9 +60,8 @@
 
 // ---------------------------------------------------------------------------
 
-HWComposer::HWComposer(const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger),
-      mAdapter(),
+HWComposer::HWComposer(bool useVrComposer)
+    : mAdapter(),
       mHwcDevice(),
       mDisplayData(2),
       mFreeDisplaySlots(),
@@ -69,14 +69,15 @@
       mCBContext(),
       mEventHandler(nullptr),
       mVSyncCounts(),
-      mRemainingHwcVirtualDisplays(0)
+      mRemainingHwcVirtualDisplays(0),
+      mDumpMayLockUp(false)
 {
     for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
         mLastHwVSync[i] = 0;
         mVSyncCounts[i] = 0;
     }
 
-    loadHwcModule();
+    loadHwcModule(useVrComposer);
 }
 
 HWComposer::~HWComposer() {}
@@ -105,10 +106,13 @@
 }
 
 // Load and prepare the hardware composer module.  Sets mHwc.
-void HWComposer::loadHwcModule()
+void HWComposer::loadHwcModule(bool useVrComposer)
 {
     ALOGV("loadHwcModule");
 
+#ifdef BYPASS_IHWC
+    (void)useVrComposer; // Silence unused parameter warning.
+
     hw_module_t const* module;
 
     if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
@@ -140,6 +144,9 @@
         mHwcDevice = std::make_unique<HWC2::Device>(
                 static_cast<hwc2_device_t*>(mAdapter.get()));
     }
+#else
+    mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer);
+#endif
 
     mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
 }
@@ -199,12 +206,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 +257,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 +267,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 +321,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 +358,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 +420,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 +437,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 +445,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 +465,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));
@@ -481,6 +494,8 @@
         return NO_ERROR;
     }
 
+    mDumpMayLockUp = true;
+
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
     auto error = hwcDisplay->validate(&numTypes, &numRequests);
@@ -589,12 +604,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 +626,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 +635,21 @@
 
     auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
-    auto error = hwcDisplay->present(&displayData.lastRetireFence);
+    auto error = hwcDisplay->present(&displayData.lastPresentFence);
+
+    mDumpMayLockUp = false;
+
     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,7 +875,20 @@
 }
 */
 
+bool HWComposer::isUsingVrComposer() const {
+#ifdef BYPASS_IHWC
+    return false;
+#else
+    return getComposer()->isUsingVrComposer();
+#endif
+}
+
 void HWComposer::dump(String8& result) const {
+    if (mDumpMayLockUp) {
+        result.append("HWComposer dump skipped because present in progress");
+        return;
+    }
+
     // 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
     // Layer itself, but it's going to take a bit of work to get there.
@@ -869,7 +901,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..78d0307 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -62,20 +62,24 @@
 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 +98,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 +123,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 +144,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 +162,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 +193,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;
@@ -198,7 +205,6 @@
         HWC2::Vsync vsyncEnabled;
     };
 
-    sp<SurfaceFlinger>              mFlinger;
     std::unique_ptr<HWC2On1Adapter> mAdapter;
     std::unique_ptr<HWC2::Device>   mHwcDevice;
     std::vector<DisplayData>        mDisplayData;
@@ -218,6 +224,9 @@
 
     // thread-safe
     mutable Mutex mVsyncLock;
+
+    // XXX temporary workaround for b/35806047
+    mutable std::atomic<bool> mDumpMayLockUp;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
new file mode 100644
index 0000000..6b91224
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposerBufferCache.cpp
@@ -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.
+ */
+
+#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)
+{
+#ifdef BYPASS_IHWC
+    *outSlot = slot;
+    *outBuffer = buffer;
+#else
+    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;
+    }
+#endif
+}
+
+} // 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 52cbb34..dcb2913 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -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 a4a1f99..a6f076e 100644
--- a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android/hardware/power/1.0/IPower.h>
 #include <stdint.h>
 #include <sys/types.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 9368db6..1b1ef75 100644
--- a/services/surfaceflinger/EventControlThread.h
+++ b/services/surfaceflinger/EventControlThread.h
@@ -45,4 +45,4 @@
 
 }
 
-#endif // ANDROID_DISPSYNC_H
+#endif // ANDROID_EVENTCONTROLTHREAD_H
diff --git a/services/surfaceflinger/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 b635115..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>
 
@@ -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 99c4f62..99c4daa 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -22,7 +22,6 @@
 #include <android/log.h>
 #include <utils/String8.h>
 
-#include <ui/Fence.h>
 #include <ui/FrameStats.h>
 
 #include "FrameTracker.h"
@@ -47,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++;
 }
 
@@ -58,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++;
 }
 
@@ -94,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() {
@@ -106,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;
@@ -155,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) {
@@ -165,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 0a795aa..6b09df2 100755
--- 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),
@@ -136,6 +141,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,23 +157,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);
 
-#ifndef TARGET_DISABLE_TRIPLE_BUFFERING
-    mProducer->setMaxDequeuedBufferCount(2);
-#endif
+    if (mFlinger->isLayerTripleBufferingDisabled()) {
+        mProducer->setMaxDequeuedBufferCount(2);
+    }
 
     const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
     updateTransformHint(hw);
@@ -212,7 +223,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) {
@@ -275,7 +287,18 @@
 // 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();
+    for (const auto& child : mCurrentChildren) {
+        child->onRemoved();
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -312,22 +335,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);
 
@@ -374,6 +381,40 @@
     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);
+
+    const sp<Layer>& p = getParent();
+    // 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);
@@ -386,15 +427,27 @@
     if (!s.crop.isEmpty()) {
         win.intersect(s.crop, &win);
     }
+
+    Rect bounds = win;
+    const auto& p = getParent();
+    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());
@@ -411,7 +464,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();
     }
@@ -420,7 +474,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 = getParent();
+    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
@@ -439,7 +513,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
@@ -494,7 +568,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,
@@ -528,16 +602,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);
@@ -547,9 +622,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
@@ -557,7 +633,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
@@ -576,7 +652,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();
@@ -588,7 +665,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,
@@ -609,15 +686,20 @@
         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));
+
+    error = hwcLayer->setInfo(s.type, s.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();
@@ -625,7 +707,7 @@
     const Transform& tr(hw->getTransform());
     layer.setFrame(tr.transform(frame));
     layer.setCrop(computeCrop(hw));
-    layer.setPlaneAlpha(s.alpha);
+    layer.setPlaneAlpha(getAlpha());
 #endif
 
     /*
@@ -637,9 +719,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
@@ -651,8 +733,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
@@ -687,9 +775,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.
@@ -697,7 +783,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(),
@@ -726,7 +813,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);
@@ -764,14 +851,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) {
@@ -815,7 +919,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);
@@ -864,7 +968,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);
@@ -891,6 +995,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
 {
@@ -907,19 +1032,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.layersSortedByZ.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;
     }
@@ -946,30 +1070,29 @@
         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.
+             */
+            if (getParent() != nullptr) {
+                const auto parentTransform = getParent()->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);
@@ -985,13 +1108,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());
@@ -1001,12 +1124,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);
@@ -1027,12 +1150,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();
         }
@@ -1052,7 +1176,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();
 }
@@ -1202,6 +1329,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)
@@ -1231,25 +1360,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()) {
@@ -1260,10 +1385,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;
     }
@@ -1334,29 +1459,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;
             }
         }
 
@@ -1379,7 +1498,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
@@ -1545,11 +1664,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
@@ -1588,15 +1703,73 @@
     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;
@@ -1622,7 +1795,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;
@@ -1657,11 +1830,15 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
-bool Layer::setFinalCrop(const Rect& crop) {
+
+bool Layer::setFinalCrop(const Rect& crop, bool immediate) {
     if (mCurrentState.finalCrop == crop)
         return false;
     mCurrentState.sequence++;
-    mCurrentState.finalCrop = crop;
+    mCurrentState.requestedFinalCrop = crop;
+    if (immediate) {
+        mCurrentState.finalCrop = crop;
+    }
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1675,6 +1852,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;
@@ -1692,17 +1876,42 @@
     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 = getParent();
+    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;
+    ALOGE("Deferred transaction");
+}
+
+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() {
@@ -1744,65 +1953,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 = getParent();
+    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();
 
@@ -1816,338 +2087,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, mFreezePositionUpdates);
+    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;
 }
 
@@ -2209,7 +2342,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,
@@ -2304,22 +2439,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(
@@ -2339,19 +2482,191 @@
     return mSurfaceFlingerConsumer->getTransformToDisplayInverse();
 }
 
-// ---------------------------------------------------------------------------
-
-Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
-        const sp<Layer>& layer)
-    : mFlinger(flinger), mLayer(layer) {
+void Layer::addChild(const sp<Layer>& layer) {
+    mCurrentChildren.add(layer);
+    layer->setParent(this);
 }
 
-Layer::LayerCleaner::~LayerCleaner() {
-    // destroy client resources
-    mFlinger->onLayerDestroyed(mLayer);
+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([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) {
+    mParent = 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() {
+    if (mDrawingState.zOrderRelatives.size() == 0) {
+        return mDrawingChildren;
+    }
+    LayerVector traverse;
+
+    for (const wp<Layer>& weakRelative : mDrawingState.zOrderRelatives) {
+        sp<Layer> strongRelative = weakRelative.promote();
+        if (strongRelative != nullptr) {
+            traverse.add(strongRelative);
+        }
+    }
+
+    for (const sp<Layer>& child : mDrawingChildren) {
+        traverse.add(child);
+    }
+
+    return traverse;
+}
+
+/**
+ * Negatively signed relatives are before 'this' in Z-order.
+ */
+void Layer::traverseInZOrder(const std::function<void(Layer*)>& exec) {
+    LayerVector list = makeTraversalList();
+
+    size_t i = 0;
+    for (; i < list.size(); i++) {
+        const auto& relative = list[i];
+        if (relative->getZ() >= 0) {
+            break;
+        }
+        relative->traverseInZOrder(exec);
+    }
+    exec(this);
+    for (; i < list.size(); i++) {
+        const auto& relative = list[i];
+        relative->traverseInZOrder(exec);
+    }
+}
+
+/**
+ * Positively signed relatives are before 'this' in reverse Z-order.
+ */
+void Layer::traverseInReverseZOrder(const std::function<void(Layer*)>& exec) {
+    LayerVector list = makeTraversalList();
+
+    int32_t i = 0;
+    for (i = list.size()-1; i>=0; i--) {
+        const auto& relative = list[i];
+        if (relative->getZ() < 0) {
+            break;
+        }
+        relative->traverseInReverseZOrder(exec);
+    }
+    exec(this);
+    for (; i>=0; i--) {
+        const auto& relative = list[i];
+        relative->traverseInReverseZOrder(exec);
+    }
+}
+
+Transform Layer::getTransform() const {
+    Transform t;
+    const auto& p = getParent();
+    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()) {
+            float sx = p->getDrawingState().active.w /
+                    static_cast<float>(p->mActiveBuffer->getWidth());
+            float sy = p->getDrawingState().active.h /
+                    static_cast<float>(p->mActiveBuffer->getHeight());
+            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 = getParent();
+
+    float parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0;
+    return parentAlpha * getDrawingState().alpha;
+}
+#else
+uint8_t Layer::getAlpha() const {
+    const auto& p = getParent();
+
+    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;
 }
 
 // ---------------------------------------------------------------------------
+
 }; // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 24de87e..a5224ec 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;
     };
 
     // -----------------------------------------------------------------------
@@ -149,8 +170,17 @@
     status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags);
 
     // modify current state
+
+    // These members of state (position, crop, and finalCrop)
+    // may be updated immediately or have the update delayed
+    // until a pending surface resize completes (if applicable).
     bool setPosition(float x, float y, bool immediate);
-    bool setLayer(uint32_t z);
+    bool setCrop(const Rect& crop, bool immediate);
+    bool setFinalCrop(const Rect& crop, bool immediate);
+
+    bool setLayer(int32_t z);
+    bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
+
     bool setSize(uint32_t w, uint32_t h);
 #ifdef USE_HWC2
     bool setAlpha(float alpha);
@@ -160,11 +190,15 @@
     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 +214,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 +248,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 +271,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 +314,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 +369,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 +426,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 +452,51 @@
     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(const std::function<void(Layer*)>& exec);
+    void traverseInZOrder(const std::function<void(Layer*)>& exec);
+
+    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 mParent.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 +505,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 +534,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,6 +555,12 @@
     // Loads the corresponding system property once per process
     static bool latchUnsignaledBuffers();
 
+    void setParent(const sp<Layer>& layer);
+
+    LayerVector makeTraversalList();
+    void addZOrderRelative(const wp<Layer>& relative);
+    void removeZOrderRelative(const wp<Layer>& relative);
+
     // -----------------------------------------------------------------------
 
     class SyncPoint
@@ -526,16 +615,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 +671,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 +692,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 +721,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 +750,17 @@
     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;
+
+    // Child list about to be committed/used for editing.
+    LayerVector mCurrentChildren;
+    // Child list used for rendering.
+    LayerVector mDrawingChildren;
+
+    wp<Layer> mParent;
 };
 
 // ---------------------------------------------------------------------------
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..0b302eb
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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),
+    mFreezePositionUpdates(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;
+        }
+
+        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.
+    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;
+    }
+
+    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;
+    }
+    mFreezePositionUpdates = false;
+
+    return false;
+}
+
+}  // namespace android
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
new file mode 100644
index 0000000..c2a9483
--- /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 &mFreezePositionUpdates;
+    };
+}  // namespace android
+
+#endif  // ANDROID_LAYER_REJECTER_H
\ No newline at end of file
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
new file mode 100644
index 0000000..90e6395
--- /dev/null
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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(const LayerVector& rhs) : SortedVector<sp<Layer>>(rhs) {
+}
+
+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(const std::function<void(Layer*)>& consume) const {
+    for (size_t i = 0; i < size(); i++) {
+        const auto& layer = (*this)[i];
+        if (layer->getDrawingState().zOrderRelativeOf != nullptr) {
+            continue;
+        }
+        layer->traverseInZOrder(consume);
+    }
+}
+
+void LayerVector::traverseInReverseZOrder(const std::function<void(Layer*)>& consume) const {
+    for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+        const auto& layer = (*this)[i];
+        if (layer->getDrawingState().zOrderRelativeOf != nullptr) {
+            continue;
+        }
+        layer->traverseInReverseZOrder(consume);
+     }
+}
+} // namespace android
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
new file mode 100644
index 0000000..7dfa4a0
--- /dev/null
+++ b/services/surfaceflinger/LayerVector.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_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() = default;
+    LayerVector(const LayerVector& rhs);
+    // Sorts layer by layer-stack, Z order, and finally creation order (sequence).
+    int do_compare(const void* lhs, const void* rhs) const override;
+
+    void traverseInReverseZOrder(const std::function<void(Layer*)>& consume) const;
+    void traverseInZOrder(const std::function<void(Layer*)>& consume) 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 aed0aa9..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"
@@ -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/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 3af8a34..7564269 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -19,12 +19,13 @@
 #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) {
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 42a421d..9cd1214 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <errno.h>
 #include <math.h>
+#include <mutex>
 #include <dlfcn.h>
 #include <inttypes.h>
 #include <stdatomic.h>
@@ -32,18 +33,18 @@
 
 #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,10 +542,10 @@
         // 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
@@ -484,11 +563,34 @@
     // 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) {
+            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 +599,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 +612,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 +643,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 +748,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 +763,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;
@@ -660,10 +792,12 @@
         ALOGE("%s : display is NULL", __func__);
         return BAD_VALUE;
     }
-    sp<DisplayDevice> device(getDisplayDevice(display));
+
+    sp<const DisplayDevice> device(getDisplayDevice(display));
     if (device != NULL) {
         return device->getActiveConfig();
     }
+
     return BAD_VALUE;
 }
 
@@ -751,7 +885,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();
     }
@@ -760,13 +894,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;
     }
 
@@ -775,6 +906,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);
 }
@@ -795,17 +929,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);
             }
@@ -833,7 +967,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;
@@ -850,6 +984,40 @@
     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() {
@@ -949,7 +1117,14 @@
     }
 }
 
-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
@@ -966,29 +1141,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);
@@ -1004,25 +1214,121 @@
     }
 }
 
+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();
+    initializeDisplays();
+}
+
+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();
+
+    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;
@@ -1042,7 +1348,7 @@
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
-    uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+    uint32_t transactionFlags = peekTransactionFlags();
     if (transactionFlags) {
         handleTransaction(transactionFlags);
         return true;
@@ -1060,14 +1366,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) {
@@ -1076,10 +1382,6 @@
                 mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
     }
 
-    // Release any buffers which were replaced this frame
-    for (auto& layer : mLayersWithQueuedFrames) {
-        layer->releasePendingBuffer();
-    }
     mLayersWithQueuedFrames.clear();
 }
 
@@ -1127,40 +1429,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)) {
@@ -1170,21 +1565,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.
@@ -1203,9 +1595,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 {
@@ -1226,7 +1617,6 @@
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        const LayerVector& layers(mDrawingState.layersSortedByZ);
         for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
             Region opaqueRegion;
             Region dirtyRegion;
@@ -1235,15 +1625,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);
@@ -1255,8 +1642,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);
@@ -1267,6 +1660,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");
@@ -1309,20 +1735,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);
                     }
@@ -1338,6 +1763,7 @@
     for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
         auto& displayDevice = mDisplays[displayId];
         const auto hwcId = displayDevice->getHwcDisplayId();
+
         if (hwcId < 0) {
             continue;
         }
@@ -1349,6 +1775,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;
@@ -1402,16 +1843,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) {
@@ -1430,7 +1865,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();
     }
@@ -1467,13 +1903,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
@@ -1481,15 +1914,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;
-        }
+        });
     }
 
     /*
@@ -1519,9 +1951,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)
@@ -1541,7 +1973,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);
@@ -1551,7 +1983,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);
@@ -1580,8 +2012,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()) {
@@ -1590,7 +2021,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);
@@ -1628,24 +2060,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);
@@ -1682,13 +2109,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,
@@ -1713,10 +2140,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;
+        });
     }
 
 
@@ -1724,9 +2153,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;
     }
 
@@ -1735,20 +2164,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();
@@ -1774,10 +2200,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();
     }
@@ -1787,13 +2213,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();
@@ -1805,16 +2233,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.
@@ -1849,12 +2274,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);
@@ -1866,7 +2291,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
@@ -1923,7 +2348,7 @@
         layer->setCoveredRegion(coveredRegion);
         layer->setVisibleNonTransparentRegion(
                 visibleRegion.subtract(transparentRegion));
-    }
+    });
 
     outOpaqueRegion = aboveOpaqueLayers;
 }
@@ -1942,11 +2367,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.
@@ -1957,24 +2382,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;
@@ -1987,7 +2414,7 @@
     }
 
     // Only continue with the refresh if there is actually new work to do
-    return !mLayersWithQueuedFrames.empty();
+    return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
 
 void SurfaceFlinger::invalidateHwcGeometry()
@@ -1996,14 +2423,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;
@@ -2014,35 +2442,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(
@@ -2064,11 +2492,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;
@@ -2151,7 +2585,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;
                     }
@@ -2187,8 +2621,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);
 }
@@ -2196,16 +2630,23 @@
 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 {
+            parent->addChild(lbc);
+        }
         mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
+        mLayersAdded = true;
+        mNumLayers++;
     }
 
     // attach this layer to the client
@@ -2214,25 +2655,35 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) {
+status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) {
     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();
+    const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
+        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--;
+    setTransactionFlags(eTransactionNeeded);
+    return NO_ERROR;
 }
 
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
     return android_atomic_release_load(&mTransactionFlags);
 }
 
@@ -2308,6 +2759,10 @@
     }
 
     if (transactionFlags) {
+        if (mInterceptor.isEnabled()) {
+            mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+        }
+
         // this triggers the transaction
         setTransactionFlags(transactionFlags);
 
@@ -2399,12 +2854,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;
             }
         }
@@ -2434,13 +2901,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)
@@ -2449,10 +2924,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
@@ -2466,9 +2961,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));
@@ -2479,15 +2974,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:
@@ -2499,15 +2996,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)
@@ -2546,10 +3070,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));
@@ -2561,7 +3086,15 @@
 {
     // 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 (l->getParent() != nullptr) {
+        // If we have a parent, then we can continue to live as long as it does.
+        return NO_ERROR;
+    }
+    return removeLayer(l);
 }
 
 // ---------------------------------------------------------------------------
@@ -2587,13 +3120,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;
@@ -2611,7 +3148,6 @@
     int currentMode = hw->getPowerMode();
 
     if (mode == currentMode) {
-        ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode);
         return;
     }
 
@@ -2621,6 +3157,16 @@
         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);
@@ -2656,22 +3202,6 @@
         getHwComposer().setPowerMode(type, mode);
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
-    } else if (mode == HWC_POWER_MODE_DOZE) {
-        // Update display while dozing
-        getHwComposer().setPowerMode(type, mode);
-        if (type == DisplayDevice::DISPLAY_PRIMARY) {
-            // 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 {
         getHwComposer().setPowerMode(type, mode);
     }
@@ -2769,9 +3299,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 +3327,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 +3348,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 +3365,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 +3377,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 +3436,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 +3480,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 +3536,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 +3551,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 +3566,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 +3594,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 +3649,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 +3688,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 +3709,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 +3725,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 +3749,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 +3823,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 +3894,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 +4007,7 @@
     }
 
 public:
-    GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+    explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
     :   impl(impl),
         looper(new Looper(true)),
         result(NO_ERROR),
@@ -3457,7 +4041,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 +4092,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 +4109,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 +4141,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 +4185,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([&](Layer* layer) {
+            if (!layer->isVisible()) {
+                return;
+            }
+            if (filtering) layer->setFiltering(true);
+            layer->draw(hw, useIdentityTransform);
+            if (filtering) layer->setFiltering(false);
+        });
     }
 
     hw->setViewportAndProjection();
@@ -3625,7 +4213,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 +4237,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([&](Layer *layer) {
+            secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() &&
+                    layer->isSecure());
+        });
     }
 
     if (!isLocalScreenshot && secureLayerIsVisible) {
@@ -3786,7 +4374,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 +4385,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([&](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 std::function<void(Layer*)>& consume) const {
+    layersSortedByZ.traverseInZOrder(consume);
 }
 
-SurfaceFlinger::LayerVector::LayerVector(const LayerVector& rhs)
-    : SortedVector<sp<Layer> >(rhs) {
+void SurfaceFlinger::State::traverseInReverseZOrder(const std::function<void(Layer*)>& consume) const {
+    layersSortedByZ.traverseInReverseZOrder(consume);
 }
 
-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..c01f701 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,13 @@
      * 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;
-    };
-
-    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 {
         LayerVector layersSortedByZ;
         DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
+
+        void traverseInZOrder(const std::function<void(Layer*)>& consume) const;
+        void traverseInReverseZOrder(const std::function<void(Layer*)>& consume) const;
     };
 
     /* ------------------------------------------------------------------------
@@ -204,7 +256,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 +265,13 @@
     virtual void bootFinished();
     virtual bool authenticateSurfaceTexture(
         const sp<IGraphicBufferProducer>& bufferProducer) const;
+    virtual status_t getSupportedFrameTimestamps(
+            std::vector<FrameEvent>* outSupported) const;
     virtual sp<IDisplayEventConnection> createDisplayEventConnection();
     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 +288,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 +305,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 +350,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 +361,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 +373,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 +385,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);
 
     // 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 +403,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 +431,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 +486,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 +523,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 +540,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
@@ -473,27 +582,39 @@
     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;
@@ -509,6 +630,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 +648,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 +671,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 +704,8 @@
     nsecs_t mTotalTime;
     std::atomic<nsecs_t> mLastSwapTime;
 
+    size_t mNumLayers;
+
     // Double- vs. triple-buffering stats
     struct BufferingStats {
         BufferingStats()
@@ -587,8 +728,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 6f2520b..9babeef 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>
@@ -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);
@@ -253,10 +252,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 12aa534..02923ae 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -24,6 +24,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <mutex>
+
 #include <EGL/egl.h>
 
 #include <cutils/properties.h>
@@ -31,18 +33,15 @@
 
 #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,10 +514,10 @@
     // 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
@@ -487,6 +542,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 +557,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 +565,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 +579,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 +597,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 +611,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 +633,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 +725,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 +740,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 +765,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,6 +894,40 @@
     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() {
@@ -908,7 +1027,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 +1045,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 +1073,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 +1105,7 @@
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
-    uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+    uint32_t transactionFlags = peekTransactionFlags();
     if (transactionFlags) {
         handleTransaction(transactionFlags);
         return true;
@@ -994,7 +1123,7 @@
 
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    preComposition();
+    preComposition(refreshStartTime);
     rebuildLayerStacks();
     setUpHWComposer();
     doDebugFlashRegions();
@@ -1042,59 +1171,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 +1345,6 @@
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        const LayerVector& layers(mDrawingState.layersSortedByZ);
         for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
             Region opaqueRegion;
             Region dirtyRegion;
@@ -1141,14 +1353,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 +1365,7 @@
                             layersSortedByZ.add(layer);
                         }
                     }
-                }
+                });
             }
             hw->setVisibleLayersSortedByZ(layersSortedByZ);
             hw->undefinedRegion.set(bounds);
@@ -1379,13 +1588,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 +1599,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 +1636,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 +1658,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 +1668,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 +1697,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 +1717,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 +1749,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 +1792,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 +1823,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 +1836,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 +1847,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 +1895,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 +1908,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 +1927,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 +1968,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 +1985,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 +2042,7 @@
         layer->setCoveredRegion(coveredRegion);
         layer->setVisibleNonTransparentRegion(
                 visibleRegion.subtract(transparentRegion));
-    }
+    });
 
     outOpaqueRegion = aboveOpaqueLayers;
 }
@@ -1857,10 +2059,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 +2075,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 +2263,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 +2307,23 @@
 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 {
+            parent->addChild(lbc);
+        }
         mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
+        mLayersAdded = true;
+        mNumLayers++;
     }
 
     // attach this layer to the client
@@ -2125,25 +2332,35 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) {
+status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) {
     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();
+    const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
+             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--;
+    setTransactionFlags(eTransactionNeeded);
+    return NO_ERROR;
 }
 
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
     return android_atomic_release_load(&mTransactionFlags);
 }
 
@@ -2219,6 +2436,10 @@
     }
 
     if (transactionFlags) {
+        if (mInterceptor.isEnabled()) {
+            mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+        }
+
         // this triggers the transaction
         setTransactionFlags(transactionFlags);
 
@@ -2310,13 +2531,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 +2573,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 +2596,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 +2633,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 +2646,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 +2668,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 +2742,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 +2758,15 @@
 {
     // 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 (l->getParent() != nullptr) {
+        // If we have a parent, then we can continue to live as long as it does.
+        return NO_ERROR;
+    }
+    return removeLayer(l);
 }
 
 // ---------------------------------------------------------------------------
@@ -2498,13 +2792,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,6 +2830,16 @@
         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);
@@ -2567,22 +2875,6 @@
         getHwComposer().setPowerMode(type, mode);
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
-    } else if (mode == HWC_POWER_MODE_DOZE) {
-        // Update display while dozing
-        getHwComposer().setPowerMode(type, mode);
-        if (type == DisplayDevice::DISPLAY_PRIMARY) {
-            // 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 {
         getHwComposer().setPowerMode(type, mode);
     }
@@ -2680,9 +2972,9 @@
             }
 
             if ((index < numArgs) &&
-                    (args[index] == String16("--fences"))) {
+                    (args[index] == String16("--frame-events"))) {
                 index++;
-                mFenceTracker.dump(&result);
+                dumpFrameEventsLocked(result);
                 dumpAll = false;
             }
         }
@@ -2702,12 +2994,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,
@@ -2726,14 +3015,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);
             }
-        }
+        });
     }
 }
 
@@ -2746,14 +3032,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();
 }
@@ -2761,31 +3044,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
@@ -2805,6 +3085,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);
@@ -2898,8 +3188,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");
 
@@ -2913,15 +3203,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
@@ -2944,7 +3231,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",
@@ -3020,7 +3307,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()
@@ -3047,7 +3334,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:
@@ -3066,6 +3352,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
@@ -3210,6 +3507,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;
@@ -3311,7 +3620,7 @@
     }
 
 public:
-    GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+    explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
     :   impl(impl),
         looper(new Looper(true)),
         result(NO_ERROR),
@@ -3345,7 +3654,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))
@@ -3386,7 +3695,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;
@@ -3396,7 +3705,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)
@@ -3413,7 +3722,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);
@@ -3445,7 +3754,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();
@@ -3489,20 +3798,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([&](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
@@ -3515,7 +3828,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)
 {
@@ -3539,16 +3852,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([&](Layer *layer) {
+            secureLayerIsVisible = secureLayerIsVisible || (layer->isVisible() &&
+                    layer->isSecure());
+        });
     }
 
     if (!isLocalScreenshot && secureLayerIsVisible) {
@@ -3667,13 +3980,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;
@@ -3684,76 +3992,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([&](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 std::function<void(Layer*)>& consume) const {
+    layersSortedByZ.traverseInZOrder(consume);
 }
 
-SurfaceFlinger::LayerVector::LayerVector(const LayerVector& rhs)
-    : SortedVector<sp<Layer> >(rhs) {
+void SurfaceFlinger::State::traverseInReverseZOrder(const std::function<void(Layer*)>& consume) const {
+    layersSortedByZ.traverseInReverseZOrder(consume);
 }
 
-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..026fe80
--- /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([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 f151087..d15376e 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -18,17 +18,44 @@
 
 #include <sched.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 <hidl/LegacySupport.h>
+#include <configstore/Utils.h>
 #include "GpuService.h"
 #include "SurfaceFlinger.h"
 
 using namespace android;
 
+using android::hardware::graphics::allocator::V2_0::IAllocator;
+using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
+using android::hardware::configstore::getBool;
+using android::hardware::configstore::getBool;
+
+static status_t startGraphicsAllocatorService() {
+    hardware::configureRpcThreadpool( 1 /* maxThreads */,
+            false /* callerWillJoin */);
+    status_t result =
+        hardware::registerPassthroughServiceImplementation<IAllocator>();
+    if (result != OK) {
+        ALOGE("could not start graphics allocator service");
+        return result;
+    }
+
+    return OK;
+}
+
 int main(int, char**) {
+    if (getBool<ISurfaceFlingerConfigs,
+            &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(false)) {
+        startGraphicsAllocatorService();
+    }
+
     signal(SIGPIPE, SIG_IGN);
     // When SF is launched in its own process, limit the number of
     // binder threads to 4.
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index 435aa0c..1c0427d 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -1,6 +1,10 @@
 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
+    socket pdx/system/vr/display/manager stream 0666 system graphics
+    socket pdx/system/vr/display/screenshot stream 0660 system graphics
+    socket pdx/system/vr/display/vsync stream 0666 system graphics
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..7a8b4ab 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>
@@ -61,7 +59,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 +82,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 +150,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 +197,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 +209,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 +220,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 +231,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 +246,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 +259,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 +270,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 +283,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 +296,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 +308,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 +321,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 +333,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 +344,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 +356,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 +368,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 +379,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 +391,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 +402,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 +415,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 +426,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 +439,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 +450,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 +463,156 @@
     {
         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 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");
+}
+
 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 +631,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 +641,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,9 +656,288 @@
     {
         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, 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);
     }
 }
 
diff --git a/services/surfaceflinger/tests/hwc2/Android.mk b/services/surfaceflinger/tests/hwc2/Android.mk
index b8c2133..203ced5 100644
--- a/services/surfaceflinger/tests/hwc2/Android.mk
+++ b/services/surfaceflinger/tests/hwc2/Android.mk
@@ -40,7 +40,8 @@
 LOCAL_STATIC_LIBRARIES := \
     libbase \
     libadf \
-    libadfhwc
+    libadfhwc \
+    libmath
 LOCAL_SRC_FILES := \
     Hwc2Test.cpp \
     Hwc2TestProperties.cpp \
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
index 5f90c7a..1d3a1d3 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
@@ -23,7 +23,7 @@
 #include <gui/BufferItemConsumer.h>
 
 #include <ui/GraphicBuffer.h>
-#include <ui/vec4.h>
+#include <math/vec4.h>
 
 #include <GLES3/gl3.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..4b1a522
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -0,0 +1,512 @@
+#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(21) << "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.slice_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.producer_usage;
+      stream << "0x";
+      stream << std::setw(8) << info.consumer_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.slice_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) << " Allocated";
+  stream << std::right << std::setw(12) << " Consumers";
+  stream << " UsageSetMask";
+  stream << " UsageClearMask";
+  stream << " UsageDenySetMask";
+  stream << " UsageDenyClearMask";
+  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.producer_set_mask;
+      stream << std::setw(7) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.producer_clear_mask;
+      stream << std::setw(9) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.producer_deny_set_mask;
+      stream << std::setw(11) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.producer_deny_clear_mask;
+      stream << std::setw(5) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.consumer_set_mask;
+      stream << std::setw(7) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.consumer_clear_mask;
+      stream << std::setw(9) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.consumer_deny_set_mask;
+      stream << std::setw(11) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_policy.consumer_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 producer_usage,
+                                              uint64_t consumer_usage,
+                                              size_t meta_size_bytes,
+                                              size_t slice_count) {
+  // 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 producer_usage=%" PRIx64 " consumer_usage=%" PRIx64
+           " meta_size_bytes=%zu slice_count=%zu",
+           buffer_id, width, height, format, producer_usage, consumer_usage,
+           meta_size_bytes, slice_count);
+
+  // 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);
+  }
+
+  auto status = ProducerChannel::Create(this, buffer_id, width, height, format,
+                                        producer_usage, consumer_usage,
+                                        meta_size_bytes, slice_count);
+  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 producer_usage,
+    uint64_t consumer_usage, size_t meta_size_bytes, size_t slice_count) {
+  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 "
+           "producer_usage=%" PRIx64 " consumer_usage=%" PRIx64
+           " meta_size_bytes=%zu slice_count=%zu",
+           channel_id, name.c_str(), user_id, group_id, width, height, format,
+           producer_usage, consumer_usage, meta_size_bytes, slice_count);
+
+  // 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, format, producer_usage,
+                                        consumer_usage, meta_size_bytes,
+                                        slice_count)) {
+      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, format, producer_usage, consumer_usage,
+        meta_size_bytes, slice_count);
+    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..4cb1eb9
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.h
@@ -0,0 +1,183 @@
+#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 format = 0;
+    uint64_t producer_usage = 0;
+    uint64_t consumer_usage = 0;
+    size_t slice_count = 0;
+    std::string name;
+
+    // Data filed for producer queue.
+    size_t capacity = 0;
+    UsagePolicy usage_policy{0, 0, 0, 0, 0, 0, 0, 0};
+
+    BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height,
+               uint32_t format, uint64_t producer_usage,
+               uint64_t consumer_usage, size_t slice_count,
+               const std::string& name)
+        : id(id),
+          type(kProducerType),
+          consumer_count(consumer_count),
+          width(width),
+          height(height),
+          format(format),
+          producer_usage(producer_usage),
+          consumer_usage(consumer_usage),
+          slice_count(slice_count),
+          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 producer_usage,
+                     uint64_t consumer_usage, size_t meta_size_bytes,
+                     size_t slice_count);
+  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 producer_usage, uint64_t consumer_usage,
+                               size_t meta_size_bytes, size_t slice_count);
+  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..8d57723
--- /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
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
new file mode 100644
index 0000000..311f5c6
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -0,0 +1,185 @@
+#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::GetBuffers::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetBuffers>(
+          *producer, &ProducerChannel::OnGetBuffers, 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..7422751
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -0,0 +1,138 @@
+#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;
+
+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)
+    REPLY_ERROR_RETURN(message, EPIPE, 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) {
+  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(
+      "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)};
+}
+
+}  // 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..e1005e4
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.h
@@ -0,0 +1,62 @@
+#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);
+
+ 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..c946a8d
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -0,0 +1,384 @@
+#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 format, uint64_t producer_usage,
+                                 uint64_t consumer_usage,
+                                 size_t meta_size_bytes, size_t slice_count,
+                                 int* error)
+    : BufferHubChannel(service, channel_id, channel_id, kProducerType),
+      pending_consumers_(0),
+      slices_(std::max(static_cast<size_t>(1), slice_count)),
+      producer_owns_(true),
+      meta_size_bytes_(meta_size_bytes),
+      meta_(meta_size_bytes ? new uint8_t[meta_size_bytes] : nullptr) {
+  for (auto& ion_buffer : slices_) {
+    const int ret =
+        ion_buffer.Alloc(width, height, format, producer_usage, consumer_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 format, uint64_t producer_usage, uint64_t consumer_usage,
+    size_t meta_size_bytes, size_t slice_count) {
+  int error;
+  std::shared_ptr<ProducerChannel> producer(new ProducerChannel(
+      service, channel_id, width, height, format, producer_usage,
+      consumer_usage, meta_size_bytes, slice_count, &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(), slices_[0].width(),
+                    slices_[0].height(), slices_[0].format(),
+                    slices_[0].producer_usage(), slices_[0].consumer_usage(),
+                    slices_.size(), 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::GetBuffers::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetBuffers>(
+          *this, &ProducerChannel::OnGetBuffers, 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, unsigned index) {
+  ATRACE_NAME("ProducerChannel::OnGetBuffer");
+  ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d", buffer_id());
+  if (index < slices_.size()) {
+    return {NativeBufferHandle<BorrowedHandle>(slices_[index], buffer_id())};
+  } else {
+    return ErrorStatus(EINVAL);
+  }
+}
+
+Status<std::vector<NativeBufferHandle<BorrowedHandle>>>
+ProducerChannel::OnGetBuffers(Message&) {
+  ATRACE_NAME("ProducerChannel::OnGetBuffers");
+  ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffers: buffer_id=%d", buffer_id());
+  std::vector<NativeBufferHandle<BorrowedHandle>> buffer_handles;
+  for (const auto& buffer : slices_)
+    buffer_handles.emplace_back(buffer, buffer_id());
+  return {std::move(buffer_handles)};
+}
+
+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())
+    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 format, uint64_t producer_usage,
+                                      uint64_t consumer_usage,
+                                      size_t meta_size_bytes,
+                                      size_t slice_count) {
+  return slices_.size() == slice_count && meta_size_bytes == meta_size_bytes_ &&
+         slices_[0].width() == width && slices_[0].height() == height &&
+         slices_[0].format() == format &&
+         slices_[0].producer_usage() == producer_usage &&
+         slices_[0].consumer_usage() == consumer_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..f04c8a5
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -0,0 +1,114 @@
+#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 format, uint64_t producer_usage,
+      uint64_t consumer_usage, size_t meta_size_bytes, size_t slice_count);
+
+  ~ProducerChannel() override;
+
+  bool HandleMessage(Message& message) override;
+  void HandleImpulse(Message& message) override;
+
+  BufferInfo GetBufferInfo() const override;
+
+  pdx::Status<NativeBufferHandle<BorrowedHandle>> OnGetBuffer(Message& message,
+                                                              unsigned index);
+  pdx::Status<std::vector<NativeBufferHandle<BorrowedHandle>>> OnGetBuffers(
+      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 format,
+                       uint64_t producer_usage, uint64_t consumer_usage,
+                       size_t meta_size_bytes, size_t slice_count);
+
+  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_;
+
+  std::vector<IonBuffer> slices_;
+
+  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 format, uint64_t producer_usage,
+                  uint64_t consumer_usage, size_t meta_size_bytes,
+                  size_t slice_count, 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..dc2a47e
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -0,0 +1,336 @@
+#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() {}
+
+/* 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.producer_deny_set_mask &
+       usage_policy.producer_deny_clear_mask) ||
+      (usage_policy.consumer_deny_set_mask &
+       usage_policy.consumer_deny_clear_mask)) {
+    ALOGE(
+        "BufferHubService::OnCreateProducerQueue: illegal usage mask "
+        "configuration: producer_deny_set_mask=%" PRIx64
+        " producer_deny_clear_mask=%" PRIx64 " consumer_deny_set_mask=%" PRIx64
+        " consumer_deny_clear_mask=%" PRIx64,
+        usage_policy.producer_deny_set_mask,
+        usage_policy.producer_deny_clear_mask,
+        usage_policy.consumer_deny_set_mask,
+        usage_policy.consumer_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);
+  }
+
+  const auto channel_status = service()->SetChannel(
+      channel_id, std::make_shared<ConsumerQueueChannel>(
+                      service(), buffer_id(), channel_id, shared_from_this()));
+  if (!channel_status) {
+    ALOGE(
+        "ProducerQueueChannel::OnCreateConsumerQueue: failed to set new "
+        "consumer 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 format,
+    uint64_t producer_usage, uint64_t consumer_usage, size_t slice_count,
+    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 (producer_usage & usage_policy_.producer_deny_set_mask) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueAllocateBuffers: producer_usage: "
+        "%" PRIx64
+        " is not permitted. Violating producer_deny_set_mask, the following "
+        "bits shall not be set: %" PRIx64 ".",
+        producer_usage, usage_policy_.producer_deny_set_mask);
+    return ErrorStatus(EINVAL);
+  }
+
+  if (consumer_usage & usage_policy_.consumer_deny_set_mask) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueAllocateBuffers: consumer_usage: "
+        "%" PRIx64
+        " is not permitted. Violating consumer_deny_set_mask, the following "
+        "bits shall not be set: %" PRIx64 ".",
+        consumer_usage, usage_policy_.consumer_deny_set_mask);
+    return ErrorStatus(EINVAL);
+  }
+
+  if (~producer_usage & usage_policy_.producer_deny_clear_mask) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueAllocateBuffers: producer_usage: "
+        "%" PRIx64
+        " is not permitted. Violating producer_deny_clear_mask, the following "
+        "bits must be set: %" PRIx64 ".",
+        producer_usage, usage_policy_.producer_deny_clear_mask);
+    return ErrorStatus(EINVAL);
+  }
+
+  if (~consumer_usage & usage_policy_.consumer_deny_clear_mask) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueAllocateBuffers: consumer_usage: "
+        "%" PRIx64
+        " is not permitted. Violating consumer_deny_clear_mask, the following "
+        "bits must be set: %" PRIx64 ".",
+        consumer_usage, usage_policy_.consumer_deny_clear_mask);
+    return ErrorStatus(EINVAL);
+  }
+  // Force set mask and clear mask. Note that |usage_policy_.X_set_mask_| takes
+  // precedence and will overwrite |usage_policy_.X_clear_mask|.
+  uint64_t effective_producer_usage =
+      (producer_usage & ~usage_policy_.producer_clear_mask) |
+      usage_policy_.producer_set_mask;
+  uint64_t effective_consumer_usage =
+      (consumer_usage & ~usage_policy_.consumer_clear_mask) |
+      usage_policy_.consumer_set_mask;
+
+  for (size_t i = 0; i < buffer_count; i++) {
+    auto status =
+        AllocateBuffer(message, width, height, format, effective_producer_usage,
+                       effective_consumer_usage, slice_count);
+    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 format,
+                                     uint64_t producer_usage,
+                                     uint64_t consumer_usage,
+                                     size_t slice_count) {
+  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 format=%u producer_usage=%" PRIx64
+           " consumer_usage=%" PRIx64 " slice_count=%zu",
+           buffer_id, width, height, format, producer_usage, consumer_usage,
+           slice_count);
+  auto buffer_handle = status.take();
+
+  auto producer_channel_status = ProducerChannel::Create(
+      service(), buffer_id, width, height, format, producer_usage,
+      consumer_usage, meta_size_bytes_, slice_count);
+  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..09b0243
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.h
@@ -0,0 +1,93 @@
+#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 format,
+                                 uint64_t producer_usage,
+                                 uint64_t consumer_usage, size_t slice_count,
+                                 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 format,
+      uint64_t producer_usage, uint64_t consumer_usage, size_t slice_count);
+
+  // 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..504b26f
--- /dev/null
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -0,0 +1,865 @@
+/*
+ * 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 = DisplayClient::Create(&error);
+  SystemDisplayMetrics metrics;
+
+  if (error) {
+    ALOGE("Could not connect to display service : %s(%d)", strerror(error),
+          error);
+    return;
+  }
+
+  error = display_client->GetDisplayMetrics(&metrics);
+  if (error) {
+    ALOGE("Could not get display metrics from display service : %s(%d)",
+          strerror(error), error);
+    return;
+  }
+
+  *width = metrics.display_native_width;
+  *height = metrics.display_native_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..c15f8fd
--- /dev/null
+++ b/services/vr/hardware_composer/vr_composer.cpp
@@ -0,0 +1,46 @@
+#include "vr_composer.h"
+
+namespace android {
+namespace dvr {
+
+VrComposer::VrComposer() {}
+
+VrComposer::~VrComposer() {}
+
+binder::Status VrComposer::registerObserver(
+    const sp<IVrComposerCallback>& callback) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  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..6283f37
--- /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
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/sensord/Android.mk b/services/vr/sensord/Android.mk
new file mode 100644
index 0000000..638c9a8
--- /dev/null
+++ b/services/vr/sensord/Android.mk
@@ -0,0 +1,76 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+SENSORD_EXTEND ?= libsensordextensionstub
+
+sourceFiles := \
+	pose_service.cpp \
+	sensord.cpp \
+	sensor_fusion.cpp \
+	sensor_hal_thread.cpp \
+	sensor_ndk_thread.cpp \
+	sensor_service.cpp \
+	sensor_thread.cpp \
+
+includeFiles += \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	libdvrcommon \
+	libvrsensor \
+	libperformance \
+	libbufferhub \
+	libpdx_default_transport \
+	libposepredictor \
+
+sharedLibraries := \
+	libandroid \
+	libbase \
+	libbinder \
+	libcutils \
+	liblog \
+	libhardware \
+	libutils \
+        libui \
+	$(SENSORD_EXTEND) \
+
+cFlags := -DLOG_TAG=\"sensord\" \
+          -DTRACE=0
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_CFLAGS := $(cFlags)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := sensord
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_C_INCLUDES += \
+    $(call local-generated-sources-dir)/proto/frameworks/native/services/vr/sensord
+LOCAL_INIT_RC := sensord.rc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_SRC_FILES := test/poselatencytest.cpp
+LOCAL_MODULE := poselatencytest
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libsensordextensionstub
+LOCAL_SRC_FILES := sensord_extension.cpp
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/vr/sensord/pose_service.cpp b/services/vr/sensord/pose_service.cpp
new file mode 100644
index 0000000..75423bb
--- /dev/null
+++ b/services/vr/sensord/pose_service.cpp
@@ -0,0 +1,649 @@
+#define ATRACE_TAG ATRACE_TAG_INPUT
+#include "pose_service.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <time.h>
+
+#include <array>
+#include <cmath>
+#include <cstdint>
+#include <sstream>
+#include <type_traits>
+
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <hardware/sensors.h>
+#include <log/log.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/benchmark.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/platform_defines.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/sensor_constants.h>
+#include <utils/Trace.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+using Vector3d = vec3d;
+using Rotationd = quatd;
+using AngleAxisd = Eigen::AngleAxis<double>;
+
+namespace {
+// Wait a few seconds before checking if we need to disable sensors.
+static constexpr int64_t kSensorTimeoutNs = 5000000000ll;
+
+static constexpr float kTwoPi = 2.0 * M_PI;
+static constexpr float kDegToRad = M_PI / 180.f;
+
+// Head model code data.
+static constexpr float kDefaultNeckHorizontalOffset = 0.080f;  // meters
+static constexpr float kDefaultNeckVerticalOffset = 0.075f;    // meters
+
+static constexpr char kDisablePosePredictionProp[] =
+    "persist.dvr.disable_predict";
+
+// Device type property for controlling classes of behavior that differ
+// between devices. If unset, defaults to kOrientationTypeSmartphone.
+static constexpr char kOrientationTypeProp[] = "ro.dvr.orientation_type";
+static constexpr char kEnableSensorRecordProp[] = "dvr.enable_6dof_recording";
+static constexpr char kEnableSensorPlayProp[] = "dvr.enable_6dof_playback";
+static constexpr char kEnableSensorPlayIdProp[] = "dvr.6dof_playback_id";
+static constexpr char kEnablePoseRecordProp[] = "dvr.enable_pose_recording";
+static constexpr char kPredictorTypeProp[] = "dvr.predictor_type";
+
+// Persistent buffer names.
+static constexpr char kPoseRingBufferName[] = "PoseService:RingBuffer";
+
+static constexpr int kDatasetIdLength = 36;
+static constexpr char kDatasetIdChars[] = "0123456789abcdef-";
+
+static constexpr int kLatencyWindowSize = 200;
+
+// These are the flags used by BufferProducer::CreatePersistentUncachedBlob,
+// plus PRIVATE_ADSP_HEAP to allow access from the DSP.
+static constexpr int kPoseRingBufferFlags =
+    GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY |
+    GRALLOC_USAGE_PRIVATE_UNCACHED | GRALLOC_USAGE_PRIVATE_ADSP_HEAP;
+
+std::string GetPoseModeString(DvrPoseMode mode) {
+  switch (mode) {
+    case DVR_POSE_MODE_6DOF:
+      return "DVR_POSE_MODE_6DOF";
+    case DVR_POSE_MODE_3DOF:
+      return "DVR_POSE_MODE_3DOF";
+    case DVR_POSE_MODE_MOCK_FROZEN:
+      return "DVR_POSE_MODE_MOCK_FROZEN";
+    case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+      return "DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW";
+    case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+      return "DVR_POSE_MODE_MOCK_HEAD_TURN_FAST";
+    case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+      return "DVR_POSE_MODE_MOCK_ROTATE_SLOW";
+    case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+      return "DVR_POSE_MODE_MOCK_ROTATE_MEDIUM";
+    case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+      return "DVR_POSE_MODE_MOCK_ROTATE_FAST";
+    case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE:
+      return "DVR_POSE_MODE_MOCK_CIRCLE_STRAFE";
+    default:
+      return "Unknown pose mode";
+  }
+}
+
+}  // namespace
+
+PoseService::PoseService(SensorThread* sensor_thread)
+    : BASE("PoseService", Endpoint::Create(DVR_POSE_SERVICE_CLIENT)),
+      sensor_thread_(sensor_thread),
+      last_sensor_usage_time_ns_(0),
+      watchdog_shutdown_(false),
+      sensors_on_(false),
+      accelerometer_index_(-1),
+      gyroscope_index_(-1),
+      pose_mode_(DVR_POSE_MODE_6DOF),
+      mapped_pose_buffer_(nullptr),
+      vsync_count_(0),
+      photon_timestamp_(0),
+      // Will be updated by external service, but start with a non-zero value:
+      display_period_ns_(16000000),
+      sensor_latency_(kLatencyWindowSize) {
+  last_known_pose_ = {
+      .orientation = {1.0f, 0.0f, 0.0f, 0.0f},
+      .translation = {0.0f, 0.0f, 0.0f, 0.0f},
+      .angular_velocity = {0.0f, 0.0f, 0.0f, 0.0f},
+      .velocity = {0.0f, 0.0f, 0.0f, 0.0f},
+      .timestamp_ns = 0,
+      .flags = DVR_POSE_FLAG_HEAD,
+      .pad = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+  };
+
+  switch (property_get_int32(kOrientationTypeProp, kOrientationTypePortrait)) {
+    case kOrientationTypeLandscape:
+      device_orientation_type_ = kOrientationTypeLandscape;
+      break;
+    default:
+      device_orientation_type_ = kOrientationTypePortrait;
+      break;
+  }
+
+  ring_buffer_ =
+      BufferProducer::Create(kPoseRingBufferName, 0, 0, kPoseRingBufferFlags,
+                             sizeof(DvrPoseRingBuffer));
+  if (!ring_buffer_) {
+    ALOGE("PoseService::PoseService: Failed to create/get pose ring buffer!");
+    return;
+  }
+
+  void* addr = nullptr;
+  int ret =
+      ring_buffer_->GetBlobReadWritePointer(sizeof(DvrPoseRingBuffer), &addr);
+  if (ret < 0) {
+    ALOGE("PoseService::PoseService: Failed to map pose ring buffer: %s",
+          strerror(-ret));
+    return;
+  }
+  memset(addr, 0, sizeof(DvrPoseRingBuffer));
+  mapped_pose_buffer_ = static_cast<DvrPoseRingBuffer*>(addr);
+  addr = nullptr;
+
+  for (int i = 0; i < sensor_thread->GetSensorCount(); ++i) {
+    if (sensor_thread->GetSensorType(i) == SENSOR_TYPE_ACCELEROMETER)
+      accelerometer_index_ = i;
+    if (sensor_thread->GetSensorType(i) == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED)
+      gyroscope_index_ = i;
+  }
+  // If we failed to find the uncalibrated gyroscope, use the regular one.
+  if (gyroscope_index_ < 0) {
+    ALOGW("PoseService was unable to find uncalibrated gyroscope");
+    for (int i = 0; i < sensor_thread->GetSensorCount(); ++i) {
+      ALOGI("Type %d", sensor_thread->GetSensorType(i));
+      if (sensor_thread->GetSensorType(i) == SENSOR_TYPE_GYROSCOPE)
+        gyroscope_index_ = i;
+    }
+  }
+
+  if (accelerometer_index_ < 0) {
+    ALOGE("PoseService was unable to find accelerometer");
+  }
+  if (gyroscope_index_ < 0) {
+    ALOGE("PoseService was unable to find gyroscope");
+  }
+
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    KickSensorWatchDogThread();
+  }
+
+  // Read the persistent dvr flags before using them in SetPoseMode.
+  enable_pose_prediction_ =
+      property_get_bool(kDisablePosePredictionProp, 0) == 0;
+
+  enable_sensor_recording_ = property_get_bool(kEnableSensorRecordProp, 0) == 1;
+
+  enable_sensor_playback_ = property_get_bool(kEnableSensorPlayProp, 0) == 1;
+
+  if (enable_sensor_playback_) {
+    char dataset_id[PROPERTY_VALUE_MAX];
+    property_get(kEnableSensorPlayIdProp, dataset_id, "");
+    sensor_playback_id_ = std::string(dataset_id);
+
+    if (sensor_playback_id_.length() != kDatasetIdLength ||
+        sensor_playback_id_.find_first_not_of(kDatasetIdChars) !=
+            std::string::npos) {
+      ALOGE("Error: invalid playback id %s", sensor_playback_id_.c_str());
+      sensor_playback_id_ = "";
+      enable_sensor_playback_ = false;
+    } else {
+      ALOGI("Playback id %s", sensor_playback_id_.c_str());
+    }
+  }
+
+  switch (property_get_int32(kPredictorTypeProp, 0)) {
+    case 1:
+      pose_predictor_ = posepredictor::Predictor::Create(
+          posepredictor::PredictorType::Quadric);
+    default:
+      pose_predictor_ = posepredictor::Predictor::Create(
+          posepredictor::PredictorType::Linear);
+  }
+
+  enable_pose_recording_ = property_get_bool(kEnablePoseRecordProp, 0) == 1;
+
+  SetPoseMode(DVR_POSE_MODE_6DOF);
+}
+
+PoseService::~PoseService() {
+  if (watchdog_thread_.get_id() != std::thread::id()) {
+    {
+      std::lock_guard<std::mutex> guard(mutex_);
+      watchdog_shutdown_ = true;
+      watchdog_condition_.notify_one();
+    }
+    watchdog_thread_.join();
+  }
+}
+
+void PoseService::KickSensorWatchDogThread() {
+  // This method is called every frame while rendering so we want to make sure
+  // it is very light weight with synchronization.
+  // TODO(jbates) For better performance, we can consider a lock-free atomic
+  // solution instead of locking this mutex.
+
+  // Update the usage time. The watchdog thread will poll this value to know
+  // when to disable sensors.
+  last_sensor_usage_time_ns_ = GetSystemClockNs();
+
+  // If sensors are still on, there's nothing else to do.
+  if (sensors_on_)
+    return;
+
+  // Enable sensors.
+  ALOGI("Start using sensors.");
+  sensors_on_ = true;
+  if (accelerometer_index_ >= 0) {
+    sensor_thread_->StartUsingSensor(accelerometer_index_);
+  }
+  if (gyroscope_index_ >= 0) {
+    sensor_thread_->StartUsingSensor(gyroscope_index_);
+  }
+
+  // Tell the thread to wake up to disable the sensors when no longer needed.
+  watchdog_condition_.notify_one();
+
+  if (watchdog_thread_.get_id() == std::thread::id()) {
+    // The sensor watchdog thread runs while sensors are in use. When no APIs
+    // have requested sensors beyond a threshold (5 seconds), sensors are
+    // disabled.
+    watchdog_thread_ = std::thread([this] {
+      std::unique_lock<std::mutex> lock(mutex_);
+      while (!watchdog_shutdown_) {
+        int64_t remaining_sensor_time_ns =
+            last_sensor_usage_time_ns_ + kSensorTimeoutNs - GetSystemClockNs();
+
+        if (remaining_sensor_time_ns > 0) {
+          // Wait for the remaining usage time before checking again.
+          watchdog_condition_.wait_for(
+              lock, std::chrono::nanoseconds(remaining_sensor_time_ns));
+          continue;
+        }
+
+        if (sensors_on_) {
+          // Disable sensors.
+          ALOGI("Stop using sensors.");
+          sensors_on_ = false;
+          if (accelerometer_index_ >= 0) {
+            sensor_thread_->StopUsingSensor(accelerometer_index_);
+          }
+          if (gyroscope_index_ >= 0) {
+            sensor_thread_->StopUsingSensor(gyroscope_index_);
+          }
+        }
+
+        // Wait for sensors to be enabled again.
+        watchdog_condition_.wait(lock);
+      }
+    });
+  }
+}
+
+bool PoseService::IsInitialized() const {
+  return BASE::IsInitialized() && ring_buffer_ && mapped_pose_buffer_;
+}
+
+void PoseService::WriteAsyncPoses(const Vector3d& start_t_head,
+                                  const Rotationd& start_q_head,
+                                  int64_t pose_timestamp) {
+  if (enable_external_pose_) {
+    return;
+  }
+
+  // If playing back data, the timestamps are different enough from the
+  // current time that prediction doesn't work. This hack pretends that
+  // there was one nanosecond of latency between the sensors and here.
+  if (enable_sensor_playback_)
+    pose_timestamp = GetSystemClockNs() - 1;
+
+  // Feed the sample to the predictor
+  AddPredictorPose(pose_predictor_.get(), start_t_head, start_q_head,
+                   pose_timestamp, &last_known_pose_);
+
+  // Store one extra value, because the application is working on the next
+  // frame and expects the minimum count from that frame on.
+  for (uint32_t i = 0; i < kPoseAsyncBufferMinFutureCount + 1; ++i) {
+    int64_t target_time = photon_timestamp_ + i * display_period_ns_;
+
+    // TODO(jbates, cwolfe) For the DSP code, we may still want poses even when
+    // the vsyncs are not ticking up. But it's important not to update the pose
+    // data that's in the past so that applications have the most accurate
+    // estimate of the last frame's *actual* pose, so that they can update
+    // simulations and calculate collisions, etc.
+    if (target_time < pose_timestamp) {
+      // Already in the past, do not update this head pose slot.
+      continue;
+    }
+
+    // Write to the actual shared memory ring buffer.
+    uint32_t index = ((vsync_count_ + i) & kPoseAsyncBufferIndexMask);
+
+    // Make a pose prediction
+    if (enable_pose_prediction_) {
+      PredictPose(pose_predictor_.get(), target_time,
+                  target_time + right_eye_photon_offset_ns_,
+                  mapped_pose_buffer_->ring + index);
+    } else {
+      mapped_pose_buffer_->ring[index] = last_known_pose_;
+    }
+  }
+}
+
+void PoseService::UpdatePoseMode() {
+  ALOGI_IF(TRACE, "UpdatePoseMode: %f %f %f", last_known_pose_.translation[0],
+           last_known_pose_.translation[1], last_known_pose_.translation[2]);
+
+  const int64_t current_time_ns = GetSystemClockNs();
+
+  const PoseState pose_state = sensor_fusion_.GetLatestPoseState();
+
+  switch (pose_mode_) {
+    case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+    case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+    case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+    case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+    case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+    case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE: {
+      // Calculate a pose based on monotic system time.
+      const Vector3d y_axis(0., 1., 0.);
+      double time_s = current_time_ns / 1e9;
+
+      // Generate fake yaw data.
+      float yaw = 0.0f;
+      Vector3d head_trans(0.0, 0.0, 0.0);
+      switch (pose_mode_) {
+        default:
+        case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+          // Pan across 120 degrees in 15 seconds.
+          yaw = std::cos(kTwoPi * time_s / 15.0) * 60.0 * kDegToRad;
+          break;
+        case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+          // Pan across 120 degrees in 4 seconds.
+          yaw = std::cos(kTwoPi * time_s / 4.0) * 60.0 * kDegToRad;
+          break;
+        case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+          // Rotate 5 degrees per second.
+          yaw = std::fmod(time_s * 5.0 * kDegToRad, kTwoPi);
+          break;
+        case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+          // Rotate 30 degrees per second.
+          yaw = std::fmod(time_s * 30.0 * kDegToRad, kTwoPi);
+          break;
+        case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+          // Rotate 90 degrees per second.
+          yaw = std::fmod(time_s * 90.0 * kDegToRad, kTwoPi);
+          break;
+        case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE:
+          // Circle strafe around origin at distance of 3 meters.
+          yaw = std::fmod(time_s * 30.0 * kDegToRad, kTwoPi);
+          head_trans += 3.0 * Vector3d(sin(yaw), 0.0, cos(yaw));
+          break;
+      }
+
+      // Calculate the simulated head rotation in an absolute "head" space.
+      // This space is not related to start space and doesn't need a
+      // reference.
+      Rotationd head_rotation_in_head_space(AngleAxisd(yaw, y_axis));
+
+      WriteAsyncPoses(head_trans, head_rotation_in_head_space, current_time_ns);
+      break;
+    }
+    case DVR_POSE_MODE_MOCK_FROZEN: {
+      // Even when frozen, we still provide a current timestamp, because
+      // consumers may rely on it being monotonic.
+
+      Rotationd start_from_head_rotation(
+          frozen_state_.head_from_start_rotation.w,
+          frozen_state_.head_from_start_rotation.x,
+          frozen_state_.head_from_start_rotation.y,
+          frozen_state_.head_from_start_rotation.z);
+      Vector3d head_from_start_translation(
+          frozen_state_.head_from_start_translation.x,
+          frozen_state_.head_from_start_translation.y,
+          frozen_state_.head_from_start_translation.z);
+
+      WriteAsyncPoses(head_from_start_translation, start_from_head_rotation,
+                      current_time_ns);
+      break;
+    }
+    case DVR_POSE_MODE_3DOF: {
+      // Sensor fusion provides IMU-space data, transform to world space.
+
+      // Constants to perform IMU orientation adjustments. Note that these
+      // calculations will be optimized out in a release build.
+      constexpr double k90DegInRad = 90.0 * M_PI / 180.0;
+      const Vector3d kVecAxisX(1.0, 0.0, 0.0);
+      const Vector3d kVecAxisY(0.0, 1.0, 0.0);
+      const Vector3d kVecAxisZ(0.0, 0.0, 1.0);
+      const Rotationd kRotX90(AngleAxisd(k90DegInRad, kVecAxisX));
+
+      Rotationd start_from_head_rotation;
+      if (device_orientation_type_ == kOrientationTypeLandscape) {
+        const Rotationd kPostRotation =
+            kRotX90 * Rotationd(AngleAxisd(-k90DegInRad, kVecAxisY));
+        start_from_head_rotation =
+            (pose_state.sensor_from_start_rotation * kPostRotation).inverse();
+      } else if (device_orientation_type_ == kOrientationTypeLandscape180) {
+        const Rotationd kPreRotation =
+            Rotationd(AngleAxisd(k90DegInRad * 2.0, kVecAxisY)) *
+            Rotationd(AngleAxisd(k90DegInRad * 2.0, kVecAxisZ));
+        const Rotationd kPostRotation = kRotX90;
+        start_from_head_rotation =
+            (kPreRotation *
+             pose_state.sensor_from_start_rotation * kPostRotation)
+                .inverse();
+      } else {
+        const Rotationd kPreRotation =
+            Rotationd(AngleAxisd(k90DegInRad, kVecAxisZ));
+        const Rotationd kPostRotation = kRotX90;
+        start_from_head_rotation =
+            (kPreRotation * pose_state.sensor_from_start_rotation *
+             kPostRotation)
+                .inverse();
+      }
+      start_from_head_rotation.normalize();
+
+      // Neck / head model code procedure for when no 6dof is available.
+      // To apply the neck model, first translate the head pose to the new
+      // center of eyes, then rotate around the origin (the original head
+      // pos).
+      Vector3d position =
+          start_from_head_rotation * Vector3d(0.0, kDefaultNeckVerticalOffset,
+                                              -kDefaultNeckHorizontalOffset);
+
+      // Update the current latency model.
+      if (pose_state.timestamp_ns != 0) {
+        sensor_latency_.AddLatency(GetSystemClockNs() -
+                                   pose_state.timestamp_ns);
+      }
+
+      // Update the timestamp with the expected latency.
+      WriteAsyncPoses(
+          position, start_from_head_rotation,
+          pose_state.timestamp_ns + sensor_latency_.CurrentLatencyEstimate());
+      break;
+    }
+    default:
+    case DVR_POSE_MODE_6DOF:
+      ALOGE("ERROR: invalid pose mode");
+      break;
+  }
+}
+
+pdx::Status<void> PoseService::HandleMessage(pdx::Message& msg) {
+  pdx::Status<void> ret;
+  const pdx::MessageInfo& info = msg.GetInfo();
+  switch (info.op) {
+    case DVR_POSE_NOTIFY_VSYNC: {
+      std::lock_guard<std::mutex> guard(mutex_);
+
+      // Kick the sensor thread, because we are still rendering.
+      KickSensorWatchDogThread();
+
+      const struct iovec data[] = {
+          {.iov_base = &vsync_count_, .iov_len = sizeof(vsync_count_)},
+          {.iov_base = &photon_timestamp_,
+           .iov_len = sizeof(photon_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_)},
+      };
+      ret = msg.ReadVectorAll(data);
+      if (ret && !enable_external_pose_) {
+        mapped_pose_buffer_->vsync_count = vsync_count_;
+      }
+
+      // TODO(jbates, eieio): make this async, no need to reply.
+      REPLY_MESSAGE(msg, ret, error);
+    }
+    case DVR_POSE_POLL: {
+      ATRACE_NAME("pose_poll");
+      std::lock_guard<std::mutex> guard(mutex_);
+
+      DvrPoseState client_state;
+      client_state = {
+          .head_from_start_rotation = {last_known_pose_.orientation[0],
+                                       last_known_pose_.orientation[1],
+                                       last_known_pose_.orientation[2],
+                                       last_known_pose_.orientation[3]},
+          .head_from_start_translation = {last_known_pose_.translation[0],
+                                          last_known_pose_.translation[1],
+                                          last_known_pose_.translation[2]},
+          .timestamp_ns = static_cast<uint64_t>(last_known_pose_.timestamp_ns),
+          .sensor_from_start_rotation_velocity = {
+              last_known_pose_.angular_velocity[0],
+              last_known_pose_.angular_velocity[1],
+              last_known_pose_.angular_velocity[2]}};
+
+      Btrace("Sensor data received",
+             static_cast<int64_t>(client_state.timestamp_ns));
+
+      Btrace("Pose polled");
+
+      ret = msg.WriteAll(&client_state, sizeof(client_state));
+      REPLY_MESSAGE(msg, ret, error);
+    }
+    case DVR_POSE_FREEZE: {
+      {
+        std::lock_guard<std::mutex> guard(mutex_);
+
+        DvrPoseState frozen_state;
+        ret = msg.ReadAll(&frozen_state, sizeof(frozen_state));
+        if (!ret) {
+          REPLY_ERROR(msg, ret.error(), error);
+        }
+        frozen_state_ = frozen_state;
+      }
+      SetPoseMode(DVR_POSE_MODE_MOCK_FROZEN);
+      REPLY_MESSAGE(msg, ret, error);
+    }
+    case DVR_POSE_SET_MODE: {
+      int mode;
+      {
+        std::lock_guard<std::mutex> guard(mutex_);
+        ret = msg.ReadAll(&mode, sizeof(mode));
+        if (!ret) {
+          REPLY_ERROR(msg, ret.error(), error);
+        }
+        if (mode < 0 || mode >= DVR_POSE_MODE_COUNT) {
+          REPLY_ERROR(msg, EINVAL, error);
+        }
+      }
+      SetPoseMode(DvrPoseMode(mode));
+      REPLY_MESSAGE(msg, ret, error);
+    }
+    case DVR_POSE_GET_MODE: {
+      std::lock_guard<std::mutex> guard(mutex_);
+      int mode = pose_mode_;
+      ret = msg.WriteAll(&mode, sizeof(mode));
+      REPLY_MESSAGE(msg, ret, error);
+    }
+    case DVR_POSE_GET_RING_BUFFER: {
+      std::lock_guard<std::mutex> guard(mutex_);
+
+      // Kick the sensor thread, because we have a new consumer.
+      KickSensorWatchDogThread();
+
+      Status<LocalChannelHandle> consumer_channel =
+          ring_buffer_->CreateConsumer();
+      REPLY_MESSAGE(msg, consumer_channel, error);
+    }
+    case DVR_POSE_GET_CONTROLLER_RING_BUFFER: {
+      std::lock_guard<std::mutex> guard(mutex_);
+      REPLY_ERROR(msg, EINVAL, error);
+    }
+    case DVR_POSE_LOG_CONTROLLER: {
+      std::lock_guard<std::mutex> guard(mutex_);
+      REPLY_ERROR(msg, EINVAL, error);
+    }
+    default:
+      // Do not lock mutex_ here, because this may call the on*() handlers,
+      // which will lock the mutex themselves.
+      ret = Service::HandleMessage(msg);
+      break;
+  }
+error:
+  return ret;
+}
+
+std::string PoseService::DumpState(size_t /*max_length*/) {
+  DvrPoseMode pose_mode;
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+    pose_mode = pose_mode_;
+  }
+
+  std::ostringstream stream;
+  stream << "Pose mode: " << GetPoseModeString(pose_mode);
+  return stream.str();
+}
+
+void PoseService::HandleEvents(const sensors_event_t* begin_events,
+                               const sensors_event_t* end_events) {
+  ATRACE_NAME("PoseService::HandleEvents");
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  for (const sensors_event_t* event = begin_events; event != end_events;
+       ++event) {
+    if (event->type == SENSOR_TYPE_ACCELEROMETER) {
+      sensor_fusion_.ProcessAccelerometerSample(
+          event->acceleration.x, event->acceleration.y, event->acceleration.z,
+          event->timestamp);
+    } else if (event->type == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) {
+      sensor_fusion_.ProcessGyroscopeSample(event->gyro.x, event->gyro.y,
+                                            event->gyro.z, event->timestamp);
+    }
+  }
+
+  UpdatePoseMode();
+}
+
+void PoseService::SetPoseMode(DvrPoseMode mode) {
+  if (mode == DVR_POSE_MODE_6DOF) {
+    // Only 3DoF is currently supported.
+    mode = DVR_POSE_MODE_3DOF;
+  }
+
+  pose_mode_ = mode;
+
+  sensor_thread_->SetPaused(false);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/pose_service.h b/services/vr/sensord/pose_service.h
new file mode 100644
index 0000000..7b7adec
--- /dev/null
+++ b/services/vr/sensord/pose_service.h
@@ -0,0 +1,149 @@
+#ifndef ANDROID_DVR_SENSORD_POSE_SERVICE_H_
+#define ANDROID_DVR_SENSORD_POSE_SERVICE_H_
+
+#include <condition_variable>
+#include <forward_list>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <dvr/pose_client.h>
+#include <pdx/service.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/dvr_pose_predictor.h>
+#include <private/dvr/latency_model.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/ring_buffer.h>
+
+#include "sensor_fusion.h"
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// PoseService implements the HMD pose service over ServiceFS.
+class PoseService : public pdx::ServiceBase<PoseService> {
+ public:
+  ~PoseService() override;
+
+  bool IsInitialized() const override;
+  pdx::Status<void> HandleMessage(pdx::Message& msg) override;
+  std::string DumpState(size_t max_length) override;
+
+  // Handle events from the sensor HAL.
+  // Safe to call concurrently with any other public member functions.
+  void HandleEvents(const sensors_event_t* begin_events,
+                    const sensors_event_t* end_events);
+
+ private:
+  friend BASE;
+
+  enum OrientationType {
+    // Typical smartphone device (default).
+    kOrientationTypePortrait = 1,
+    // Landscape device.
+    kOrientationTypeLandscape = 2,
+    // 180 Landscape device.
+    kOrientationTypeLandscape180 = 3,
+  };
+
+  // Initializes the service. Keeps a reference to sensor_thread, which must be
+  // non-null.
+  explicit PoseService(SensorThread* sensor_thread);
+
+  // Kick the sensor watch dog thread which will robustly disable IMU usage
+  // when there are no sensor data consumers.
+  // The class mutex (mutex_) must be locked while calling this method.
+  void KickSensorWatchDogThread();
+
+  void UpdatePoseMode();
+
+  // Update the async pose ring buffer with new pose data.
+  // |start_t_head| Head position in start space.
+  // |start_q_head| Head orientation quaternion in start space.
+  // |pose_timestamp| System timestamp of pose data in seconds.
+  // |pose_delta_time| Elapsed time in seconds between this pose and the last.
+  void WriteAsyncPoses(const Eigen::Vector3<double>& start_t_head,
+                       const Eigen::Quaternion<double>& start_q_head,
+                       int64_t pose_timestamp);
+
+  // Set the pose mode.
+  void SetPoseMode(DvrPoseMode mode);
+
+  // The abstraction around the sensor data.
+  SensorThread* sensor_thread_;
+
+  // Protects access to all member variables.
+  std::mutex mutex_;
+
+  // Watchdog thread data. The watchdog thread will ensure that sensor access
+  // is disabled when nothing has been consuming it for a while.
+  int64_t last_sensor_usage_time_ns_;
+  std::thread watchdog_thread_;
+  std::condition_variable watchdog_condition_;
+  bool watchdog_shutdown_;
+  bool sensors_on_;
+
+  // Indices for the accelerometer and gyroscope sensors, or -1 if the sensor
+  // wasn't present on construction.
+  int accelerometer_index_;
+  int gyroscope_index_;
+
+  // The sensor fusion algorithm and its state.
+  SensorFusion sensor_fusion_;
+
+  // Current pose mode.
+  DvrPoseMode pose_mode_;
+
+  // State which is sent if pose_mode_ is DVR_POSE_MODE_MOCK_FROZEN.
+  DvrPoseState frozen_state_;
+
+  // Last known pose.
+  DvrPoseAsync last_known_pose_;
+
+  // If this flag is true, the pose published includes a small prediction of
+  // where it'll be when it's consumed.
+  bool enable_pose_prediction_;
+
+  // Flag to turn on recording of raw sensor data
+  bool enable_sensor_recording_;
+
+  // Flag to log pose to a file
+  bool enable_pose_recording_;
+
+  // Flag to turn on playback from a saved dataset instead of using live data.
+  bool enable_sensor_playback_;
+
+  std::string sensor_playback_id_;
+
+  // External pose generation.
+  bool enable_external_pose_ = false;
+
+  // The predictor to extrapolate pose samples.
+  std::unique_ptr<posepredictor::Predictor> pose_predictor_;
+
+  // Pose ring buffer.
+  std::shared_ptr<BufferProducer> ring_buffer_;
+  // Temporary mapped ring buffer.
+  DvrPoseRingBuffer* mapped_pose_buffer_;
+  // Current vsync info, updated by displayd.
+  uint32_t vsync_count_;
+  int64_t photon_timestamp_;
+  int64_t display_period_ns_;
+  int64_t right_eye_photon_offset_ns_ = 0;
+
+  // To model the measurement - arrival latency.
+  LatencyModel sensor_latency_;
+
+  // Type for controlling pose orientation calculation.
+  OrientationType device_orientation_type_;
+
+  PoseService(const PoseService&) = delete;
+  void operator=(const PoseService&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_POSE_SERVICE_H_
diff --git a/services/vr/sensord/sensor_fusion.cpp b/services/vr/sensord/sensor_fusion.cpp
new file mode 100644
index 0000000..5663ae4
--- /dev/null
+++ b/services/vr/sensord/sensor_fusion.cpp
@@ -0,0 +1,348 @@
+#include "sensor_fusion.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// --- start of added bits for porting to eigen
+
+// In general, we prefer to add wrappers for things like Inverse() to minimize
+// the changes to the imported code, so that merging in upstream changes becomes
+// simpler.
+
+inline Matrix3d Inverse(const Matrix3d& matrix) { return matrix.inverse(); }
+inline Matrix3d Transpose(const Matrix3d& matrix) { return matrix.transpose(); }
+inline Matrix3d RotationMatrixNH(const Rotationd& rotation) {
+  return rotation.toRotationMatrix();
+}
+inline double Length(const Vector3d& vector) { return vector.norm(); }
+
+using uint64 = uint64_t;
+
+// --- end of added bits for porting to eigen
+
+static const double kFiniteDifferencingEpsilon = 1e-7;
+static const double kEpsilon = 1e-15;
+// Default gyroscope frequency. This corresponds to 200 Hz.
+static const double kDefaultGyroscopeTimestep_s = 0.005f;
+// Maximum time between gyroscope before we start limiting the integration.
+static const double kMaximumGyroscopeSampleDelay_s = 0.04f;
+// Compute a first-order exponential moving average of changes in accel norm per
+// frame.
+static const double kSmoothingFactor = 0.5;
+// Minimum and maximum values used for accelerometer noise covariance matrix.
+// The smaller the sigma value, the more weight is given to the accelerometer
+// signal.
+static const double kMinAccelNoiseSigma = 0.75;
+static const double kMaxAccelNoiseSigma = 7.0;
+// Initial value for the diagonal elements of the different covariance matrices.
+static const double kInitialStateCovarianceValue = 25.0;
+static const double kInitialProcessCovarianceValue = 1.0;
+// Maximum accelerometer norm change allowed before capping it covariance to a
+// large value.
+static const double kMaxAccelNormChange = 0.15;
+// Timestep IIR filtering coefficient.
+static const double kTimestepFilterCoeff = 0.95;
+// Minimum number of sample for timestep filtering.
+static const uint32_t kTimestepFilterMinSamples = 10;
+
+// Z direction in start space.
+static const Vector3d kCanonicalZDirection(0.0, 0.0, 1.0);
+
+// Computes a axis angle rotation from the input vector.
+// angle = norm(a)
+// axis = a.normalized()
+// If norm(a) == 0, it returns an identity rotation.
+static Rotationd RotationFromVector(const Vector3d& a) {
+  const double norm_a = Length(a);
+  if (norm_a < kEpsilon) {
+    return Rotationd::Identity();
+  }
+  return Rotationd(AngleAxisd(norm_a, a / norm_a));
+}
+
+// --- start of functions ported from pose_prediction.cc
+
+namespace pose_prediction {
+
+// Returns a rotation matrix based on the integration of the gyroscope_value
+// over the timestep_s in seconds.
+// TODO(pfg): Document the space better here.
+//
+// @param gyroscope_value gyroscope sensor values.
+// @param timestep_s integration period in seconds.
+// @return Integration of the gyroscope value the rotation is from Start to
+//         Sensor Space.
+Rotationd GetRotationFromGyroscope(const Vector3d& gyroscope_value,
+                                   double timestep_s) {
+  const double velocity = Length(gyroscope_value);
+
+  // When there is no rotation data return an identity rotation.
+  if (velocity < kEpsilon) {
+    return Rotationd::Identity();
+  }
+  // Since the gyroscope_value is a start from sensor transformation we need to
+  // invert it to have a sensor from start transformation, hence the minus sign.
+  // For more info:
+  // http://developer.android.com/guide/topics/sensors/sensors_motion.html#sensors-motion-gyro
+  return Rotationd(AngleAxisd(-timestep_s * velocity,
+                              gyroscope_value / velocity));
+}
+
+}  // namespace pose_prediction
+
+// --- end of functions ported from pose_prediction.cc
+
+}  // namespace
+
+SensorFusion::SensorFusion()
+    : execute_reset_with_next_accelerometer_sample_(false) {
+  ResetState();
+}
+
+void SensorFusion::Reset() {
+  execute_reset_with_next_accelerometer_sample_ = true;
+}
+
+void SensorFusion::ResetState() {
+  current_state_.timestamp_ns = 0;
+  current_state_.sensor_from_start_rotation = Rotationd::Identity();
+  current_state_.sensor_from_start_rotation_velocity = Vector3d::Zero();
+
+  current_accelerometer_timestamp_ns_ = 0;
+
+  state_covariance_ = Matrix3d::Identity() * kInitialStateCovarianceValue;
+  process_covariance_ = Matrix3d::Identity() * kInitialProcessCovarianceValue;
+  accelerometer_measurement_covariance_ =
+      Matrix3d::Identity() * kMinAccelNoiseSigma * kMinAccelNoiseSigma;
+  innovation_covariance_.setIdentity();
+
+  accelerometer_measurement_jacobian_ = Matrix3d::Zero();
+  kalman_gain_ = Matrix3d::Zero();
+  innovation_ = Vector3d::Zero();
+  accelerometer_measurement_ = Vector3d::Zero();
+  prediction_ = Vector3d::Zero();
+  control_input_ = Vector3d::Zero();
+  state_update_ = Vector3d::Zero();
+
+  moving_average_accelerometer_norm_change_ = 0.0;
+
+  is_timestep_filter_initialized_ = false;
+  is_gyroscope_filter_valid_ = false;
+  is_aligned_with_gravity_ = false;
+}
+
+// Here I am doing something wrong relative to time stamps. The state timestamps
+// always correspond to the gyrostamps because it would require additional
+// extrapolation if I wanted to do otherwise.
+// TODO(pfg): investigate about published an updated pose after accelerometer
+// data was used for filtering.
+PoseState SensorFusion::GetLatestPoseState() const {
+  std::unique_lock<std::mutex> lock(mutex_);
+  return current_state_;
+}
+
+void SensorFusion::ProcessGyroscopeSample(float v_x, float v_y, float v_z,
+                                          uint64 timestamp_ns) {
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  // Don't accept gyroscope sample when waiting for a reset.
+  if (execute_reset_with_next_accelerometer_sample_) {
+    return;
+  }
+
+  // Discard outdated samples.
+  if (current_state_.timestamp_ns >= timestamp_ns) {
+    // TODO(pfg): Investigate why this happens.
+    return;
+  }
+
+  // Checks that we received at least one gyroscope sample in the past.
+  if (current_state_.timestamp_ns != 0) {
+    // TODO(pfg): roll this in filter gyroscope timestep function.
+    double current_timestep_s =
+        static_cast<double>(timestamp_ns - current_state_.timestamp_ns) * 1e-9;
+    if (current_timestep_s > kMaximumGyroscopeSampleDelay_s) {
+      if (is_gyroscope_filter_valid_) {
+        // Replaces the delta timestamp by the filtered estimates of the delta
+        // time.
+        current_timestep_s = filtered_gyroscope_timestep_s_;
+      } else {
+        current_timestep_s = kDefaultGyroscopeTimestep_s;
+      }
+    } else {
+      FilterGyroscopeTimestep(current_timestep_s);
+    }
+
+    // Only integrate after receiving a accelerometer sample.
+    if (is_aligned_with_gravity_) {
+      const Rotationd rotation_from_gyroscope =
+          pose_prediction::GetRotationFromGyroscope(Vector3d(v_x, v_y, v_z),
+                                                    current_timestep_s);
+      current_state_.sensor_from_start_rotation =
+          rotation_from_gyroscope * current_state_.sensor_from_start_rotation;
+      current_state_.sensor_from_start_rotation.normalize();
+      UpdateStateCovariance(RotationMatrixNH(rotation_from_gyroscope));
+      state_covariance_ =
+          state_covariance_ +
+          (process_covariance_ * (current_timestep_s * current_timestep_s));
+    }
+  }
+
+  // Saves gyroscope event for future prediction.
+  current_state_.timestamp_ns = timestamp_ns;
+  current_state_.sensor_from_start_rotation_velocity = Vector3d(v_x, v_y, v_z);
+}
+
+// TODO(pfg): move to rotation object for the input.
+Vector3d SensorFusion::ComputeInnovation(const Rotationd& pose) {
+  const Vector3d predicted_down_direction =
+      RotationMatrixNH(pose) * kCanonicalZDirection;
+
+  const Rotationd rotation = Rotationd::FromTwoVectors(
+      predicted_down_direction, accelerometer_measurement_);
+  AngleAxisd angle_axis(rotation);
+  return angle_axis.axis() * angle_axis.angle();
+}
+
+void SensorFusion::ComputeMeasurementJacobian() {
+  for (int dof = 0; dof < 3; dof++) {
+    // TODO(pfg): Create this delta rotation in the constructor and used unitX..
+    Vector3d delta = Vector3d::Zero();
+    delta[dof] = kFiniteDifferencingEpsilon;
+
+    const Rotationd epsilon_rotation = RotationFromVector(delta);
+    const Vector3d delta_rotation = ComputeInnovation(
+        epsilon_rotation * current_state_.sensor_from_start_rotation);
+
+    const Vector3d col =
+        (innovation_ - delta_rotation) / kFiniteDifferencingEpsilon;
+    accelerometer_measurement_jacobian_(0, dof) = col[0];
+    accelerometer_measurement_jacobian_(1, dof) = col[1];
+    accelerometer_measurement_jacobian_(2, dof) = col[2];
+  }
+}
+
+void SensorFusion::ProcessAccelerometerSample(float acc_x, float acc_y,
+                                              float acc_z,
+                                              uint64 timestamp_ns) {
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  // Discard outdated samples.
+  if (current_accelerometer_timestamp_ns_ >= timestamp_ns) {
+    // TODO(pfg): Investigate why this happens.
+    return;
+  }
+
+  // Call reset state if required.
+  if (execute_reset_with_next_accelerometer_sample_.exchange(false)) {
+    ResetState();
+  }
+
+  accelerometer_measurement_ = Vector3d(acc_x, acc_y, acc_z);
+  current_accelerometer_timestamp_ns_ = timestamp_ns;
+
+  if (!is_aligned_with_gravity_) {
+    // This is the first accelerometer measurement so it initializes the
+    // orientation estimate.
+    current_state_.sensor_from_start_rotation = Rotationd::FromTwoVectors(
+        kCanonicalZDirection, accelerometer_measurement_);
+    is_aligned_with_gravity_ = true;
+
+    previous_accelerometer_norm_ = Length(accelerometer_measurement_);
+    return;
+  }
+
+  UpdateMeasurementCovariance();
+
+  innovation_ = ComputeInnovation(current_state_.sensor_from_start_rotation);
+  ComputeMeasurementJacobian();
+
+  // S = H * P * H' + R
+  innovation_covariance_ = accelerometer_measurement_jacobian_ *
+                               state_covariance_ *
+                               Transpose(accelerometer_measurement_jacobian_) +
+                           accelerometer_measurement_covariance_;
+
+  // K = P * H' * S^-1
+  kalman_gain_ = state_covariance_ *
+                 Transpose(accelerometer_measurement_jacobian_) *
+                 Inverse(innovation_covariance_);
+
+  // x_update = K*nu
+  state_update_ = kalman_gain_ * innovation_;
+
+  // P = (I - K * H) * P;
+  state_covariance_ = (Matrix3d::Identity() -
+                       kalman_gain_ * accelerometer_measurement_jacobian_) *
+                      state_covariance_;
+
+  // Updates pose and associate covariance matrix.
+  const Rotationd rotation_from_state_update =
+      RotationFromVector(state_update_);
+
+  current_state_.sensor_from_start_rotation =
+      rotation_from_state_update * current_state_.sensor_from_start_rotation;
+  UpdateStateCovariance(RotationMatrixNH(rotation_from_state_update));
+}
+
+void SensorFusion::UpdateStateCovariance(const Matrix3d& motion_update) {
+  state_covariance_ =
+      motion_update * state_covariance_ * Transpose(motion_update);
+}
+
+void SensorFusion::FilterGyroscopeTimestep(double gyroscope_timestep_s) {
+  if (!is_timestep_filter_initialized_) {
+    // Initializes the filter.
+    filtered_gyroscope_timestep_s_ = gyroscope_timestep_s;
+    num_gyroscope_timestep_samples_ = 1;
+    is_timestep_filter_initialized_ = true;
+    return;
+  }
+
+  // Computes the IIR filter response.
+  filtered_gyroscope_timestep_s_ =
+      kTimestepFilterCoeff * filtered_gyroscope_timestep_s_ +
+      (1 - kTimestepFilterCoeff) * gyroscope_timestep_s;
+  ++num_gyroscope_timestep_samples_;
+
+  if (num_gyroscope_timestep_samples_ > kTimestepFilterMinSamples) {
+    is_gyroscope_filter_valid_ = true;
+  }
+}
+
+void SensorFusion::UpdateMeasurementCovariance() {
+  const double current_accelerometer_norm = Length(accelerometer_measurement_);
+  // Norm change between current and previous accel readings.
+  const double current_accelerometer_norm_change =
+      std::abs(current_accelerometer_norm - previous_accelerometer_norm_);
+  previous_accelerometer_norm_ = current_accelerometer_norm;
+
+  moving_average_accelerometer_norm_change_ =
+      kSmoothingFactor * current_accelerometer_norm_change +
+      (1. - kSmoothingFactor) * moving_average_accelerometer_norm_change_;
+
+  // If we hit the accel norm change threshold, we use the maximum noise sigma
+  // for the accel covariance. For anything below that, we use a linear
+  // combination between min and max sigma values.
+  const double norm_change_ratio =
+      moving_average_accelerometer_norm_change_ / kMaxAccelNormChange;
+  const double accelerometer_noise_sigma = std::min(
+      kMaxAccelNoiseSigma,
+      kMinAccelNoiseSigma +
+          norm_change_ratio * (kMaxAccelNoiseSigma - kMinAccelNoiseSigma));
+
+  // Updates the accel covariance matrix with the new sigma value.
+  accelerometer_measurement_covariance_ = Matrix3d::Identity() *
+                                          accelerometer_noise_sigma *
+                                          accelerometer_noise_sigma;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/sensor_fusion.h b/services/vr/sensord/sensor_fusion.h
new file mode 100644
index 0000000..0ceae21
--- /dev/null
+++ b/services/vr/sensord/sensor_fusion.h
@@ -0,0 +1,181 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
+#define ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
+
+#include <atomic>
+#include <cstdlib>
+#include <mutex>
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+using Matrix3d = Eigen::Matrix<double, 3, 3>;
+using Rotationd = quatd;
+using Vector3d = vec3d;
+using AngleAxisd = Eigen::AngleAxisd;
+
+// Ported from GVR's pose_state.h.
+// Stores a 3dof pose plus derivatives. This can be used for prediction.
+struct PoseState {
+  // Time in nanoseconds for the current pose.
+  uint64_t timestamp_ns;
+
+  // Rotation from Sensor Space to Start Space.
+  Rotationd sensor_from_start_rotation;
+
+  // First derivative of the rotation.
+  // TODO(pfg): currently storing gyro data, switch to first derivative instead.
+  Vector3d sensor_from_start_rotation_velocity;
+};
+
+// Sensor fusion class that implements an Extended Kalman Filter (EKF) to
+// estimate a 3D rotation from a gyroscope and and accelerometer.
+// This system only has one state, the pose. It does not estimate any velocity
+// or acceleration.
+//
+// To learn more about Kalman filtering one can read this article which is a
+// good introduction: http://en.wikipedia.org/wiki/Kalman_filter
+//
+// Start Space is :
+// z is up.
+// y is forward based on the first sensor data.
+// x = y \times z
+// Sensor Space follows the android specification {@link
+// http://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords}
+// See http://go/vr-coords for definitions of Start Space and Sensor Space.
+//
+// This is a port from GVR's SensorFusion code (See
+// https://cs/vr/gvr/sensors/sensor_fusion.h)
+// which in turn is a port from java of OrientationEKF (See
+// https://cs/java/com/google/vr/cardboard/vrtoolkit/vrtoolkit/src/main/java/com/google/vrtoolkit/cardboard/sensors/internal/OrientationEKF.java)
+class SensorFusion {
+ public:
+  SensorFusion();
+  SensorFusion(const SensorFusion&) = delete;
+  void operator=(const SensorFusion&) = delete;
+
+  // Resets the state of the sensor fusion. It sets the velocity for
+  // prediction to zero. The reset will happen with the next
+  // accelerometer sample. Gyroscope sample will be discarded until a new
+  // accelerometer sample arrives.
+  void Reset();
+
+  // Gets the PoseState representing the latest pose and  derivatives at a
+  // particular timestamp as estimated by SensorFusion.
+  PoseState GetLatestPoseState() const;
+
+  // Processes one gyroscope sample event. This updates the pose of the system
+  // and the prediction model. The gyroscope data is assumed to be in axis angle
+  // form. Angle = ||v|| and Axis = v / ||v||, with v = [v_x, v_y, v_z]^T.
+  //
+  // @param v_x velocity in x.
+  // @param v_y velocity in y.
+  // @param v_z velocity in z.
+  // @param timestamp_ns gyroscope event timestamp in nanosecond.
+  void ProcessGyroscopeSample(float v_x, float v_y, float v_z,
+                              uint64_t timestamp_ns);
+
+  // Processes one accelerometer sample event. This updates the pose of the
+  // system. If the Accelerometer norm changes too much between sample it is not
+  // trusted as much.
+  //
+  // @param acc_x accelerometer data in x.
+  // @param acc_y accelerometer data in y.
+  // @param acc_z accelerometer data in z.
+  // @param timestamp_ns accelerometer event timestamp in nanosecond.
+  void ProcessAccelerometerSample(float acc_x, float acc_y, float acc_z,
+                                  uint64_t timestamp_ns);
+
+ private:
+  // Estimates the average timestep between gyroscope event.
+  void FilterGyroscopeTimestep(double gyroscope_timestep);
+
+  // Updates the state covariance with an incremental motion. It changes the
+  // space of the quadric.
+  void UpdateStateCovariance(const Matrix3d& motion_update);
+
+  // Computes the innovation vector of the Kalman based on the input pose.
+  // It uses the latest measurement vector (i.e. accelerometer data), which must
+  // be set prior to calling this function.
+  Vector3d ComputeInnovation(const Rotationd& pose);
+
+  // This computes the measurement_jacobian_ via numerical differentiation based
+  // on the current value of sensor_from_start_rotation_.
+  void ComputeMeasurementJacobian();
+
+  // Updates the accelerometer covariance matrix.
+  //
+  // This looks at the norm of recent accelerometer readings. If it has changed
+  // significantly, it means the phone receives additional acceleration than
+  // just gravity, and so the down vector information gravity signal is noisier.
+  //
+  // TODO(dcoz,pfg): this function is very simple, we probably need something
+  // more elaborated here once we have proper regression testing.
+  void UpdateMeasurementCovariance();
+
+  // Reset all internal states. This is not thread safe. Lock should be acquired
+  // outside of it. This function is called in ProcessAccelerometerSample.
+  void ResetState();
+
+  // Current transformation from Sensor Space to Start Space.
+  // x_sensor = sensor_from_start_rotation_ * x_start;
+  PoseState current_state_;
+
+  // Filtering of the gyroscope timestep started?
+  bool is_timestep_filter_initialized_;
+  // Filtered gyroscope timestep valid?
+  bool is_gyroscope_filter_valid_;
+  // Sensor fusion currently aligned with gravity? After initialization
+  // it will requires a couple of accelerometer data for the system to get
+  // aligned.
+  bool is_aligned_with_gravity_;
+
+  // Covariance of Kalman filter state (P in common formulation).
+  Matrix3d state_covariance_;
+  // Covariance of the process noise (Q in common formulation).
+  Matrix3d process_covariance_;
+  // Covariance of the accelerometer measurement (R in common formulation).
+  Matrix3d accelerometer_measurement_covariance_;
+  // Covariance of innovation (S in common formulation).
+  Matrix3d innovation_covariance_;
+  // Jacobian of the measurements (H in common formulation).
+  Matrix3d accelerometer_measurement_jacobian_;
+  // Gain of the Kalman filter (K in common formulation).
+  Matrix3d kalman_gain_;
+  // Parameter update a.k.a. innovation vector. (\nu in common formulation).
+  Vector3d innovation_;
+  // Measurement vector (z in common formulation).
+  Vector3d accelerometer_measurement_;
+  // Current prediction vector (g in common formulation).
+  Vector3d prediction_;
+  // Control input, currently this is only the gyroscope data (\mu in common
+  // formulation).
+  Vector3d control_input_;
+  // Update of the state vector. (x in common formulation).
+  Vector3d state_update_;
+
+  // Time of the last accelerometer processed event.
+  uint64_t current_accelerometer_timestamp_ns_;
+
+  // Estimates of the timestep between gyroscope event in seconds.
+  double filtered_gyroscope_timestep_s_;
+  // Number of timestep samples processed so far by the filter.
+  uint32_t num_gyroscope_timestep_samples_;
+  // Norm of the accelerometer for the previous measurement.
+  double previous_accelerometer_norm_;
+  // Moving average of the accelerometer norm changes. It is computed for every
+  // sensor datum.
+  double moving_average_accelerometer_norm_change_;
+
+  // Flag indicating if a state reset should be executed with the next
+  // accelerometer sample.
+  std::atomic<bool> execute_reset_with_next_accelerometer_sample_;
+
+  mutable std::mutex mutex_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
diff --git a/services/vr/sensord/sensor_hal_thread.cpp b/services/vr/sensord/sensor_hal_thread.cpp
new file mode 100644
index 0000000..c321d4f
--- /dev/null
+++ b/services/vr/sensord/sensor_hal_thread.cpp
@@ -0,0 +1,158 @@
+#include "sensor_hal_thread.h"
+
+#include <dvr/performance_client_api.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+SensorHalThread::SensorHalThread(bool* out_success)
+    : shutting_down_(false),
+      paused_(false),
+      sensor_module_(nullptr),
+      sensor_device_(nullptr),
+      sensor_list_(nullptr) {
+  // Assume failure; we will change this to true on success.
+  *out_success = false;
+
+  // TODO(segal): module & device should be singletons.
+  int32_t err = hw_get_module_by_class(SENSORS_HARDWARE_MODULE_ID, "platform",
+                                       (hw_module_t const**)&sensor_module_);
+
+  if (err) {
+    ALOGE("couldn't load %s module (%s)", SENSORS_HARDWARE_MODULE_ID,
+          strerror(-err));
+    return;
+  }
+
+  err = sensors_open_1(&sensor_module_->common, &sensor_device_);
+  if (err) {
+    ALOGE("couldn't open device for module %s (%s)", SENSORS_HARDWARE_MODULE_ID,
+          strerror(-err));
+    return;
+  }
+
+  const int sensor_count =
+      sensor_module_->get_sensors_list(sensor_module_, &sensor_list_);
+
+  // Deactivate all of the sensors initially.
+  sensor_user_count_.resize(sensor_count, 0);
+  for (int i = 0; i < sensor_count; ++i) {
+    err = sensor_device_->activate(
+        reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+        sensor_list_[i].handle, 0);
+
+    if (err) {
+      ALOGE("failed to deactivate sensor %d (%s)", i, strerror(-err));
+      return;
+    }
+  }
+
+  // At this point, we've successfully initialized everything.
+  *out_success = true;
+}
+
+SensorHalThread::~SensorHalThread() {
+  {
+    std::unique_lock<std::mutex> lock(mutex_);
+    shutting_down_ = true;
+    condition_.notify_one();
+  }
+
+  // Implicitly joins *thread_ if it's running.
+}
+
+void SensorHalThread::StartPolling(const EventConsumer& consumer) {
+  if (thread_) {
+    ALOGE("SensorHalThread::Start() called but thread is already running!");
+    return;
+  }
+
+  thread_.reset(new std::thread([this, consumer] {
+    const int priority_error = dvrSetSchedulerClass(0, "sensors:high");
+    LOG_ALWAYS_FATAL_IF(
+        priority_error < 0,
+        "SensorHalTread::StartPolling: Failed to set scheduler class: %s",
+        strerror(-priority_error));
+
+    for (;;) {
+      for (;;) {
+        std::unique_lock<std::mutex> lock(mutex_);
+        if (shutting_down_)
+          return;
+        if (!paused_)
+          break;
+        condition_.wait(lock);
+      }
+      const int kMaxEvents = 100;
+      sensors_event_t events[kMaxEvents];
+      ssize_t event_count = 0;
+      do {
+        if (sensor_device_) {
+          event_count = sensor_device_->poll(
+              reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+              events, kMaxEvents);
+        } else {
+          // When there is no sensor_device_, we still call the consumer at
+          // regular intervals in case mock poses are in use. Note that this
+          // will never be the case for production devices, but this helps
+          // during bringup.
+          usleep(5000);
+        }
+      } while (event_count == -EINTR);
+      if (event_count == kMaxEvents)
+        ALOGI("max events (%d) reached", kMaxEvents);
+
+      if (event_count >= 0) {
+        consumer(events, events + event_count);
+      } else {
+        ALOGE(
+            "SensorHalThread::StartPolling: Error while polling sensor: %s "
+            "(%zd)",
+            strerror(-event_count), -event_count);
+      }
+    }
+  }));
+}
+
+void SensorHalThread::SetPaused(bool is_paused) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  paused_ = is_paused;
+  condition_.notify_one();
+}
+
+void SensorHalThread::StartUsingSensor(const int sensor_index) {
+  if (sensor_index < 0 || sensor_index >= GetSensorCount()) {
+    ALOGE("StartUsingSensor(): sensor index %d out of range [0, %d)",
+          sensor_index, GetSensorCount());
+    return;
+  }
+
+  std::lock_guard<std::mutex> guard(user_count_mutex_);
+  if (sensor_user_count_[sensor_index]++ == 0) {
+    sensor_device_->activate(
+        reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+        sensor_list_[sensor_index].handle, 1);
+    sensor_device_->setDelay(
+        reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+        sensor_list_[sensor_index].handle, 0);
+  }
+}
+
+void SensorHalThread::StopUsingSensor(const int sensor_index) {
+  if (sensor_index < 0 || sensor_index >= GetSensorCount()) {
+    ALOGE("StopUsingSensor(): sensor index %d out of range [0, %d)",
+          sensor_index, GetSensorCount());
+    return;
+  }
+
+  std::lock_guard<std::mutex> guard(user_count_mutex_);
+  if (--sensor_user_count_[sensor_index] == 0) {
+    sensor_device_->activate(
+        reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+        sensor_list_[sensor_index].handle, 0);
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/sensor_hal_thread.h b/services/vr/sensord/sensor_hal_thread.h
new file mode 100644
index 0000000..9220757
--- /dev/null
+++ b/services/vr/sensord/sensor_hal_thread.h
@@ -0,0 +1,99 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
+#define ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
+
+#include <hardware/sensors.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// Manages initialization and polling of the sensor HAL. Polling is performed
+// continuously on a thread that passes events along to an arbitrary consumer.
+// All const member functions are thread-safe; otherwise, thread safety is noted
+// for each function.
+class SensorHalThread : public SensorThread {
+ public:
+  // Initializes the sensor HAL, but does not yet start polling (see Start()
+  // below). Sets *out_success to true on success; otherwise, sets *out_success
+  // to false and logs an error.
+  explicit SensorHalThread(bool* out_success);
+
+  // Tells the polling thread to shut down if it's running, and waits for it to
+  // complete its polling loop.
+  ~SensorHalThread() override;
+
+  // Begins polling on the thread. The provided consumer will be notified of
+  // events. Event notification occurs on the polling thread.
+  // Calling Start() more than once on an instance of SensorHalThread is
+  // invalid.
+  void StartPolling(const EventConsumer& consumer) override;
+
+  // Set whether the sensor polling thread is paused or not. This is useful
+  // while we need to support both 3DoF and 6DoF codepaths. This 3DoF codepath
+  // must be paused while the 6DoF codepath is using the IMU event stream.
+  void SetPaused(bool is_paused) override;
+
+  // Increase the number of users of the given sensor by one. Activates the
+  // sensor if it wasn't already active.
+  // Safe to call concurrently with any other functions in this class.
+  void StartUsingSensor(int sensor_index) override;
+
+  // Decrease the number of users of the given sensor by one. Deactivates the
+  // sensor if its usage count has dropped to zero.
+  // Safe to call concurrently with any other functions in this class.
+  void StopUsingSensor(int sensor_index) override;
+
+  // The number of sensors that are available. Returns a negative number if
+  // initialization failed.
+  int GetSensorCount() const override {
+    return static_cast<int>(sensor_user_count_.size());
+  }
+
+  // The underlying sensor HAL data structure for the sensor at the given index.
+  int GetSensorType(int index) const override {
+    return sensor_list_[index].type;
+  }
+
+ private:
+  // The actual thread on which we consume events.
+  std::unique_ptr<std::thread> thread_;
+
+  // Mutex for access to shutting_down_ and paused_ members.
+  std::mutex mutex_;
+
+  // Condition for signaling pause/unpause to the thread.
+  std::condition_variable condition_;
+
+  // If this member is set to true, the thread will stop running at its next
+  // iteration. Only set with the mutex held and signal condition_ when changed.
+  bool shutting_down_;
+
+  // If this member is set to true, the thread will pause at its next
+  // iteration. Only set with the mutex held and signal condition_ when changed.
+  bool paused_;
+
+  // HAL access
+  struct sensors_module_t* sensor_module_;
+  sensors_poll_device_1_t* sensor_device_;
+
+  // Contiguous array of available sensors, owned by the sensor HAL.
+  const sensor_t* sensor_list_;
+
+  // Mutex that protects access to sensor_user_count_.data().
+  std::mutex user_count_mutex_;
+
+  // A count of how many users each sensor has. Protected by user_count_mutex.
+  std::vector<int> sensor_user_count_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
diff --git a/services/vr/sensord/sensor_ndk_thread.cpp b/services/vr/sensord/sensor_ndk_thread.cpp
new file mode 100644
index 0000000..9c3abbc
--- /dev/null
+++ b/services/vr/sensord/sensor_ndk_thread.cpp
@@ -0,0 +1,269 @@
+#include "sensor_ndk_thread.h"
+
+#include <dvr/performance_client_api.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+static constexpr int kLooperIdUser = 5;
+}  // namespace
+
+SensorNdkThread::SensorNdkThread(bool* out_success)
+    : shutting_down_(false),
+      paused_(true),
+      thread_started_(false),
+      initialization_result_(false),
+      looper_(nullptr),
+      sensor_manager_(nullptr),
+      event_queue_(nullptr),
+      sensor_list_(nullptr),
+      sensor_count_(0) {
+  // Assume failure; we will change this to true on success.
+  *out_success = false;
+
+  // These structs are the same, but sanity check the sizes.
+  static_assert(sizeof(sensors_event_t) == sizeof(ASensorEvent),
+                "Error: sizeof(sensors_event_t) != sizeof(ASensorEvent)");
+
+  thread_.reset(new std::thread([this] {
+    const int priority_error = dvrSetSchedulerClass(0, "sensors:high");
+    LOG_ALWAYS_FATAL_IF(
+        priority_error < 0,
+        "SensorHalTread::StartPolling: Failed to set scheduler class: %s",
+        strerror(-priority_error));
+
+    // Start ALooper and initialize sensor access.
+    {
+      std::unique_lock<std::mutex> lock(mutex_);
+      InitializeSensors();
+      thread_started_ = true;
+      init_condition_.notify_one();
+      // Continue on failure - the loop below will periodically retry.
+    }
+
+    EventConsumer consumer;
+    for (;;) {
+      for (;;) {
+        std::unique_lock<std::mutex> lock(mutex_);
+        UpdateSensorUse();
+        if (!consumer)
+          consumer = consumer_;
+        if (shutting_down_)
+          return;
+        if (!paused_)
+          break;
+        condition_.wait(lock);
+      }
+
+      constexpr int kMaxEvents = 100;
+      sensors_event_t events[kMaxEvents];
+      ssize_t event_count = 0;
+      if (initialization_result_) {
+        int poll_fd, poll_events;
+        void* poll_source;
+        // Poll for events.
+        int ident = ALooper_pollAll(-1, &poll_fd, &poll_events, &poll_source);
+
+        if (ident != kLooperIdUser)
+          continue;
+
+        ASensorEvent* event = reinterpret_cast<ASensorEvent*>(&events[0]);
+        event_count =
+            ASensorEventQueue_getEvents(event_queue_, event, kMaxEvents);
+
+        if (event_count == 0) {
+          ALOGE("Detected sensor service failure, restarting sensors");
+          // This happens when sensorservice has died and restarted. To avoid
+          // spinning we need to restart the sensor access.
+          DestroySensors();
+        }
+      } else {
+        // When there is no sensor_device_, we still call the consumer at
+        // regular intervals in case mock poses are in use. Note that this
+        // will never be the case for production devices, but this helps
+        // during bringup.
+        usleep(5000);
+      }
+      if (event_count == kMaxEvents)
+        ALOGI("max events (%d) reached", kMaxEvents);
+
+      if (event_count >= 0) {
+        consumer(events, events + event_count);
+      } else {
+        ALOGE(
+            "SensorNdkThread::StartPolling: Error while polling sensor: %s "
+            "(%zd)",
+            strerror(-event_count), -event_count);
+      }
+    }
+
+    // About to exit sensor thread, destroy sensor objects.
+    DestroySensors();
+  }));
+
+  // Wait for thread to startup and initialize sensors so that we know whether
+  // it succeeded.
+  {
+    std::unique_lock<std::mutex> lock(mutex_);
+    while (!thread_started_)
+      init_condition_.wait(lock);
+  }
+
+  // At this point, we've successfully initialized everything.
+  // The NDK sensor thread will continue to retry on error, so assume success here.
+  *out_success = true;
+}
+
+SensorNdkThread::~SensorNdkThread() {
+  {
+    if (looper_)
+      ALooper_wake(looper_);
+    std::unique_lock<std::mutex> lock(mutex_);
+    shutting_down_ = true;
+    condition_.notify_one();
+  }
+
+  thread_->join();
+}
+
+bool SensorNdkThread::InitializeSensors() {
+  looper_ = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+  if (!looper_) {
+    ALOGE("Failed to create ALooper.");
+    return false;
+  }
+
+  // Prepare to monitor accelerometer
+  sensor_manager_ = ASensorManager_getInstanceForPackage(nullptr);
+  if (!sensor_manager_) {
+    ALOGE("Failed to create ASensorManager.");
+    return false;
+  }
+
+  event_queue_ = ASensorManager_createEventQueue(
+      sensor_manager_, looper_, kLooperIdUser, nullptr, nullptr);
+  if (!event_queue_) {
+    ALOGE("Failed to create sensor EventQueue.");
+    return false;
+  }
+
+  sensor_count_ = ASensorManager_getSensorList(sensor_manager_, &sensor_list_);
+  ALOGI("Sensor count %d", sensor_count_);
+
+  sensor_user_count_.resize(sensor_count_, 0);
+
+  // To recover from sensorservice restart, enable the sensors that are already
+  // requested.
+  for (size_t sensor_index = 0; sensor_index < sensor_user_count_.size();
+       ++sensor_index) {
+    if (sensor_user_count_[sensor_index] > 0) {
+      int result = ASensorEventQueue_registerSensor(
+          event_queue_, sensor_list_[sensor_index], 0, 0);
+      ALOGE_IF(result < 0, "ASensorEventQueue_registerSensor failed: %d",
+               result);
+    }
+  }
+
+  initialization_result_ = true;
+  return true;
+}
+
+void SensorNdkThread::DestroySensors() {
+  if (!event_queue_)
+    return;
+  for (size_t sensor_index = 0; sensor_index < sensor_user_count_.size();
+       ++sensor_index) {
+    if (sensor_user_count_[sensor_index] > 0) {
+      ASensorEventQueue_disableSensor(event_queue_, sensor_list_[sensor_index]);
+    }
+  }
+  ASensorManager_destroyEventQueue(sensor_manager_, event_queue_);
+  event_queue_ = nullptr;
+  initialization_result_ = false;
+}
+
+void SensorNdkThread::UpdateSensorUse() {
+  if (!initialization_result_) {
+    // Sleep for 1 second to avoid spinning during system instability.
+    usleep(1000 * 1000);
+    InitializeSensors();
+    if (!initialization_result_)
+      return;
+  }
+
+  if (!enable_sensors_.empty()) {
+    for (int sensor_index : enable_sensors_) {
+      if (sensor_user_count_[sensor_index]++ == 0) {
+        int result = ASensorEventQueue_registerSensor(
+            event_queue_, sensor_list_[sensor_index], 0, 0);
+        ALOGE_IF(result < 0, "ASensorEventQueue_registerSensor failed: %d",
+                 result);
+      }
+    }
+    enable_sensors_.clear();
+  }
+
+  if (!disable_sensors_.empty()) {
+    for (int sensor_index : disable_sensors_) {
+      if (--sensor_user_count_[sensor_index] == 0) {
+        int result = ASensorEventQueue_disableSensor(
+            event_queue_, sensor_list_[sensor_index]);
+        ALOGE_IF(result < 0, "ASensorEventQueue_disableSensor failed: %d",
+                 result);
+      }
+    }
+    disable_sensors_.clear();
+  }
+}
+
+void SensorNdkThread::StartPolling(const EventConsumer& consumer) {
+  {
+    std::unique_lock<std::mutex> lock(mutex_);
+    if (consumer_) {
+      ALOGE("Already started sensor thread.");
+      return;
+    }
+    consumer_ = consumer;
+  }
+  SetPaused(false);
+}
+
+void SensorNdkThread::SetPaused(bool is_paused) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  // SetPaused may be called before we have StartPolling, make sure we have
+  // an event consumer. Otherwise we defer until StartPolling is called.
+  if (!consumer_)
+    return;
+  paused_ = is_paused;
+  condition_.notify_one();
+  ALooper_wake(looper_);
+}
+
+void SensorNdkThread::StartUsingSensor(const int sensor_index) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  if (sensor_index < 0 || sensor_index >= sensor_count_) {
+    ALOGE("StartUsingSensor(): sensor index %d out of range [0, %d)",
+          sensor_index, sensor_count_);
+    return;
+  }
+
+  enable_sensors_.push_back(sensor_index);
+  ALooper_wake(looper_);
+}
+
+void SensorNdkThread::StopUsingSensor(const int sensor_index) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  if (sensor_index < 0 || sensor_index >= sensor_count_) {
+    ALOGE("StopUsingSensor(): sensor index %d out of range [0, %d)",
+          sensor_index, sensor_count_);
+    return;
+  }
+
+  disable_sensors_.push_back(sensor_index);
+  ALooper_wake(looper_);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/sensor_ndk_thread.h b/services/vr/sensord/sensor_ndk_thread.h
new file mode 100644
index 0000000..eb3cf9d
--- /dev/null
+++ b/services/vr/sensord/sensor_ndk_thread.h
@@ -0,0 +1,124 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
+#define ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
+
+#include <android/sensor.h>
+#include <hardware/sensors.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// Manages initialization and polling of the sensor data. Polling is performed
+// continuously on a thread that passes events along to an arbitrary consumer.
+// All const member functions are thread-safe; otherwise, thread safety is noted
+// for each function.
+class SensorNdkThread : public SensorThread {
+ public:
+  // Initializes the sensor access, but does not yet start polling (see Start()
+  // below). Sets *out_success to true on success; otherwise, sets *out_success
+  // to false and logs an error.
+  explicit SensorNdkThread(bool* out_success);
+
+  // Tells the polling thread to shut down if it's running, and waits for it to
+  // complete its polling loop.
+  ~SensorNdkThread() override;
+
+  // Begins polling on the thread. The provided consumer will be notified of
+  // events. Event notification occurs on the polling thread.
+  // Calling Start() more than once on an instance of SensorNdkThread is
+  // invalid.
+  void StartPolling(const EventConsumer& consumer) override;
+
+  // Set whether the sensor polling thread is paused or not. This is useful
+  // while we need to support both 3DoF and 6DoF codepaths. This 3DoF codepath
+  // must be paused while the 6DoF codepath is using the IMU event stream.
+  void SetPaused(bool is_paused) override;
+
+  // Increase the number of users of the given sensor by one. Activates the
+  // sensor if it wasn't already active.
+  // Safe to call concurrently with any other functions in this class.
+  void StartUsingSensor(int sensor_index) override;
+
+  // Decrease the number of users of the given sensor by one. Deactivates the
+  // sensor if its usage count has dropped to zero.
+  // Safe to call concurrently with any other functions in this class.
+  void StopUsingSensor(int sensor_index) override;
+
+  // The number of sensors that are available. Returns a negative number if
+  // initialization failed.
+  int GetSensorCount() const override { return sensor_count_; }
+
+  // The underlying sensor HAL data structure for the sensor at the given index.
+  int GetSensorType(int index) const override {
+    return ASensor_getType(sensor_list_[index]);
+  }
+
+ private:
+  // Initialize ALooper and sensor access on the thread.
+  // Returns true on success, false on failure.
+  bool InitializeSensors();
+
+  // Destroy sensor access.
+  void DestroySensors();
+
+  // Start or stop requested sensors from the thread. Class mutex must already
+  // be locked.
+  void UpdateSensorUse();
+
+  // The actual thread on which we consume events.
+  std::unique_ptr<std::thread> thread_;
+
+  // Mutex for access to shutting_down_ and paused_ members.
+  std::mutex mutex_;
+
+  // Condition for signaling pause/unpause to the thread.
+  std::condition_variable condition_;
+
+  // Condition for signaling thread initialization.
+  std::condition_variable init_condition_;
+
+  // If this member is set to true, the thread will stop running at its next
+  // iteration. Only set with the mutex held and signal condition_ when changed.
+  bool shutting_down_;
+
+  // If this member is set to true, the thread will pause at its next
+  // iteration. Only set with the mutex held and signal condition_ when changed.
+  bool paused_;
+
+  // Thread start hand shake to verify that sensor initialization succeeded.
+  bool thread_started_;
+
+  // Initialization result (true for success).
+  bool initialization_result_;
+
+  // The callback.
+  EventConsumer consumer_;
+
+  // Sensor access
+  ALooper* looper_;
+  ASensorManager* sensor_manager_;
+  ASensorEventQueue* event_queue_;
+
+  // Sensor list from NDK.
+  ASensorList sensor_list_;
+  int sensor_count_;
+
+  // Requests to the sensor thread to enable or disable given sensors.
+  std::vector<int> enable_sensors_;
+  std::vector<int> disable_sensors_;
+
+  // A count of how many users each sensor has. Protected by user_count_mutex.
+  std::vector<int> sensor_user_count_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
diff --git a/services/vr/sensord/sensor_service.cpp b/services/vr/sensord/sensor_service.cpp
new file mode 100644
index 0000000..a182a26
--- /dev/null
+++ b/services/vr/sensord/sensor_service.cpp
@@ -0,0 +1,184 @@
+#include "sensor_service.h"
+
+#include <hardware/sensors.h>
+#include <log/log.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <poll.h>
+#include <private/dvr/sensor-ipc.h>
+#include <time.h>
+
+using android::pdx::default_transport::Endpoint;
+
+namespace android {
+namespace dvr {
+
+SensorService::SensorService(SensorThread* sensor_thread)
+    : BASE("SensorService", Endpoint::Create(DVR_SENSOR_SERVICE_CLIENT)),
+      sensor_thread_(sensor_thread) {
+  sensor_clients_.resize(sensor_thread_->GetSensorCount());
+
+  for (int i = 0; i < sensor_thread_->GetSensorCount(); ++i)
+    type_to_sensor_[sensor_thread_->GetSensorType(i)] = i;
+}
+
+std::shared_ptr<pdx::Channel> SensorService::OnChannelOpen(pdx::Message& msg) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  const pdx::MessageInfo& info = msg.GetInfo();
+
+  std::shared_ptr<SensorClient> client(
+      new SensorClient(*this, info.pid, info.cid));
+  AddClient(client);
+  return client;
+}
+
+void SensorService::OnChannelClose(pdx::Message& /*msg*/,
+                                   const std::shared_ptr<pdx::Channel>& chan) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  auto client = std::static_pointer_cast<SensorClient>(chan);
+  if (!client) {
+    ALOGW("WARNING: SensorClient was NULL!\n");
+    return;
+  }
+  RemoveClient(client);
+}
+
+void SensorService::AddClient(const std::shared_ptr<SensorClient>& client) {
+  clients_.push_front(client);
+}
+
+void SensorService::RemoveClient(const std::shared_ptr<SensorClient>& client) {
+  // First remove it from the clients associated with its sensor, if any.
+  RemoveSensorClient(client.get());
+
+  // Finally, remove it from the list of clients we're aware of, and decrease
+  // its reference count.
+  clients_.remove(client);
+}
+
+void SensorService::RemoveSensorClient(SensorClient* client) {
+  if (!client->has_sensor())
+    return;
+
+  std::forward_list<SensorClient*>& sensor_clients =
+      sensor_clients_[client->sensor()];
+  sensor_clients.remove(client);
+  sensor_thread_->StopUsingSensor(client->sensor());
+
+  client->unset_sensor();
+}
+
+pdx::Status<void> SensorService::HandleMessage(pdx::Message& msg) {
+  pdx::Status<void> ret;
+  const pdx::MessageInfo& info = msg.GetInfo();
+  switch (info.op) {
+    case DVR_SENSOR_START: {
+      std::lock_guard<std::mutex> guard(mutex_);
+      // Associate this channel with the indicated sensor,
+      // unless it already has an association. In that case,
+      // fail.
+      auto client = std::static_pointer_cast<SensorClient>(msg.GetChannel());
+      if (client->has_sensor())
+        REPLY_ERROR(msg, EINVAL, error);
+      int sensor_type;
+      if (!msg.ReadAll(&sensor_type, sizeof(sensor_type)))
+        REPLY_ERROR(msg, EIO, error);
+
+      // Find the sensor of the requested type.
+      if (type_to_sensor_.find(sensor_type) == type_to_sensor_.end())
+        REPLY_ERROR(msg, EINVAL, error);
+      const int sensor_index = type_to_sensor_[sensor_type];
+
+      sensor_clients_[sensor_index].push_front(client.get());
+      client->set_sensor(sensor_index);
+      sensor_thread_->StartUsingSensor(sensor_index);
+
+      REPLY_SUCCESS(msg, 0, error);
+    }
+    case DVR_SENSOR_STOP: {
+      std::lock_guard<std::mutex> guard(mutex_);
+      auto client = std::static_pointer_cast<SensorClient>(msg.GetChannel());
+      if (!client->has_sensor())
+        REPLY_ERROR(msg, EINVAL, error);
+      RemoveSensorClient(client.get());
+      REPLY_SUCCESS(msg, 0, error);
+    }
+    case DVR_SENSOR_POLL: {
+      std::lock_guard<std::mutex> guard(mutex_);
+      auto client = std::static_pointer_cast<SensorClient>(msg.GetChannel());
+
+      // Package up the events we've got for this client. Number of
+      // events, followed by 0 or more sensor events, popped from
+      // this client's queue until it's empty.
+      int num_events = client->EventCount();
+      sensors_event_t out_buffer[num_events];
+      client->WriteEvents(out_buffer);
+      struct iovec svec[] = {
+          {.iov_base = &num_events, .iov_len = sizeof(num_events)},
+          {.iov_base = out_buffer,
+           .iov_len = num_events * sizeof(sensors_event_t)},
+      };
+      ret = msg.WriteVectorAll(svec, 2);
+      if (!ret) {
+        REPLY_ERROR(msg, EIO, error);
+      }
+      REPLY_SUCCESS(msg, 0, error);
+    }
+    default:
+      // Do not lock mutex_ here, because this may call the on*() handlers,
+      // which will lock the mutex themselves.
+      ret = Service::HandleMessage(msg);
+      break;
+  }
+error:
+  return ret;
+}
+
+void SensorService::EnqueueEvents(const sensors_event_t* begin_events,
+                                  const sensors_event_t* end_events) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  // Put the sensor values we got in the circular queue for each client that
+  // cares about the given event.
+  for (const sensors_event_t* event = begin_events; event != end_events;
+       ++event) {
+    const int sensor_index = type_to_sensor_[event->type];
+    for (const auto& client : sensor_clients_[sensor_index]) {
+      client->EnqueueEvent(*event);
+    }
+  }
+}
+
+void SensorClient::WriteEvents(sensors_event_t* buffer) {
+  while (!event_queue_.Empty()) {
+    *buffer = *(event_queue_.Top());
+    event_queue_.Pop();
+    ++buffer;
+  }
+}
+
+void SensorClient::CircularQ::Push(const sensors_event_t& event) {
+  if (count_ != 0 && head_ == tail_) {
+    Pop();  // If we're full, throw away the oldest event.
+  }
+  events_[head_] = event;
+  head_ = (head_ + 1) % kCqSize;
+  ++count_;
+}
+
+const sensors_event_t* SensorClient::CircularQ::Top() const {
+  if (count_ == 0)
+    return nullptr;
+  return &events_[tail_];
+}
+
+void SensorClient::CircularQ::Pop() {
+  if (count_ == 0)
+    return;
+  tail_ = (tail_ + 1) % kCqSize;
+  --count_;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/sensor_service.h b/services/vr/sensord/sensor_service.h
new file mode 100644
index 0000000..6ea470b
--- /dev/null
+++ b/services/vr/sensord/sensor_service.h
@@ -0,0 +1,132 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_SERVICE_H_
+#define ANDROID_DVR_SENSORD_SENSOR_SERVICE_H_
+
+#include <forward_list>
+#include <unordered_map>
+#include <vector>
+
+#include <pdx/service.h>
+#include <pthread.h>
+
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+class SensorClient;
+
+/*
+ * SensorService implements the sensor service over ServiceFS.
+ * The sensor service provides an interface to one sensor over
+ * each channel.
+ */
+class SensorService : public pdx::ServiceBase<SensorService> {
+ public:
+  pdx::Status<void> HandleMessage(pdx::Message& msg) override;
+  std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& msg) override;
+  void OnChannelClose(pdx::Message& msg,
+                      const std::shared_ptr<pdx::Channel>& chan) override;
+
+  // Enqueue the events in [begin_events, end_events) onto any clients that care
+  // about them.
+  // Safe to call concurrently with any other public member functions.
+  void EnqueueEvents(const sensors_event_t* begin_events,
+                     const sensors_event_t* end_events);
+
+ private:
+  friend BASE;
+
+  // Initializes the service. Keeps a reference to sensor_thread, which must be
+  // non-null.
+  explicit SensorService(SensorThread* sensor_thread);
+
+  // The abstraction around the sensor HAL.
+  SensorThread* sensor_thread_;
+
+  // All of the clients we are connected to. This is the one place in this class
+  // where we keep the SensorClient instances alive using shared_ptr instances.
+  std::forward_list<std::shared_ptr<SensorClient>> clients_;
+
+  // Map types back to sensor indexes.
+  std::unordered_map<int, int> type_to_sensor_;
+  // For each sensor, the list of clients that are connected to it.
+  // Every entry in here must also be in clients_, so that its reference count
+  // remains positive.
+  std::vector<std::forward_list<SensorClient*>> sensor_clients_;
+
+  // Protects access to all member variables.
+  std::mutex mutex_;
+
+  // None of the following functions is thread-safe; callers must lock mutex_
+  // before calling one.
+  void AddClient(const std::shared_ptr<SensorClient>& client);
+  void RemoveClient(const std::shared_ptr<SensorClient>& client);
+  // Dissociate the indicated client from its sensor, if it has one; otherwise
+  // do nothing.
+  void RemoveSensorClient(SensorClient* client);
+
+  SensorService(const SensorService&) = delete;
+  void operator=(const SensorService&) = delete;
+};
+
+/*
+ * SensorClient manages the service-side per-client context for each client
+ * using the service.
+ */
+class SensorClient : public pdx::Channel {
+ public:
+  SensorClient(SensorService& /*service*/, int /*pid*/, int /*cid*/)
+      : sensor_index_(-1), has_sensor_index_(false) {}
+
+  bool has_sensor() const { return has_sensor_index_; }
+  int sensor() const { return sensor_index_; }
+  void set_sensor(int sensor) {
+    sensor_index_ = sensor;
+    has_sensor_index_ = true;
+  }
+  void unset_sensor() {
+    sensor_index_ = -1;
+    has_sensor_index_ = false;
+  }
+
+  int EventCount() const { return event_queue_.Count(); }
+
+  // Push an event onto our queue.
+  void EnqueueEvent(const sensors_event_t& event) { event_queue_.Push(event); }
+
+  // Write all the events in our queue (and clear it) to the supplied
+  // buffer. Buffer must be large enough.
+  void WriteEvents(sensors_event_t* buffer);
+
+ private:
+  SensorClient(const SensorClient&) = delete;
+  SensorClient& operator=(const SensorClient&) = delete;
+
+  int sensor_index_ = -1;
+  bool has_sensor_index_ = false;
+  // Circular queue holds as-yet-unasked-for events for the sensor associated
+  // with this client.
+  class CircularQ {
+   public:
+    static const int kCqSize = 10;
+    CircularQ() : head_(0), tail_(0), count_(0) {}
+    ~CircularQ() {}
+    void Push(const sensors_event_t& event);
+    const sensors_event_t* Top() const;
+    void Pop();
+    bool Empty() const { return count_ == 0; }
+    int Count() const { return count_; }
+
+   private:
+    sensors_event_t events_[kCqSize];
+    int head_ = 0;
+    int tail_ = 0;
+    int count_ = 0;
+  };
+  CircularQ event_queue_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_SENSOR_SERVICE_H_
diff --git a/services/vr/sensord/sensor_thread.cpp b/services/vr/sensord/sensor_thread.cpp
new file mode 100644
index 0000000..01e4e7e
--- /dev/null
+++ b/services/vr/sensord/sensor_thread.cpp
@@ -0,0 +1,9 @@
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+SensorThread::~SensorThread() {}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/sensor_thread.h b/services/vr/sensord/sensor_thread.h
new file mode 100644
index 0000000..46aba17
--- /dev/null
+++ b/services/vr/sensord/sensor_thread.h
@@ -0,0 +1,58 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_THREAD_H_
+#define ANDROID_DVR_SENSORD_SENSOR_THREAD_H_
+
+#include <hardware/sensors.h>
+
+#include <functional>
+
+namespace android {
+namespace dvr {
+
+// Manages initialization and polling of the sensor data. Polling is performed
+// continuously on a thread that passes events along to an arbitrary consumer.
+// All const member functions are thread-safe; otherwise, thread safety is noted
+// for each function.
+class SensorThread {
+ public:
+  // A function type that can be called to provide it with new events.
+  // [events_begin, events_end) forms a contiguous array of events.
+  using EventConsumer = std::function<void(const sensors_event_t* events_begin,
+                                           const sensors_event_t* events_end)>;
+
+  // Tells the polling thread to shut down if it's running, and waits for it to
+  // complete its polling loop.
+  virtual ~SensorThread();
+
+  // Begins polling on the thread. The provided consumer will be notified of
+  // events. Event notification occurs on the polling thread.
+  // Calling Start() more than once on an instance of SensorThread is
+  // invalid.
+  virtual void StartPolling(const EventConsumer& consumer) = 0;
+
+  // Set whether the sensor polling thread is paused or not. This is useful
+  // while we need to support both 3DoF and 6DoF codepaths. This 3DoF codepath
+  // must be paused while the 6DoF codepath is using the IMU event stream.
+  virtual void SetPaused(bool is_paused) = 0;
+
+  // Increase the number of users of the given sensor by one. Activates the
+  // sensor if it wasn't already active.
+  // Safe to call concurrently with any other functions in this class.
+  virtual void StartUsingSensor(int sensor_index) = 0;
+
+  // Decrease the number of users of the given sensor by one. Deactivates the
+  // sensor if its usage count has dropped to zero.
+  // Safe to call concurrently with any other functions in this class.
+  virtual void StopUsingSensor(int sensor_index) = 0;
+
+  // The number of sensors that are available. Returns a negative number if
+  // initialization failed.
+  virtual int GetSensorCount() const = 0;
+
+  // Get the sensor type for the sensor at the given index.
+  virtual int GetSensorType(int index) const = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_SENSOR_THREAD_H_
diff --git a/services/vr/sensord/sensord.cpp b/services/vr/sensord/sensord.cpp
new file mode 100644
index 0000000..db39152
--- /dev/null
+++ b/services/vr/sensord/sensord.cpp
@@ -0,0 +1,91 @@
+#define LOG_TAG "sensord"
+
+#include <string.h>
+
+#include <binder/ProcessState.h>
+
+#include <dvr/performance_client_api.h>
+#include <pdx/default_transport/service_dispatcher.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/sensor-ipc.h>
+
+#include "pose_service.h"
+#include "sensor_hal_thread.h"
+#include "sensor_ndk_thread.h"
+#include "sensor_service.h"
+#include "sensor_thread.h"
+#include "sensord_extension.h"
+
+using android::dvr::PoseService;
+using android::dvr::SensorHalThread;
+using android::dvr::SensorNdkThread;
+using android::dvr::SensorService;
+using android::dvr::SensorThread;
+using android::pdx::Service;
+using android::pdx::ServiceDispatcher;
+using android::dvr::SensordExtension;
+
+int main(int, char**) {
+  ALOGI("Starting up...");
+
+  SensordExtension::run();
+
+  // We need to be able to create endpoints with full perms.
+  umask(0000);
+
+  android::ProcessState::self()->startThreadPool();
+
+  bool sensor_thread_succeeded = false;
+#ifdef SENSORD_USES_HAL
+  std::unique_ptr<SensorThread> sensor_thread(
+      new SensorHalThread(&sensor_thread_succeeded));
+#else
+  std::unique_ptr<SensorThread> sensor_thread(
+      new SensorNdkThread(&sensor_thread_succeeded));
+#endif
+
+  if (!sensor_thread_succeeded) {
+    ALOGE("ERROR: Failed to initialize SensorThread! No 3DoF!\n");
+  }
+
+  if (sensor_thread->GetSensorCount() == 0)
+    ALOGW("No sensors found\n");
+
+  auto sensor_service = SensorService::Create(sensor_thread.get());
+  if (!sensor_service) {
+    ALOGE("TERMINATING: failed to create SensorService!!!\n");
+    return -1;
+  }
+
+  auto pose_service = PoseService::Create(sensor_thread.get());
+  if (!pose_service) {
+    ALOGE("TERMINATING: failed to create PoseService!!!\n");
+    return -1;
+  }
+
+  std::unique_ptr<ServiceDispatcher> dispatcher =
+      android::pdx::default_transport::ServiceDispatcher::Create();
+  if (!dispatcher) {
+    ALOGE("TERMINATING: failed to create ServiceDispatcher!!!\n");
+    return -1;
+  }
+
+  dispatcher->AddService(sensor_service);
+  dispatcher->AddService(pose_service);
+
+  sensor_thread->StartPolling([sensor_service, pose_service](
+      const sensors_event_t* events_begin, const sensors_event_t* events_end) {
+    sensor_service->EnqueueEvents(events_begin, events_end);
+    pose_service->HandleEvents(events_begin, events_end);
+  });
+
+  const int priority_error = dvrSetSchedulerClass(0, "sensors:low");
+  LOG_ALWAYS_FATAL_IF(priority_error < 0,
+                      "SensorService: Failed to set scheduler class: %s",
+                      strerror(-priority_error));
+
+  int ret = dispatcher->EnterDispatchLoop();
+  ALOGI("Dispatch loop exited because: %s\n", strerror(-ret));
+
+  return ret;
+}
diff --git a/services/vr/sensord/sensord.rc b/services/vr/sensord/sensord.rc
new file mode 100644
index 0000000..36cd377
--- /dev/null
+++ b/services/vr/sensord/sensord.rc
@@ -0,0 +1,11 @@
+on init
+  mkdir /dev/socket/pdx/system/vr/pose 0775 system system
+  mkdir /dev/socket/pdx/system/vr/sensors 0775 system system
+
+service sensord /system/bin/sensord
+  class core
+  user system
+  group system camera sdcard_rw
+  writepid /dev/cpuset/system/tasks
+  socket pdx/system/vr/sensors/client stream 0666 system system
+  socket pdx/system/vr/pose/client stream 0666 system system
diff --git a/services/vr/sensord/sensord_extension.cpp b/services/vr/sensord/sensord_extension.cpp
new file mode 100644
index 0000000..6cd7db3
--- /dev/null
+++ b/services/vr/sensord/sensord_extension.cpp
@@ -0,0 +1,4 @@
+#include "sensord_extension.h"
+
+void android::dvr::SensordExtension::run() {
+}
diff --git a/services/vr/sensord/sensord_extension.h b/services/vr/sensord/sensord_extension.h
new file mode 100644
index 0000000..e553eed
--- /dev/null
+++ b/services/vr/sensord/sensord_extension.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_DVR_SENSORD_EXTENSION_H_
+#define ANDROID_DVR_SENSORD_EXTENSION_H_
+
+namespace android {
+namespace dvr {
+
+// Allows sensord to be extended with additional code.
+class SensordExtension {
+ public:
+  static void run();
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_SENSORD_EXTENSION_H_
diff --git a/services/vr/sensord/test/poselatencytest.cpp b/services/vr/sensord/test/poselatencytest.cpp
new file mode 100644
index 0000000..615fc75
--- /dev/null
+++ b/services/vr/sensord/test/poselatencytest.cpp
@@ -0,0 +1,87 @@
+#include <dvr/pose_client.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <vector>
+
+// Creates a pose client and polls 30x for new data. Prints timestamp and
+// latency.  Latency is calculated based on the difference between the
+// current clock and the timestamp from the Myriad, which has been synced
+// to QC time. Note that there is some clock drift and clocks are only sycned
+// when the FW is loaded.
+int main(int /*argc*/, char** /*argv*/) {
+  DvrPose* pose_client = dvrPoseCreate();
+  if (pose_client == nullptr) {
+    printf("Unable to create pose client\n");
+    return -1;
+  }
+
+  DvrPoseAsync last_state;
+  DvrPoseAsync current_state;
+  last_state.timestamp_ns = 0;
+  current_state.timestamp_ns = 0;
+
+  double avg_latency = 0;
+  double min_latency = (float)UINT64_MAX;
+  double max_latency = 0;
+  double std = 0;
+  std::vector<uint64_t> latency;
+
+  int num_samples = 100;
+  for (int i = 0; i < num_samples; ++i) {
+    while (last_state.timestamp_ns == current_state.timestamp_ns) {
+      uint32_t vsync_count = dvrPoseGetVsyncCount(pose_client);
+      int err = dvrPoseGet(pose_client, vsync_count, &current_state);
+      if (err) {
+        printf("Error polling pose: %d\n", err);
+        dvrPoseDestroy(pose_client);
+        return err;
+      }
+    }
+    struct timespec timespec;
+    uint64_t timestamp, diff;
+    clock_gettime(CLOCK_MONOTONIC, &timespec);
+    timestamp =
+        ((uint64_t)timespec.tv_sec * 1000000000) + (uint64_t)timespec.tv_nsec;
+    if (timestamp < current_state.timestamp_ns) {
+      printf("ERROR: excessive clock drift detected, reload FW to resync\n");
+      return -1;
+    }
+    diff = timestamp - current_state.timestamp_ns;
+    printf("%02d) ts = %" PRIu64 " time = %" PRIu64 "\n", i + 1,
+           current_state.timestamp_ns, timestamp);
+    printf("\tlatency: %" PRIu64 " ns (%" PRIu64 " us) (%" PRIu64 " ms)\n",
+           diff, diff / 1000, diff / 1000000);
+
+    avg_latency += diff;
+    if (diff < min_latency) {
+      min_latency = diff;
+    }
+    if (diff > max_latency) {
+      max_latency = diff;
+    }
+    latency.push_back(diff);
+
+    last_state = current_state;
+  }
+  avg_latency /= num_samples;
+  for (unsigned int i = 0; i < latency.size(); i++) {
+    std += pow(latency[i] - avg_latency, 2);
+  }
+  std /= latency.size();
+  std = sqrt(std);
+
+  printf("\n************************\n");
+  printf("Avg latency =  %lf ns (%lf us) (%lf ms)\n", avg_latency,
+         avg_latency / 1000, avg_latency / 1000000);
+  printf("Max latency =  %lf ns (%lf us) (%lf ms)\n", max_latency,
+         max_latency / 1000, max_latency / 1000000);
+  printf("Min latency =  %lf ns (%lf us) (%lf ms)\n", min_latency,
+         min_latency / 1000, min_latency / 1000000);
+  printf("Standard dev = %lf ns (%lf us) (%lf ms)\n", std, std / 1000,
+         std / 1000000);
+  printf("\n************************\n");
+  return 0;
+}
diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp
new file mode 100644
index 0000000..c8bc884
--- /dev/null
+++ b/services/vr/virtual_touchpad/Android.bp
@@ -0,0 +1,117 @@
+
+
+// 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\"",
+        "-DSELINUX_ACCESS_CONTROL",
+    ],
+    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..ee09d48
--- /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..191bcfb
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -0,0 +1,139 @@
+#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();
+#ifdef SELINUX_ACCESS_CONTROL
+  return true;
+#else
+  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;
+#endif
+}
+
+}  // 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..24cfdf8
--- /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/services/vr/vr_window_manager/Android.bp b/services/vr/vr_window_manager/Android.bp
new file mode 100644
index 0000000..d7ddba1
--- /dev/null
+++ b/services/vr/vr_window_manager/Android.bp
@@ -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.
+
+native_src = [
+    "application.cpp",
+    "controller_mesh.cpp",
+    "elbow_model.cpp",
+    "hwc_callback.cpp",
+    "reticle.cpp",
+    "shell_view.cpp",
+    "surface_flinger_view.cpp",
+    "texture.cpp",
+    "vr_window_manager.cpp",
+    "vr_window_manager_binder.cpp",
+    "aidl/android/service/vr/IVrWindowManager.aidl",
+    "display_view.cpp",
+]
+
+static_libs = [
+    "libdisplay",
+    "libbufferhub",
+    "libbufferhubqueue",
+    "libeds",
+    "libdvrgraphics",
+    "libdvrcommon",
+    "libhwcomposer-client",
+    "libvrsensor",
+    "libperformance",
+    "libpdx_default_transport",
+    "libcutils",
+    "libvr_hwc-binder",
+    "libvr_manager",
+    "libvirtualtouchpadclient",
+]
+
+shared_libs = [
+    "android.frameworks.vr.composer@1.0",
+    "android.hardware.graphics.composer@2.1",
+    "libbase",
+    "libbinder",
+    "libinput",
+    "libhardware",
+    "libhwbinder",
+    "libsync",
+    "libutils",
+    "libgui",
+    "libEGL",
+    "libGLESv2",
+    "libvulkan",
+    "libsync",
+    "libui",
+    "libhidlbase",
+    "libhidltransport",
+    "liblog",
+    "libvr_hwc-hal",
+]
+
+cc_binary {
+    srcs: native_src,
+    static_libs: static_libs,
+    shared_libs: shared_libs,
+    cflags: ["-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", "-DLOG_TAG=\"VrWindowManager\""],
+    host_ldlibs: ["-llog"],
+    name: "vr_wm",
+    tags: ["optional"],
+    init_rc: ["vr_wm.rc"],
+}
+
+cmd_src = [
+    "vr_wm_ctl.cpp",
+    "aidl/android/service/vr/IVrWindowManager.aidl",
+]
+
+staticLibs = ["libcutils"]
+
+sharedLibs = [
+    "libbase",
+    "libbinder",
+    "libutils",
+]
+
+cc_binary {
+    srcs: cmd_src,
+    static_libs: staticLibs,
+    shared_libs: sharedLibs,
+    cppflags: ["-std=c++11"],
+    cflags: ["-DLOG_TAG=\"vrwmctl\""],
+    host_ldlibs: ["-llog"],
+    name: "vr_wm_ctl",
+    tags: ["optional"],
+}
diff --git a/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl b/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl
new file mode 100644
index 0000000..b16049f
--- /dev/null
+++ b/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl
@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+package android.service.vr;
+
+/** @hide */
+interface IVrWindowManager {
+    const String SERVICE_NAME = "vr_window_manager";
+    void connectController(in FileDescriptor fd) = 0;
+    void disconnectController() = 1;
+    void enterVrMode() = 2;
+    void exitVrMode() = 3;
+    void setDebugMode(int mode) = 4;
+    void set2DMode(int mode) = 5;
+    void setRotation(int angle) = 6;
+}
+
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
new file mode 100644
index 0000000..8b4460a
--- /dev/null
+++ b/services/vr/vr_window_manager/application.cpp
@@ -0,0 +1,330 @@
+#include "application.h"
+
+#include <inttypes.h>
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <binder/IServiceManager.h>
+#include <dvr/graphics.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <gui/ISurfaceComposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+Application::Application() {
+  vr_mode_listener_ = new VrModeListener(this);
+}
+
+Application::~Application() {
+  sp<IVrManager> vrManagerService = interface_cast<IVrManager>(
+      defaultServiceManager()->getService(String16("vrmanager")));
+  if (vrManagerService.get()) {
+    vrManagerService->unregisterPersistentVrStateListener(vr_mode_listener_);
+  }
+}
+
+int Application::Initialize() {
+  dvrSetCpuPartition(0, "/application/performance");
+
+  bool is_right_handed = true;  // TODO: retrieve setting from system
+  elbow_model_.Enable(ElbowModel::kDefaultNeckPosition, is_right_handed);
+  last_frame_time_ = std::chrono::system_clock::now();
+
+  sp<IVrManager> vrManagerService = interface_cast<IVrManager>(
+      defaultServiceManager()->getService(String16("vrmanager")));
+  if (vrManagerService.get()) {
+    vrManagerService->registerPersistentVrStateListener(vr_mode_listener_);
+  }
+  return 0;
+}
+
+int Application::AllocateResources() {
+  int surface_width = 0, surface_height = 0;
+  DvrLensInfo lens_info = {};
+  GLuint texture_id = 0;
+  GLenum texture_target = 0;
+  std::vector<DvrSurfaceParameter> surface_params = {
+    DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+    DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+    DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &lens_info.inter_lens_meters),
+    DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, &lens_info.left_fov),
+    DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, &lens_info.right_fov),
+    DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_TYPE, &texture_target),
+    DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_ID, &texture_id),
+    DVR_SURFACE_PARAMETER_IN(VISIBLE, 0),
+    DVR_SURFACE_PARAMETER_IN(Z_ORDER, 1),
+    DVR_SURFACE_PARAMETER_IN(GEOMETRY, DVR_SURFACE_GEOMETRY_SINGLE),
+    DVR_SURFACE_PARAMETER_IN(ENABLE_LATE_LATCH, 0),
+    DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, 0),
+    DVR_SURFACE_PARAMETER_LIST_END,
+  };
+
+  int ret = dvrGraphicsContextCreate(surface_params.data(), &graphics_context_);
+  if (ret)
+    return ret;
+
+  GLuint fbo = 0;
+  GLuint depth_stencil_buffer = 0;
+  GLuint samples = 1;
+  glGenFramebuffers(1, &fbo);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+  glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                       texture_target, texture_id, 0, samples);
+
+  glGenRenderbuffers(1, &depth_stencil_buffer);
+  glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer);
+  glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
+                                   GL_DEPTH_COMPONENT24, surface_width,
+                                   surface_height);
+
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                            GL_RENDERBUFFER, depth_stencil_buffer);
+
+  ALOGI("Surface size=%dx%d", surface_width, surface_height);
+  pose_client_ = dvrPoseCreate();
+  if (!pose_client_)
+    return 1;
+
+  vec2i eye_size(surface_width / 2, surface_height);
+
+  eye_viewport_[0] = Range2i::FromSize(vec2i(0, 0), eye_size);
+  eye_viewport_[1] = Range2i::FromSize(vec2i(surface_width / 2, 0), eye_size);
+
+  eye_from_head_[0] = Eigen::Translation3f(
+      vec3(lens_info.inter_lens_meters * 0.5f, 0.0f, 0.0f));
+  eye_from_head_[1] = Eigen::Translation3f(
+      vec3(-lens_info.inter_lens_meters * 0.5f, 0.0f, 0.0f));
+
+  fov_[0] = FieldOfView(lens_info.left_fov[0], lens_info.left_fov[1],
+                        lens_info.left_fov[2], lens_info.left_fov[3]);
+  fov_[1] = FieldOfView(lens_info.right_fov[0], lens_info.right_fov[1],
+                        lens_info.right_fov[2], lens_info.right_fov[3]);
+
+  return 0;
+}
+
+void Application::DeallocateResources() {
+  if (graphics_context_)
+    dvrGraphicsContextDestroy(graphics_context_);
+  graphics_context_ = nullptr;
+
+  if (pose_client_)
+    dvrPoseDestroy(pose_client_);
+
+  initialized_ = false;
+}
+
+void Application::ProcessTasks(const std::vector<MainThreadTask>& tasks) {
+  for (auto task : tasks) {
+    switch (task) {
+      case MainThreadTask::EnableDebugMode:
+        if (!debug_mode_) {
+          debug_mode_ = true;
+          SetVisibility(debug_mode_);
+        }
+        break;
+      case MainThreadTask::DisableDebugMode:
+        if (debug_mode_) {
+          debug_mode_ = false;
+          SetVisibility(debug_mode_);
+        }
+        break;
+      case MainThreadTask::EnteringVrMode:
+        if (!initialized_) {
+          LOG_ALWAYS_FATAL_IF(AllocateResources(),
+                              "Failed to allocate resources");
+        }
+        break;
+      case MainThreadTask::ExitingVrMode:
+        if (initialized_)
+          DeallocateResources();
+        break;
+      case MainThreadTask::Show:
+        if (!is_visible_)
+          SetVisibility(true);
+        break;
+    }
+  }
+}
+
+void Application::DrawFrame() {
+  // Thread should block if we are invisible or not fully initialized.
+  std::unique_lock<std::mutex> lock(mutex_);
+  wake_up_init_and_render_.wait(lock, [this]() {
+    return (is_visible_ && initialized_) || !main_thread_tasks_.empty();
+  });
+
+  // Process main thread tasks if there are any.
+  std::vector<MainThreadTask> tasks;
+  tasks.swap(main_thread_tasks_);
+  lock.unlock();
+
+  if (!tasks.empty())
+    ProcessTasks(tasks);
+
+  if (!initialized_)
+    return;
+
+  // TODO(steventhomas): If we're not visible, block until we are. For now we
+  // throttle by calling dvrGraphicsWaitNextFrame.
+  DvrFrameSchedule schedule;
+  int status = dvrGraphicsWaitNextFrame(graphics_context_, 0, &schedule);
+  if (status < 0) {
+    ALOGE("Context lost, deallocating graphics resources");
+    SetVisibility(false);
+    DeallocateResources();
+  }
+
+  OnDrawFrame();
+
+  if (is_visible_) {
+    ProcessControllerInput();
+
+    DvrPoseAsync pose;
+    dvrPoseGet(pose_client_, schedule.vsync_count, &pose);
+    last_pose_ = Posef(
+        quat(pose.orientation[3], pose.orientation[0], pose.orientation[1],
+             pose.orientation[2]),
+        vec3(pose.translation[0], pose.translation[1], pose.translation[2]));
+
+    std::chrono::time_point<std::chrono::system_clock> now =
+        std::chrono::system_clock::now();
+    double delta =
+        std::chrono::duration<double>(now - last_frame_time_).count();
+    last_frame_time_ = now;
+
+    if (delta > 1.0f)
+      delta = 0.05f;
+
+    fade_value_ += delta / 0.25f;
+    if (fade_value_ > 1.0f)
+      fade_value_ = 1.0f;
+
+    controller_position_ =
+        elbow_model_.Update(delta, last_pose_.GetRotation(),
+                            controller_orientation_, should_recenter_);
+
+    dvrBeginRenderFrameEds(graphics_context_, pose.orientation,
+                           pose.translation);
+
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    mat4 head_matrix = last_pose_.GetObjectFromReferenceMatrix();
+    glViewport(eye_viewport_[kLeftEye].GetMinPoint()[0],
+               eye_viewport_[kLeftEye].GetMinPoint()[1],
+               eye_viewport_[kLeftEye].GetSize()[0],
+               eye_viewport_[kLeftEye].GetSize()[1]);
+    DrawEye(kLeftEye, fov_[kLeftEye].GetProjectionMatrix(0.1f, 500.0f),
+            eye_from_head_[kLeftEye], head_matrix);
+
+    glViewport(eye_viewport_[kRightEye].GetMinPoint()[0],
+               eye_viewport_[kRightEye].GetMinPoint()[1],
+               eye_viewport_[kRightEye].GetSize()[0],
+               eye_viewport_[kRightEye].GetSize()[1]);
+    DrawEye(kRightEye, fov_[kRightEye].GetProjectionMatrix(0.1f, 500.0f),
+            eye_from_head_[kRightEye], head_matrix);
+
+    OnEndFrame();
+
+    dvrPresent(graphics_context_);
+    should_recenter_ = false;
+  }
+}
+
+void Application::ProcessControllerInput() {
+  if (controller_data_provider_) {
+    shmem_controller_active_ = false;
+    const void* data = controller_data_provider_->LockControllerData();
+    // TODO(kpschoedel): define wire format.
+    if (data) {
+      struct wire_format {
+        uint32_t version;
+        uint32_t timestamph;
+        uint32_t timestampl;
+        uint32_t quat_count;
+        float q[4];
+        uint32_t buttonsh;
+        uint32_t buttonsl;
+      } __attribute__((__aligned__(32)));
+      const wire_format* wire_data = static_cast<const wire_format*>(data);
+      static uint64_t last_timestamp = 0;
+      if (wire_data->version == 1) {
+        shmem_controller_active_ = true;
+        uint64_t timestamp =
+            (((uint64_t)wire_data->timestamph) << 32) | wire_data->timestampl;
+        if (timestamp == last_timestamp) {
+          static uint64_t last_logged_timestamp = 0;
+          if (last_logged_timestamp != last_timestamp) {
+            last_logged_timestamp = last_timestamp;
+            ALOGI("Controller shmem stale T=0x%" PRIX64, last_timestamp);
+          }
+        } else {
+          last_timestamp = timestamp;
+          controller_orientation_ = quat(wire_data->q[3], wire_data->q[0],
+                                         wire_data->q[1], wire_data->q[2]);
+          shmem_controller_buttons_ =
+              (((uint64_t)wire_data->buttonsh) << 32) | wire_data->buttonsl;
+        }
+      } else if (wire_data->version == 0xFEEDFACE) {
+        static bool logged_init = false;
+        if (!logged_init) {
+          logged_init = true;
+          ALOGI("Controller shmem waiting for data");
+        }
+      }
+    }
+    controller_data_provider_->UnlockControllerData();
+    if (shmem_controller_active_) {
+      ALOGV("Controller shmem orientation: %f %f %f %f",
+            controller_orientation_.x(), controller_orientation_.y(),
+            controller_orientation_.z(), controller_orientation_.w());
+      if (shmem_controller_buttons_) {
+        ALOGV("Controller shmem buttons: %017" PRIX64,
+            shmem_controller_buttons_);
+      }
+    }
+  }
+}
+
+void Application::SetVisibility(bool visible) {
+  if (visible && !initialized_) {
+    if (AllocateResources())
+      ALOGE("Failed to allocate resources");
+  }
+
+  bool changed = is_visible_ != visible;
+  if (changed) {
+    is_visible_ = visible;
+    dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
+    OnVisibilityChanged(is_visible_);
+  }
+}
+
+void Application::OnVisibilityChanged(bool visible) {
+  if (visible) {
+    fade_value_ = 0;
+    // We have been sleeping so to ensure correct deltas, reset the time.
+    last_frame_time_ = std::chrono::system_clock::now();
+  }
+}
+
+void Application::QueueTask(MainThreadTask task) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  main_thread_tasks_.push_back(task);
+  wake_up_init_and_render_.notify_one();
+}
+
+void Application::VrModeListener::onPersistentVrStateChanged(bool enabled) {
+  if (!enabled)
+    app_->QueueTask(MainThreadTask::ExitingVrMode);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/application.h b/services/vr/vr_window_manager/application.h
new file mode 100644
index 0000000..ed99157
--- /dev/null
+++ b/services/vr/vr_window_manager/application.h
@@ -0,0 +1,111 @@
+#ifndef VR_WINDOW_MANAGER_APPLICATION_H_
+#define VR_WINDOW_MANAGER_APPLICATION_H_
+
+#include <memory>
+#include <private/dvr/types.h>
+#include <stdint.h>
+#include <vr/vr_manager/vr_manager.h>
+
+#include <chrono>
+#include <mutex>
+#include <vector>
+
+#include "controller_data_provider.h"
+#include "elbow_model.h"
+
+struct DvrGraphicsContext;
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class Application {
+ public:
+  Application();
+  virtual ~Application();
+
+  virtual int Initialize();
+
+  virtual int AllocateResources();
+  virtual void DeallocateResources();
+
+  void DrawFrame();
+
+  void SetControllerDataProvider(ControllerDataProvider* provider) {
+    controller_data_provider_ = provider;
+  }
+
+ protected:
+  enum class MainThreadTask {
+    EnteringVrMode,
+    ExitingVrMode,
+    EnableDebugMode,
+    DisableDebugMode,
+    Show,
+  };
+
+  class VrModeListener : public BnPersistentVrStateCallbacks {
+   public:
+    VrModeListener(Application *app) : app_(app) {}
+    void onPersistentVrStateChanged(bool enabled) override;
+
+   private:
+    Application *app_;
+  };
+
+  sp<VrModeListener> vr_mode_listener_;
+  virtual void OnDrawFrame() = 0;
+  virtual void DrawEye(EyeType eye, const mat4& perspective,
+                       const mat4& eye_matrix, const mat4& head_matrix) = 0;
+  virtual void OnEndFrame() = 0;
+
+  void SetVisibility(bool visible);
+  virtual void OnVisibilityChanged(bool visible);
+
+  void ProcessControllerInput();
+
+  void ProcessTasks(const std::vector<MainThreadTask>& tasks);
+
+  void QueueTask(MainThreadTask task);
+
+  DvrGraphicsContext* graphics_context_ = nullptr;
+  DvrPose* pose_client_ = nullptr;
+
+  Range2i eye_viewport_[2];
+  mat4 eye_from_head_[2];
+  FieldOfView fov_[2];
+  Posef last_pose_;
+
+  quat controller_orientation_;
+  bool shmem_controller_active_ = false;
+  uint64_t shmem_controller_buttons_;
+
+  // Used to center the scene when the shell becomes visible.
+  bool should_recenter_ = true;
+
+  bool is_visible_ = false;
+  std::chrono::time_point<std::chrono::system_clock> visibility_button_press_;
+  bool debug_mode_ = false;
+
+  std::chrono::time_point<std::chrono::system_clock> last_frame_time_;
+  vec3 controller_position_;
+  ElbowModel elbow_model_;
+
+  float fade_value_ = 0;
+
+  std::mutex mutex_;
+  std::condition_variable wake_up_init_and_render_;
+  bool initialized_ = false;
+  std::vector<MainThreadTask> main_thread_tasks_;
+
+  // Controller data provider from shared memory buffer.
+  ControllerDataProvider* controller_data_provider_ = nullptr;
+
+  Application(const Application&) = delete;
+  void operator=(const Application&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_APPLICATION_H_
diff --git a/services/vr/vr_window_manager/controller_data_provider.h b/services/vr/vr_window_manager/controller_data_provider.h
new file mode 100644
index 0000000..bc1450c
--- /dev/null
+++ b/services/vr/vr_window_manager/controller_data_provider.h
@@ -0,0 +1,19 @@
+#ifndef VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
+#define VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
+
+namespace android {
+namespace dvr {
+
+class ControllerDataProvider {
+ public:
+  virtual ~ControllerDataProvider() {}
+  // Returns data pointer or nullptr. If pointer is valid, call to
+  // UnlockControllerData is required.
+  virtual const void* LockControllerData() = 0;
+  virtual void UnlockControllerData() = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
\ No newline at end of file
diff --git a/services/vr/vr_window_manager/controller_mesh.cpp b/services/vr/vr_window_manager/controller_mesh.cpp
new file mode 100644
index 0000000..c6095b1
--- /dev/null
+++ b/services/vr/vr_window_manager/controller_mesh.cpp
@@ -0,0 +1,75 @@
+#include "controller_mesh.h"
+
+namespace android {
+namespace dvr {
+
+const int kNumControllerMeshVertices = 60;
+
+// Vertices in position.xyz, normal.xyz, uv.xy oder.
+// Generated from an .obj mesh.
+const float kControllerMeshVertices[] = {
+    0.002023,  0.001469,  -0.5, 0.809016,  0.587787,  0, 0,   0,
+    0.000773,  0.002378,  -0.5, 0.309004,  0.951061,  0, 0.1, 0,
+    0.000773,  0.002378,  0,    0.309004,  0.951061,  0, 0.1, 1,
+    0.002023,  0.001469,  -0.5, 0.809016,  0.587787,  0, 0,   0,
+    0.000773,  0.002378,  0,    0.309004,  0.951061,  0, 0.1, 1,
+    0.002023,  0.001469,  0,    0.809016,  0.587787,  0, 0,   1,
+    0.000773,  0.002378,  -0.5, 0.309004,  0.951061,  0, 0.1, 0,
+    -0.000773, 0.002378,  -0.5, -0.309004, 0.951061,  0, 0.2, 0,
+    -0.000773, 0.002378,  0,    -0.309004, 0.951061,  0, 0.2, 1,
+    0.000773,  0.002378,  -0.5, 0.309004,  0.951061,  0, 0.1, 0,
+    -0.000773, 0.002378,  0,    -0.309004, 0.951061,  0, 0.2, 1,
+    0.000773,  0.002378,  0,    0.309004,  0.951061,  0, 0.1, 1,
+    -0.000773, 0.002378,  -0.5, -0.309004, 0.951061,  0, 0.2, 0,
+    -0.002023, 0.001469,  -0.5, -0.809016, 0.587787,  0, 0.3, 0,
+    -0.002023, 0.001469,  0,    -0.809016, 0.587787,  0, 0.3, 1,
+    -0.000773, 0.002378,  -0.5, -0.309004, 0.951061,  0, 0.2, 0,
+    -0.002023, 0.001469,  0,    -0.809016, 0.587787,  0, 0.3, 1,
+    -0.000773, 0.002378,  0,    -0.309004, 0.951061,  0, 0.2, 1,
+    -0.002023, 0.001469,  -0.5, -0.809016, 0.587787,  0, 0.3, 0,
+    -0.0025,   0,         -0.5, -1,        -0,        0, 0.4, 0,
+    -0.0025,   0,         0,    -1,        -0,        0, 0.4, 1,
+    -0.002023, 0.001469,  -0.5, -0.809016, 0.587787,  0, 0.3, 0,
+    -0.0025,   0,         0,    -1,        -0,        0, 0.4, 1,
+    -0.002023, 0.001469,  0,    -0.809016, 0.587787,  0, 0.3, 1,
+    -0.0025,   0,         -0.5, -1,        -0,        0, 0.4, 0,
+    -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+    -0.002023, -0.001469, 0,    -0.809016, -0.587787, 0, 0.5, 1,
+    -0.0025,   0,         -0.5, -1,        -0,        0, 0.4, 0,
+    -0.002023, -0.001469, 0,    -0.809016, -0.587787, 0, 0.5, 1,
+    -0.0025,   0,         0,    -1,        -0,        0, 0.4, 1,
+    -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+    -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+    -0.000773, -0.002378, 0,    -0.309004, -0.951061, 0, 0.6, 1,
+    -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+    -0.000773, -0.002378, 0,    -0.309004, -0.951061, 0, 0.6, 1,
+    -0.002023, -0.001469, 0,    -0.809016, -0.587787, 0, 0.5, 1,
+    -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+    0.000773,  -0.002378, -0.5, 0.309004,  -0.951061, 0, 0.7, 0,
+    0.000773,  -0.002378, 0,    0.309004,  -0.951061, 0, 0.7, 1,
+    -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+    0.000773,  -0.002378, 0,    0.309004,  -0.951061, 0, 0.7, 1,
+    -0.000773, -0.002378, 0,    -0.309004, -0.951061, 0, 0.6, 1,
+    0.000773,  -0.002378, -0.5, 0.309004,  -0.951061, 0, 0.7, 0,
+    0.002023,  -0.001469, -0.5, 0.809016,  -0.587787, 0, 0.8, 0,
+    0.002023,  -0.001469, 0,    0.809016,  -0.587787, 0, 0.8, 1,
+    0.000773,  -0.002378, -0.5, 0.309004,  -0.951061, 0, 0.7, 0,
+    0.002023,  -0.001469, 0,    0.809016,  -0.587787, 0, 0.8, 1,
+    0.000773,  -0.002378, 0,    0.309004,  -0.951061, 0, 0.7, 1,
+    0.002023,  -0.001469, -0.5, 0.809016,  -0.587787, 0, 0.8, 0,
+    0.0025,    0,         -0.5, 1,         0,         0, 0.9, 0,
+    0.0025,    0,         0,    1,         0,         0, 0.9, 1,
+    0.002023,  -0.001469, -0.5, 0.809016,  -0.587787, 0, 0.8, 0,
+    0.0025,    0,         0,    1,         0,         0, 0.9, 1,
+    0.002023,  -0.001469, 0,    0.809016,  -0.587787, 0, 0.8, 1,
+    0.0025,    0,         -0.5, 1,         0,         0, 0.9, 0,
+    0.002023,  0.001469,  -0.5, 0.809016,  0.587787,  0, 1,   0,
+    0.002023,  0.001469,  0,    0.809016,  0.587787,  0, 1,   1,
+    0.0025,    0,         -0.5, 1,         0,         0, 0.9, 0,
+    0.002023,  0.001469,  0,    0.809016,  0.587787,  0, 1,   1,
+    0.0025,    0,         0,    1,         0,         0, 0.9, 1,
+
+};
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/controller_mesh.h b/services/vr/vr_window_manager/controller_mesh.h
new file mode 100644
index 0000000..88872c7
--- /dev/null
+++ b/services/vr/vr_window_manager/controller_mesh.h
@@ -0,0 +1,13 @@
+#ifndef VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
+#define VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
+
+namespace android {
+namespace dvr {
+
+extern const int kNumControllerMeshVertices;
+extern const float kControllerMeshVertices[];
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
diff --git a/services/vr/vr_window_manager/display_view.cpp b/services/vr/vr_window_manager/display_view.cpp
new file mode 100644
index 0000000..88768a0
--- /dev/null
+++ b/services/vr/vr_window_manager/display_view.cpp
@@ -0,0 +1,458 @@
+#include "display_view.h"
+
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr float kLayerScaleFactor = 3.0f;
+constexpr unsigned int kMaximumPendingFrames = 8;
+constexpr uint32_t kSystemId = 1000;
+
+// clang-format off
+const GLfloat kVertices[] = {
+  -1, -1, 0,
+   1, -1, 0,
+  -1, 1, 0,
+   1, 1, 0,
+};
+
+const GLfloat kTextureVertices[] = {
+  0, 1,
+  1, 1,
+  0, 0,
+  1, 0,
+};
+// clang-format on
+
+// Returns true if the given point is inside the given rect.
+bool IsInside(const vec2& pt, const vec2& tl, const vec2& br) {
+  return pt.x() >= tl.x() && pt.x() <= br.x() && pt.y() >= tl.y() &&
+         pt.y() <= br.y();
+}
+
+mat4 GetScalingMatrix(float width, float height) {
+  float xscale = 1, yscale = 1;
+  float ar = width / height;
+  if (ar > 1)
+    yscale = 1.0 / ar;
+  else
+    xscale = ar;
+
+  xscale *= kLayerScaleFactor;
+  yscale *= kLayerScaleFactor;
+
+  return mat4(Eigen::Scaling<float>(xscale, yscale, 1.0));
+}
+
+// Helper function that applies the crop transform to the texture layer and
+// positions (and scales) the texture layer in the appropriate location in the
+// display space.
+mat4 GetLayerTransform(const TextureLayer& texture_layer, float display_width,
+                       float display_height) {
+  // Map from vertex coordinates to [0, 1] coordinates:
+  //  1) Flip y since in vertex coordinates (-1, -1) is at the bottom left and
+  //     in texture coordinates (0, 0) is at the top left.
+  //  2) Translate by (1, 1) to map vertex coordinates to [0, 2] on x and y.
+  //  3) Scale by 1 / 2 to map coordinates to [0, 1] on x  and y.
+  mat4 unit_space(Eigen::AlignedScaling3f(0.5f, 0.5f, 1.0f) *
+                  Eigen::Translation3f(1.0f, 1.0f, 0.0f) *
+                  Eigen::AlignedScaling3f(1.0f, -1.0f, 1.0f));
+
+  mat4 texture_space(Eigen::AlignedScaling3f(
+      texture_layer.texture->width(), texture_layer.texture->height(), 1.0f));
+
+  // 1) Translate the layer to crop the left and top edge.
+  // 2) Scale the layer such that the cropped right and bottom edges map outside
+  //    the exture region.
+  float crop_width = texture_layer.crop.right - texture_layer.crop.left;
+  float crop_height = texture_layer.crop.bottom - texture_layer.crop.top;
+  mat4 texture_crop(Eigen::AlignedScaling3f(
+                        texture_layer.texture->width() / crop_width,
+                        texture_layer.texture->height() / crop_height, 1.0f) *
+                    Eigen::Translation3f(-texture_layer.crop.left,
+                                         -texture_layer.crop.top, 0.0f));
+
+  mat4 display_space(
+      Eigen::AlignedScaling3f(display_width, display_height, 1.0f));
+
+  // 1) Scale the texture to fit the display frame.
+  // 2) Translate the texture in the display frame location.
+  float display_frame_width =
+      texture_layer.display_frame.right - texture_layer.display_frame.left;
+  float display_frame_height =
+      texture_layer.display_frame.bottom - texture_layer.display_frame.top;
+  mat4 display_frame(
+      Eigen::Translation3f(texture_layer.display_frame.left,
+                           texture_layer.display_frame.top, 0.0f) *
+      Eigen::AlignedScaling3f(display_frame_width / display_width,
+                              display_frame_height / display_height, 1.0f));
+
+  mat4 layer_transform = unit_space.inverse() * display_space.inverse() *
+                         display_frame * display_space *
+                         texture_space.inverse() * texture_crop *
+                         texture_space * unit_space;
+  return layer_transform;
+}
+
+// Determine if ths frame should be shown or hidden.
+ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
+                                            uint32_t* appid) {
+  auto& layers = frame.layers();
+
+  size_t index;
+  // Skip all layers that we don't know about.
+  for (index = 0; index < layers.size(); index++) {
+    if (layers[index].type != 0xFFFFFFFF && layers[index].type != 0)
+      break;
+  }
+
+  if (index == layers.size())
+    return ViewMode::Hidden;
+
+  if (layers[index].type != 1) {
+    // We don't have a VR app layer? Abort.
+    return ViewMode::Hidden;
+  }
+
+  if (layers[index].appid != *appid) {
+    *appid = layers[index].appid;
+    return ViewMode::App;
+  }
+
+  // This is the VR app, ignore it.
+  index++;
+
+  // Now, find a dim layer if it exists.
+  // If it does, ignore any layers behind it for visibility determination.
+  for (size_t i = index; i < layers.size(); i++) {
+    if (layers[i].appid == HwcCallback::HwcLayer::kSurfaceFlingerLayer) {
+      index = i + 1;
+    }
+  }
+
+  // If any non-skipped layers exist now then we show, otherwise hide.
+  for (size_t i = index; i < layers.size(); i++) {
+    if (!layers[i].should_skip_layer())
+      return ViewMode::VR;
+  }
+
+  return ViewMode::Hidden;
+}
+
+}  // namespace
+
+DisplayView::DisplayView(uint32_t id, int touchpad_id)
+    : id_(id), touchpad_id_(touchpad_id) {
+  translate_ = Eigen::Translation3f(0, 0, -5.0f);
+  ime_translate_ = mat4(Eigen::Translation3f(0.0f, -0.5f, 0.25f));
+  ime_top_left_ = vec2(0, 0);
+  ime_size_ = vec2(0, 0);
+  rotation_ = mat4::Identity();
+}
+
+DisplayView::~DisplayView() {}
+
+void DisplayView::Recenter(const mat4& initial) {
+  initial_head_matrix_ =
+      initial * Eigen::AngleAxisf(M_PI * 0.5f, vec3::UnitZ());
+}
+
+void DisplayView::SetPrograms(ShaderProgram* program,
+                              ShaderProgram* overlay_program) {
+  program_ = program;
+  overlay_program_ = overlay_program;
+}
+
+void DisplayView::DrawEye(EyeType /* eye */, const mat4& perspective,
+                          const mat4& eye_matrix, const mat4& head_matrix,
+                          float fade_value) {
+  scale_ = GetScalingMatrix(size_.x(), size_.y());
+
+  DrawOverlays(perspective, eye_matrix, head_matrix, fade_value);
+}
+
+void DisplayView::AdvanceFrame() {
+  if (!pending_frames_.empty()) {
+    // Check if we should advance the frame.
+    auto& frame = pending_frames_.front();
+    if (frame.visibility == ViewMode::Hidden ||
+        frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
+      current_frame_ = std::move(frame);
+      pending_frames_.pop_front();
+    }
+  }
+}
+
+void DisplayView::OnDrawFrame(SurfaceFlingerView* surface_flinger_view,
+                              bool debug_mode) {
+  textures_.clear();
+  has_ime_ = false;
+
+  if (!visible())
+    return;
+
+  surface_flinger_view->GetTextures(*current_frame_.frame.get(), &textures_,
+                                    &ime_texture_, debug_mode,
+                                    current_frame_.visibility == ViewMode::VR);
+  has_ime_ = ime_texture_.texture != nullptr;
+}
+
+base::unique_fd DisplayView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
+                                     bool debug_mode, bool is_vr_active,
+                                     bool* showing) {
+  size_ = vec2(frame->display_width(), frame->display_height());
+  uint32_t app = current_vr_app_;
+  ViewMode visibility = CalculateVisibilityFromLayerConfig(*frame.get(), &app);
+
+  if (visibility == ViewMode::Hidden && debug_mode)
+    visibility = ViewMode::VR;
+
+  if (frame->layers().empty()) {
+    current_vr_app_ = 0;
+  } else if (visibility == ViewMode::App) {
+    // This is either a VR app switch or a 2D app launching.
+    // If we can have VR apps, update if it's 0.
+    if (!always_2d_ && is_vr_active && !use_2dmode_ && app != kSystemId) {
+      visibility = ViewMode::Hidden;
+      current_vr_app_ = app;
+    }
+  } else if ((use_2dmode_ || !is_vr_active) && app != 0 &&
+             visibility == ViewMode::Hidden) {
+    // This is the case for the VR app launching a 2D intent of itself on some
+    // display.
+    visibility = ViewMode::App;
+  } else if (!current_vr_app_) {
+    // The VR app is running.
+    current_vr_app_ = app;
+  }
+
+  pending_frames_.emplace_back(std::move(frame), visibility);
+
+  if (pending_frames_.size() > kMaximumPendingFrames) {
+    pending_frames_.pop_front();
+  }
+
+  if (visibility == ViewMode::Hidden &&
+      current_frame_.visibility == ViewMode::Hidden) {
+    // Consume all frames while hidden.
+    while (!pending_frames_.empty())
+      AdvanceFrame();
+  }
+
+  // If we are showing ourselves the main thread is not processing anything,
+  // so give it a kick.
+  if (visibility != ViewMode::Hidden &&
+      current_frame_.visibility == ViewMode::Hidden) {
+    *showing = true;
+  }
+
+  return base::unique_fd(dup(release_fence_.get()));
+}
+
+bool DisplayView::IsHit(const vec3& view_location, const vec3& view_direction,
+                        vec3* hit_location, vec2* hit_location_in_window_coord,
+                        bool test_ime) {
+  mat4 m = GetStandardTransform();
+  if (test_ime)
+    m = m * ime_translate_;
+  mat4 inverse = (m * scale_).inverse();
+  vec4 transformed_loc =
+      inverse * vec4(view_location[0], view_location[1], view_location[2], 1);
+  vec4 transformed_dir = inverse * vec4(view_direction[0], view_direction[1],
+                                        view_direction[2], 0);
+
+  if (transformed_dir.z() >= 0 || transformed_loc.z() <= 0)
+    return false;
+
+  float distance = -transformed_loc.z() / transformed_dir.z();
+  vec4 transformed_hit_loc = transformed_loc + transformed_dir * distance;
+  if (transformed_hit_loc.x() < -1 || transformed_hit_loc.x() > 1)
+    return false;
+  if (transformed_hit_loc.y() < -1 || transformed_hit_loc.y() > 1)
+    return false;
+
+  hit_location_in_window_coord->x() =
+      (1 + transformed_hit_loc.x()) / 2 * size_.x();
+  hit_location_in_window_coord->y() =
+      (1 - transformed_hit_loc.y()) / 2 * size_.y();
+
+  *hit_location = view_location + view_direction * distance;
+  return true;
+}
+
+void DisplayView::DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+                               const mat4& head_matrix, float fade_value) {
+  if (textures_.empty())
+    return;
+
+  program_->Use();
+  mat4 mvp = perspective * eye_matrix * head_matrix;
+  GLint view_projection_location =
+      glGetUniformLocation(program_->GetProgram(), "uViewProjection");
+  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+  GLint alpha_location = glGetUniformLocation(program_->GetProgram(), "uAlpha");
+
+  GLint tex_location = glGetUniformLocation(program_->GetProgram(), "tex");
+  glUniform1i(tex_location, 0);
+  glActiveTexture(GL_TEXTURE0);
+
+  for (const auto& texture_layer : textures_) {
+    switch (texture_layer.blending) {
+      case HWC2_BLEND_MODE_PREMULTIPLIED:
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+        break;
+      case HWC2_BLEND_MODE_COVERAGE:
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        break;
+      default:
+        break;
+    }
+
+    glUniform1f(alpha_location, fade_value * texture_layer.alpha);
+
+    glBindTexture(GL_TEXTURE_2D, texture_layer.texture->id());
+
+    mat4 layer_transform =
+        GetLayerTransform(texture_layer, size_.x(), size_.y());
+
+    mat4 transform = GetStandardTransform() * scale_ * layer_transform;
+    DrawWithTransform(transform, *program_);
+
+    glDisable(GL_BLEND);
+  }
+
+  if (has_ime_) {
+    ime_top_left_ = vec2(static_cast<float>(ime_texture_.display_frame.left),
+                         static_cast<float>(ime_texture_.display_frame.top));
+    ime_size_ = vec2(static_cast<float>(ime_texture_.display_frame.right -
+                                        ime_texture_.display_frame.left),
+                     static_cast<float>(ime_texture_.display_frame.bottom -
+                                        ime_texture_.display_frame.top));
+
+    DrawDimOverlay(mvp, textures_[0], ime_top_left_, ime_top_left_ + ime_size_);
+
+    DrawIme();
+  }
+}
+
+void DisplayView::UpdateReleaseFence() {
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  EGLSyncKHR sync =
+      eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+  if (sync != EGL_NO_SYNC_KHR) {
+    // Need to flush in order to get the fence FD.
+    glFlush();
+    base::unique_fd fence(eglDupNativeFenceFDANDROID(display, sync));
+    eglDestroySyncKHR(display, sync);
+    release_fence_ = std::move(fence);
+  } else {
+    ALOGE("Failed to create sync fence");
+    release_fence_ = base::unique_fd();
+  }
+}
+
+mat4 DisplayView::GetStandardTransform() {
+  mat4 m = initial_head_matrix_ * rotation_ * translate_;
+  if (current_frame_.visibility == ViewMode::App)
+    m *= Eigen::AngleAxisf(M_PI * -0.5f, vec3::UnitZ());
+  return m;
+}
+
+void DisplayView::DrawIme() {
+  program_->Use();
+  glBindTexture(GL_TEXTURE_2D, ime_texture_.texture->id());
+
+  mat4 layer_transform = GetLayerTransform(ime_texture_, size_.x(), size_.y());
+
+  mat4 transform =
+      GetStandardTransform() * ime_translate_ * scale_ * layer_transform;
+
+  DrawWithTransform(transform, *program_);
+}
+
+void DisplayView::DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+                                 const vec2& top_left,
+                                 const vec2& bottom_right) {
+  overlay_program_->Use();
+  glUniformMatrix4fv(
+      glGetUniformLocation(overlay_program_->GetProgram(), "uViewProjection"),
+      1, 0, mvp.data());
+  glUniform4f(glGetUniformLocation(overlay_program_->GetProgram(), "uCoords"),
+              top_left.x() / size_.x(), top_left.y() / size_.y(),
+              bottom_right.x() / size_.x(), bottom_right.y() / size_.y());
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  mat4 layer_transform = GetLayerTransform(layer, size_.x(), size_.y());
+
+  mat4 transform = GetStandardTransform() * scale_ * layer_transform;
+  DrawWithTransform(transform, *overlay_program_);
+  glDisable(GL_BLEND);
+}
+
+void DisplayView::DrawWithTransform(const mat4& transform,
+                                    const ShaderProgram& program) {
+  GLint transform_location =
+      glGetUniformLocation(program.GetProgram(), "uTransform");
+  glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+  glEnableVertexAttribArray(0);
+  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, kVertices);
+  glEnableVertexAttribArray(1);
+  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, kTextureVertices);
+  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
+bool DisplayView::UpdateHitInfo(const vec3& view_location,
+                                const vec3& view_direction,
+                                vec3* hit_location) {
+  bool is_hit = false;
+  if (has_ime_) {
+    // This will set allow_input_ and hit_location_in_window_coord_.
+    is_hit = IsImeHit(view_location, view_direction, hit_location);
+  } else {
+    is_hit = IsHit(view_location, view_direction, hit_location,
+                   &hit_location_in_window_coord_, false);
+    allow_input_ = is_hit;
+  }
+  return is_hit;
+}
+
+bool DisplayView::IsImeHit(const vec3& view_location,
+                           const vec3& view_direction, vec3* hit_location) {
+  // First, check if the IME window is hit.
+  bool is_hit = IsHit(view_location, view_direction, hit_location,
+                      &hit_location_in_window_coord_, true);
+  if (is_hit) {
+    // If it is, check if the window coordinate is in the IME region;
+    // if so then we are done.
+    if (IsInside(hit_location_in_window_coord_, ime_top_left_,
+                 ime_top_left_ + ime_size_)) {
+      allow_input_ = true;
+      return true;
+    }
+  }
+
+  allow_input_ = false;
+  // Check if we have hit the main window.
+  is_hit = IsHit(view_location, view_direction, hit_location,
+                 &hit_location_in_window_coord_, false);
+  if (is_hit) {
+    // Only allow input if we are not hitting the region hidden by the IME.
+    // Allowing input here would cause clicks on the main window to actually
+    // be clicks on the IME.
+    if (!IsInside(hit_location_in_window_coord_, ime_top_left_,
+                  ime_top_left_ + ime_size_)) {
+      allow_input_ = true;
+    }
+  }
+  return is_hit;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/display_view.h b/services/vr/vr_window_manager/display_view.h
new file mode 100644
index 0000000..ad624c6
--- /dev/null
+++ b/services/vr/vr_window_manager/display_view.h
@@ -0,0 +1,119 @@
+#ifndef VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
+#define VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
+
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+
+#include "hwc_callback.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+enum class ViewMode {
+  Hidden,
+  VR,
+  App,
+};
+
+class DisplayView {
+ public:
+  DisplayView(uint32_t id, int touchpad_id);
+  ~DisplayView();
+
+  // Calls to these 3 functions must be synchronized.
+  base::unique_fd OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
+                          bool debug_mode, bool is_vr_active, bool* showing);
+  void AdvanceFrame();
+  void UpdateReleaseFence();
+
+  void OnDrawFrame(SurfaceFlingerView* surface_flinger_view, bool debug_mode);
+  void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
+               const mat4& head_matrix, float fade_value);
+
+  void Recenter(const mat4& initial);
+
+  bool UpdateHitInfo(const vec3& view_location, const vec3& view_direction,
+                     vec3* hit_location);
+
+  void SetPrograms(ShaderProgram* program, ShaderProgram* overlay_program);
+
+  bool visible() const { return current_frame_.visibility != ViewMode::Hidden; }
+  bool allow_input() const { return allow_input_; }
+  const vec2& hit_location() const { return hit_location_in_window_coord_; }
+  uint32_t id() const { return id_; }
+  int touchpad_id() const { return touchpad_id_; }
+  vec2 size() const { return size_; }
+
+  void set_2dmode(bool mode) { use_2dmode_ = mode; }
+  void set_always_2d(bool mode) { always_2d_ = mode; }
+
+  void set_rotation(const mat4& rotation) { rotation_ = rotation; }
+
+ private:
+  bool IsHit(const vec3& view_location, const vec3& view_direction,
+             vec3* hit_location, vec2* hit_location_in_window_coord,
+             bool test_ime);
+  bool IsImeHit(const vec3& view_location, const vec3& view_direction,
+                vec3* hit_location);
+  void DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+                    const mat4& head_matrix, float fade_value);
+  void DrawIme();
+  void DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+                      const vec2& top_left, const vec2& bottom_right);
+  void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
+
+  // This is the rotated, translated but unscaled transform to apply everywhere.
+  mat4 GetStandardTransform();
+
+  uint32_t id_;
+  int touchpad_id_;
+
+  uint32_t current_vr_app_;
+
+  ShaderProgram* program_;
+  ShaderProgram* overlay_program_;
+
+  mat4 initial_head_matrix_;
+  mat4 scale_;
+  mat4 translate_;
+  mat4 ime_translate_;
+  mat4 rotation_;
+  vec2 size_;
+
+  std::vector<TextureLayer> textures_;
+  TextureLayer ime_texture_;
+
+  bool allow_input_ = false;
+  vec2 hit_location_in_window_coord_;
+  vec2 ime_top_left_;
+  vec2 ime_size_;
+  bool has_ime_ = false;
+  bool use_2dmode_ = false;
+  bool always_2d_ = false;
+
+  struct PendingFrame {
+    PendingFrame() = default;
+    PendingFrame(std::unique_ptr<HwcCallback::Frame>&& frame,
+                 ViewMode visibility)
+        : frame(std::move(frame)), visibility(visibility) {}
+    PendingFrame(PendingFrame&& r)
+        : frame(std::move(r.frame)), visibility(r.visibility) {}
+
+    void operator=(PendingFrame&& r) {
+      frame.reset(r.frame.release());
+      visibility = r.visibility;
+    }
+
+    std::unique_ptr<HwcCallback::Frame> frame;
+    ViewMode visibility = ViewMode::Hidden;
+  };
+  std::deque<PendingFrame> pending_frames_;
+  PendingFrame current_frame_;
+  base::unique_fd release_fence_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_DISPLAY_VIEW_H_
diff --git a/services/vr/vr_window_manager/elbow_model.cpp b/services/vr/vr_window_manager/elbow_model.cpp
new file mode 100644
index 0000000..9543f17
--- /dev/null
+++ b/services/vr/vr_window_manager/elbow_model.cpp
@@ -0,0 +1,134 @@
+#include "elbow_model.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+const vec3 kControllerForearm(0.0f, 0.0f, -0.25f);
+const vec3 kControllerPosition(0.0f, 0.0f, -0.05f);
+const vec3 kLeftElbowPosition(-0.195f, -0.5f, 0.075f);
+const vec3 kLeftArmExtension(0.13f, 0.14f, -0.08f);
+const vec3 kRightElbowPosition(0.195f, -0.5f, 0.075f);
+const vec3 kRightArmExtension(-0.13f, 0.14f, -0.08f);
+constexpr float kElbowBendRatio = 0.4f;
+constexpr float kCosMaxExtensionAngle =
+    0.87f;  // Cos of 30 degrees (90-30 = 60)
+constexpr float kCosMinExtensionAngle = 0.12f;  // Cos of 83 degrees (90-83 = 7)
+constexpr float kYAxisExtensionFraction = 0.4f;
+constexpr float kMinRotationSpeed = 0.61f;  // 35 degrees in radians
+constexpr float kMinAngleDelta = 0.175f;    // 10 degrees in radians
+
+float clamp(float v, float min, float max) {
+  if (v < min)
+    return min;
+  if (v > max)
+    return max;
+  return v;
+}
+
+float NormalizeAngle(float angle) {
+  if (angle > M_PI)
+    angle = 2.0f * M_PI - angle;
+  return angle;
+}
+
+}  // namespace
+
+const vec3 ElbowModel::kDefaultNeckPosition = vec3(0, -0.075f, -0.080f);
+
+ElbowModel::ElbowModel() {}
+ElbowModel::~ElbowModel() {}
+
+void ElbowModel::Enable(const vec3& neck_position, bool right_handed) {
+  enabled_ = true;
+  neck_position_ = neck_position;
+
+  if (right_handed) {
+    elbow_position_ = kRightElbowPosition;
+    arm_extension_ = kRightArmExtension;
+  } else {
+    elbow_position_ = kLeftElbowPosition;
+    arm_extension_ = kLeftArmExtension;
+  }
+
+  ResetRoot();
+}
+
+void ElbowModel::Disable() { enabled_ = false; }
+
+vec3 ElbowModel::Update(float delta_t, const quat& hmd_orientation,
+                        const quat& controller_orientation, bool recenter) {
+  if (!enabled_)
+    return vec3::Zero();
+
+  float heading_rad = GetHeading(hmd_orientation);
+
+  quat y_rotation;
+  y_rotation = Eigen::AngleAxis<float>(heading_rad, vec3::UnitY());
+
+  // If the controller's angular velocity is above a certain amount, we can
+  // assume torso rotation and move the elbow joint relative to the
+  // camera orientation.
+  float angle_delta = last_controller_.angularDistance(controller_orientation);
+  float rot_speed = angle_delta / delta_t;
+
+  if (recenter) {
+    root_rot_ = y_rotation;
+  } else if (rot_speed > kMinRotationSpeed) {
+    root_rot_.slerp(angle_delta / kMinAngleDelta, y_rotation);
+  }
+
+  // Calculate angle (or really, cos thereof) between controller forward vector
+  // and Y axis to determine extension amount.
+  vec3 controller_forward_rotated = controller_orientation * -vec3::UnitZ();
+  float dot_y = controller_forward_rotated.y();
+  float amt_extension = clamp(dot_y - kCosMinExtensionAngle, 0, 1);
+
+  // Remove the root rotation from the orientation reading--we'll add it back in
+  // later.
+  quat controller_rot = root_rot_.inverse() * controller_orientation;
+  controller_forward_rotated = controller_rot * -vec3::UnitZ();
+  quat rot_xy;
+  rot_xy.setFromTwoVectors(-vec3::UnitZ(), controller_forward_rotated);
+
+  // Fixing polar singularity
+  float total_angle = NormalizeAngle(atan2f(rot_xy.norm(), rot_xy.w()) * 2.0f);
+  float lerp_amount = (1.0f - powf(total_angle / M_PI, 6.0f)) *
+                      (1.0f - (kElbowBendRatio +
+                               (1.0f - kElbowBendRatio) *
+                                   (amt_extension + kYAxisExtensionFraction)));
+
+  // Calculate the relative rotations of the elbow and wrist joints.
+  quat wrist_rot = quat::Identity();
+  wrist_rot.slerp(lerp_amount, rot_xy);
+  quat elbow_rot = wrist_rot.inverse() * rot_xy;
+
+  last_controller_ = controller_orientation;
+
+  vec3 position =
+      root_rot_ *
+      ((controller_root_offset_ + arm_extension_ * amt_extension) +
+       elbow_rot * (kControllerForearm + wrist_rot * kControllerPosition));
+
+  return position;
+}
+
+float ElbowModel::GetHeading(const quat& orientation) {
+  vec3 gaze = orientation * -vec3::UnitZ();
+
+  if (gaze.y() > 0.99)
+    gaze = orientation * -vec3::UnitY();
+  else if (gaze.y() < -0.99)
+    gaze = orientation * vec3::UnitY();
+
+  return atan2f(-gaze.x(), -gaze.z());
+}
+
+void ElbowModel::ResetRoot() {
+  controller_root_offset_ = elbow_position_ + neck_position_;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/elbow_model.h b/services/vr/vr_window_manager/elbow_model.h
new file mode 100644
index 0000000..a6d5ca9
--- /dev/null
+++ b/services/vr/vr_window_manager/elbow_model.h
@@ -0,0 +1,45 @@
+#ifndef VR_WINDOW_MANAGER_ELBOW_MODEL_H_
+#define VR_WINDOW_MANAGER_ELBOW_MODEL_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class ElbowModel {
+ public:
+  ElbowModel();
+  ~ElbowModel();
+
+  void Enable(const vec3& neck_position, bool right_handed);
+  void Disable();
+
+  vec3 Update(float delta_t, const quat& hmd_orientation,
+              const quat& controller_orientation, bool recenter);
+
+  static const vec3 kDefaultNeckPosition;
+
+ private:
+  ElbowModel(const ElbowModel&) = delete;
+  void operator=(const ElbowModel&) = delete;
+
+  void ResetRoot();
+
+  float GetHeading(const quat& orientation);
+
+  bool enabled_ = false;
+
+  quat last_controller_ = quat::Identity();
+
+  quat root_rot_ = quat::Identity();
+
+  vec3 controller_root_offset_ = vec3::Zero();
+  vec3 elbow_position_ = vec3::Zero();
+  vec3 arm_extension_ = vec3::Zero();
+  vec3 neck_position_ = vec3::Zero();
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_ELBOW_MODEL_H_
diff --git a/services/vr/vr_window_manager/hwc_callback.cpp b/services/vr/vr_window_manager/hwc_callback.cpp
new file mode 100644
index 0000000..28e97ff
--- /dev/null
+++ b/services/vr/vr_window_manager/hwc_callback.cpp
@@ -0,0 +1,96 @@
+#include "hwc_callback.h"
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <private/dvr/native_buffer.h>
+#include <sync/sync.h>
+#include <ui/GraphicBufferMapper.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+HwcCallback::FrameStatus GetFrameStatus(const HwcCallback::Frame& frame) {
+  for (const auto& layer : frame.layers()) {
+    // If there is no fence it means the buffer is already finished.
+    if (layer.fence->isValid()) {
+      status_t result = layer.fence->wait(0);
+      if (result != OK) {
+        if (result != -ETIME) {
+          ALOGE("fence wait on buffer fence failed. status=%d (%s).",
+                result, strerror(-result));
+          return HwcCallback::FrameStatus::kError;
+        }
+        return HwcCallback::FrameStatus::kUnfinished;
+      }
+    }
+  }
+
+  return HwcCallback::FrameStatus::kFinished;
+}
+
+}  // namespace
+
+void HwcCallback::HwcLayer::PrintLayer() {
+  ALOGI("appid=%d, type=%d, alpha=%.2f, cursor=%dx%d, color=%02X%02X%02X%02X, "
+      "crop=%.1f,%.1f,%.1f,%.1f, display=%d,%d,%d,%d, dataspace=%d, "
+      "transform=%d", appid, type, alpha, cursor_x, cursor_y, color.r, color.g,
+      color.b, color.a, crop.left, crop.top, crop.right, crop.bottom,
+      display_frame.left, display_frame.right, display_frame.top,
+      display_frame.bottom, dataspace, transform);
+}
+
+HwcCallback::HwcCallback(Client* client) : client_(client) {
+}
+
+HwcCallback::~HwcCallback() {
+}
+
+binder::Status HwcCallback::onNewFrame(
+    const ParcelableComposerFrame& parcelable_frame,
+    ParcelableUniqueFd* fence) {
+  ComposerView::Frame frame = parcelable_frame.frame();
+  std::vector<HwcLayer> hwc_frame(frame.layers.size());
+  for (size_t i = 0; i < frame.layers.size(); ++i) {
+    const ComposerView::ComposerLayer& layer = frame.layers[i];
+    hwc_frame[i] = HwcLayer{
+      .fence = layer.fence,
+      .buffer = layer.buffer,
+      .crop = layer.crop,
+      .display_frame = layer.display_frame,
+      .blending = static_cast<int32_t>(layer.blend_mode),
+      .appid = layer.app_id,
+      .type = static_cast<HwcLayer::LayerType>(layer.type),
+      .alpha = layer.alpha,
+      .cursor_x = layer.cursor_x,
+      .cursor_y = layer.cursor_y,
+      .color = layer.color,
+      .dataspace = layer.dataspace,
+      .transform = layer.transform,
+    };
+  }
+
+  fence->set_fence(client_->OnFrame(std::make_unique<Frame>(
+      std::move(hwc_frame), frame.display_id, frame.removed,
+      frame.display_width, frame.display_height)));
+  return binder::Status::ok();
+}
+
+HwcCallback::Frame::Frame(std::vector<HwcLayer>&& layers, uint32_t display_id,
+                          bool removed, int32_t display_width,
+                          int32_t display_height)
+    : display_id_(display_id),
+      removed_(removed),
+      display_width_(display_width),
+      display_height_(display_height),
+      layers_(std::move(layers)) {}
+
+HwcCallback::FrameStatus HwcCallback::Frame::Finish() {
+  if (status_ == FrameStatus::kUnfinished)
+    status_ = GetFrameStatus(*this);
+  return status_;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/hwc_callback.h b/services/vr/vr_window_manager/hwc_callback.h
new file mode 100644
index 0000000..259c4ac
--- /dev/null
+++ b/services/vr/vr_window_manager/hwc_callback.h
@@ -0,0 +1,132 @@
+#ifndef VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+#define VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+
+#include <android/dvr/BnVrComposerCallback.h>
+#include <android-base/unique_fd.h>
+#include <impl/vr_hwc.h>
+
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+namespace android {
+
+class Fence;
+class GraphicBuffer;
+
+namespace dvr {
+
+using Recti = ComposerView::ComposerLayer::Recti;
+using Rectf = ComposerView::ComposerLayer::Rectf;
+
+class HwcCallback : public BnVrComposerCallback {
+ public:
+  struct HwcLayer {
+    enum LayerType : uint32_t {
+      // These are from frameworks/base/core/java/android/view/WindowManager.java
+      kSurfaceFlingerLayer = 0,
+      kUndefinedWindow = ~0U,
+      kFirstApplicationWindow = 1,
+      kLastApplicationWindow = 99,
+      kFirstSubWindow = 1000,
+      kLastSubWindow = 1999,
+      kFirstSystemWindow = 2000,
+      kStatusBar = kFirstSystemWindow,
+      kInputMethod = kFirstSystemWindow + 11,
+      kNavigationBar = kFirstSystemWindow + 19,
+      kLastSystemWindow = 2999,
+    };
+
+    bool should_skip_layer() const {
+      switch (type) {
+        // Always skip the following layer types
+      case kNavigationBar:
+      case kStatusBar:
+      case kSurfaceFlingerLayer:
+      case kUndefinedWindow:
+        return true;
+      default:
+        return false;
+      }
+    }
+
+    // This is a layer that provides some other functionality, eg dim layer.
+    // We use this to determine the point at which layers are "on top".
+    bool is_extra_layer() const {
+      switch(type) {
+      case kSurfaceFlingerLayer:
+      case kUndefinedWindow:
+        return true;
+      default:
+        return false;
+      }
+    }
+
+    void PrintLayer();
+
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+    Rectf crop;
+    Recti display_frame;
+    int32_t blending;
+    uint32_t appid;
+    LayerType type;
+    float alpha;
+    int32_t cursor_x;
+    int32_t cursor_y;
+    IComposerClient::Color color;
+    int32_t dataspace;
+    int32_t transform;
+  };
+
+  enum class FrameStatus {
+    kUnfinished,
+    kFinished,
+    kError
+  };
+
+  class Frame {
+  public:
+    Frame(std::vector<HwcLayer>&& layers, uint32_t display_id, bool removed,
+          int32_t display_width, int32_t display_height);
+
+    FrameStatus Finish();
+    const std::vector<HwcLayer>& layers() const { return layers_; }
+    uint32_t display_id() const { return display_id_; }
+    bool removed() const { return removed_; }
+    int32_t display_width() const { return display_width_; }
+    int32_t display_height() const { return display_height_; }
+
+  private:
+    uint32_t display_id_;
+    bool removed_;
+    int32_t display_width_;
+    int32_t display_height_;
+    std::vector<HwcLayer> layers_;
+    FrameStatus status_ = FrameStatus::kUnfinished;
+  };
+
+  class Client {
+   public:
+    virtual ~Client() {}
+    virtual base::unique_fd OnFrame(std::unique_ptr<Frame>) = 0;
+  };
+
+  explicit HwcCallback(Client* client);
+  ~HwcCallback() override;
+
+ private:
+  binder::Status onNewFrame(const ParcelableComposerFrame& frame,
+                            ParcelableUniqueFd* fence) override;
+
+  Client *client_;
+
+  HwcCallback(const HwcCallback&) = delete;
+  void operator=(const HwcCallback&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_HWC_CALLBACK_H_
diff --git a/services/vr/vr_window_manager/proguard.flags b/services/vr/vr_window_manager/proguard.flags
new file mode 100644
index 0000000..7683d6e
--- /dev/null
+++ b/services/vr/vr_window_manager/proguard.flags
@@ -0,0 +1,22 @@
+# Don't obfuscate any NDK/SDK code. This makes the debugging of stack traces in
+# in release builds easier.
+-keepnames class com.google.vr.ndk.** { *; }
+-keepnames class com.google.vr.sdk.** { *; }
+
+# These are part of the SDK <-> VrCore interfaces for GVR.
+-keepnames class com.google.vr.vrcore.library.api.** { *; }
+
+# These are part of the Java <-> native interfaces for GVR.
+-keep class com.google.vr.** { native <methods>; }
+
+-keep class com.google.vr.cardboard.annotations.UsedByNative
+-keep @com.google.vr.cardboard.annotations.UsedByNative class *
+-keepclassmembers class * {
+    @com.google.vr.cardboard.annotations.UsedByNative *;
+}
+
+-keep class com.google.vr.cardboard.UsedByNative
+-keep @com.google.vr.cardboard.UsedByNative class *
+-keepclassmembers class * {
+    @com.google.vr.cardboard.UsedByNative *;
+}
diff --git a/services/vr/vr_window_manager/reticle.cpp b/services/vr/vr_window_manager/reticle.cpp
new file mode 100644
index 0000000..cbd0caf
--- /dev/null
+++ b/services/vr/vr_window_manager/reticle.cpp
@@ -0,0 +1,100 @@
+#include "reticle.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+const std::string kVertexShader = SHADER0([]() {
+  layout(location = 0) in vec4 aPosition;
+  layout(location = 1) in vec4 aTexCoord;
+  uniform mat4 uViewProjection;
+  uniform mat4 uTransform;
+
+  out vec2 vTexCoord;
+  void main() {
+    gl_Position = uViewProjection * uTransform * aPosition;
+    vTexCoord = aTexCoord.xy;
+  }
+});
+
+const std::string kFragmentShader = SHADER0([]() {
+  precision mediump float;
+
+  in vec2 vTexCoord;
+  uniform vec3 uColor;
+
+  out vec4 fragColor;
+  void main() {
+    float alpha = smoothstep(1.0, 0.0, length(vTexCoord));
+    fragColor = vec4(uColor, alpha);
+  }
+});
+
+}  // namespace
+
+Reticle::Reticle() {}
+
+Reticle::~Reticle() {}
+
+bool Reticle::Initialize() {
+  program_.Link(kVertexShader, kFragmentShader);
+  if (!program_)
+    return false;
+
+  return true;
+}
+
+void Reticle::ShowAt(const mat4& hit_transform, const vec3& color) {
+  transform_ = hit_transform;
+  shown_ = true;
+
+  GLint view_projection_location =
+      glGetUniformLocation(program_.GetProgram(), "uColor");
+  glProgramUniform3f(program_.GetProgram(), view_projection_location, color.x(),
+                     color.y(), color.z());
+}
+
+void Reticle::Draw(const mat4& perspective, const mat4& eye_matrix,
+                   const mat4& head_matrix) {
+  if (!shown_)
+    return;
+
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  program_.Use();
+
+  const float kRadius = 0.015;
+  GLfloat vertices[] = {
+      -kRadius, -kRadius, 0, kRadius, -kRadius, 0,
+      -kRadius, kRadius,  0, kRadius, kRadius,  0,
+  };
+  GLfloat texture_vertices[] = {
+      -1, 1, 1, 1, -1, -1, 1, -1,
+  };
+
+  mat4 mvp = perspective * eye_matrix * head_matrix;
+  GLint view_projection_location =
+      glGetUniformLocation(program_.GetProgram(), "uViewProjection");
+  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+  GLint transform_location =
+      glGetUniformLocation(program_.GetProgram(), "uTransform");
+  glUniformMatrix4fv(transform_location, 1, 0, transform_.data());
+
+  glEnableVertexAttribArray(0);
+  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
+  glEnableVertexAttribArray(1);
+  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texture_vertices);
+
+  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+  glDisable(GL_BLEND);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/reticle.h b/services/vr/vr_window_manager/reticle.h
new file mode 100644
index 0000000..d8522aa
--- /dev/null
+++ b/services/vr/vr_window_manager/reticle.h
@@ -0,0 +1,35 @@
+#ifndef VR_WINDOW_MANAGER_SHELL_RETICLE_H_
+#define VR_WINDOW_MANAGER_SHELL_RETICLE_H_
+
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class Reticle {
+ public:
+  Reticle();
+  ~Reticle();
+
+  bool Initialize();
+
+  void ShowAt(const mat4& hit_transform, const vec3& color);
+  void Hide() { shown_ = false; }
+
+  void Draw(const mat4& perspective, const mat4& eye_matrix,
+            const mat4& head_matrix);
+
+ private:
+  bool shown_ = false;
+  ShaderProgram program_;
+  mat4 transform_;
+
+  Reticle(const Reticle&) = delete;
+  void operator=(const Reticle&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_SHELL_RETICLE_H_
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
new file mode 100644
index 0000000..2b53cd6
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -0,0 +1,529 @@
+#include "shell_view.h"
+
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <android/input.h>
+#include <binder/IServiceManager.h>
+#include <dvr/graphics.h>
+#include <hardware/hwcomposer2.h>
+#include <inttypes.h>
+#include <log/log.h>
+
+#include "controller_mesh.h"
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr uint32_t kPrimaryDisplayId = 1;
+
+const std::string kVertexShader = SHADER0([]() {
+  layout(location = 0) in vec4 aPosition;
+  layout(location = 1) in vec4 aTexCoord;
+  uniform mat4 uViewProjection;
+  uniform mat4 uTransform;
+
+  out vec2 vTexCoord;
+  void main() {
+    gl_Position = uViewProjection * uTransform * aPosition;
+    vTexCoord = aTexCoord.xy;
+  }
+});
+
+const std::string kFragmentShader = SHADER0([]() {
+  precision mediump float;
+
+  in vec2 vTexCoord;
+  uniform sampler2D tex;
+  uniform float uAlpha;
+
+  out vec4 fragColor;
+  void main() {
+    fragColor = texture(tex, vTexCoord);
+    fragColor.a *= uAlpha;
+  }
+});
+
+// This shader provides a dim layer in a given rect. This is intended
+// to indicate the non-interactive region.
+// Texture coordinates between [uCoords.xy, uCoords.zw] are dim, otherwise
+// transparent.
+const std::string kOverlayFragmentShader = SHADER0([]() {
+  precision highp float;
+
+  in vec2 vTexCoord;
+  uniform sampler2D tex;
+  uniform vec4 uCoords;
+
+  out vec4 fragColor;
+  void main() {
+    vec4 color = vec4(0, 0, 0, 0);
+    if (all(greaterThan(vTexCoord, uCoords.xy)) &&
+        all(lessThan(vTexCoord, uCoords.zw))) {
+      color = vec4(0, 0, 0, 0.5);
+    }
+    fragColor = color;
+  }
+});
+
+const std::string kControllerFragmentShader = SHADER0([]() {
+  precision mediump float;
+
+  in vec2 vTexCoord;
+
+  out vec4 fragColor;
+  void main() { fragColor = vec4(0.8, 0.2, 0.2, 1.0); }
+});
+
+mat4 GetHorizontallyAlignedMatrixFromPose(const Posef& pose) {
+  vec3 position = pose.GetPosition();
+  quat view_quaternion = pose.GetRotation();
+
+  vec3 z = vec3(view_quaternion * vec3(0.0f, 0.0f, 1.0f));
+  vec3 y(0.0f, 1.0f, 0.0f);
+  vec3 x = y.cross(z);
+  x.normalize();
+  y = z.cross(x);
+
+  mat4 m;
+  // clang-format off
+  m(0, 0) = x[0]; m(0, 1) = y[0]; m(0, 2) = z[0]; m(0, 3) = position[0];
+  m(1, 0) = x[1]; m(1, 1) = y[1]; m(1, 2) = z[1]; m(1, 3) = position[1];
+  m(2, 0) = x[2]; m(2, 1) = y[2]; m(2, 2) = z[2]; m(2, 3) = position[2];
+  m(3, 0) = 0.0f; m(3, 1) = 0.0f; m(3, 2) = 0.0f; m(3, 3) = 1.0f;
+  // clang-format on
+
+  return m;
+}
+
+int GetTouchIdForDisplay(uint32_t display) {
+  return display == kPrimaryDisplayId ? DVR_VIRTUAL_TOUCHPAD_PRIMARY
+                                      : DVR_VIRTUAL_TOUCHPAD_VIRTUAL;
+}
+
+}  // namespace
+
+ShellView::ShellView() {}
+
+ShellView::~ShellView() {}
+
+int ShellView::Initialize() {
+  int ret = Application::Initialize();
+  if (ret)
+    return ret;
+
+  virtual_touchpad_.reset(dvrVirtualTouchpadCreate());
+  const status_t touchpad_status =
+      dvrVirtualTouchpadAttach(virtual_touchpad_.get());
+  if (touchpad_status != OK) {
+    ALOGE("Failed to connect to virtual touchpad");
+    return touchpad_status;
+  }
+
+  surface_flinger_view_.reset(new SurfaceFlingerView);
+  if (!surface_flinger_view_->Initialize(this))
+    return 1;
+
+  return 0;
+}
+
+int ShellView::AllocateResources() {
+  int ret = Application::AllocateResources();
+  if (ret)
+    return ret;
+
+  program_.reset(new ShaderProgram);
+  program_->Link(kVertexShader, kFragmentShader);
+  overlay_program_.reset(new ShaderProgram);
+  overlay_program_->Link(kVertexShader, kOverlayFragmentShader);
+  controller_program_.reset(new ShaderProgram);
+  controller_program_->Link(kVertexShader, kControllerFragmentShader);
+  if (!program_ || !overlay_program_ || !controller_program_)
+    return 1;
+
+  reticle_.reset(new Reticle());
+  if (!reticle_->Initialize())
+    return 1;
+
+  controller_mesh_.reset(new Mesh<vec3, vec3, vec2>());
+  controller_mesh_->SetVertices(kNumControllerMeshVertices,
+                                kControllerMeshVertices);
+
+  for (auto& display : displays_)
+    display->SetPrograms(program_.get(), overlay_program_.get());
+
+  initialized_ = true;
+
+  return 0;
+}
+
+void ShellView::DeallocateResources() {
+  {
+    std::unique_lock<std::mutex> l(display_frame_mutex_);
+    removed_displays_.clear();
+    new_displays_.clear();
+    displays_.clear();
+  }
+
+  display_client_.reset();
+  reticle_.reset();
+  controller_mesh_.reset();
+  program_.reset(new ShaderProgram);
+  overlay_program_.reset(new ShaderProgram);
+  controller_program_.reset(new ShaderProgram);
+  Application::DeallocateResources();
+}
+
+void ShellView::EnableDebug(bool debug) {
+  QueueTask(debug ? MainThreadTask::EnableDebugMode
+                  : MainThreadTask::DisableDebugMode);
+}
+
+void ShellView::VrMode(bool mode) {
+  QueueTask(mode ? MainThreadTask::EnteringVrMode
+                 : MainThreadTask::ExitingVrMode);
+}
+
+void ShellView::dumpInternal(String8& result) {
+  result.append("[shell]\n");
+  result.appendFormat("initialized = %s\n", initialized_ ? "true" : "false");
+  result.appendFormat("is_visible = %s\n", is_visible_ ? "true" : "false");
+  result.appendFormat("debug_mode = %s\n\n", debug_mode_ ? "true" : "false");
+
+  result.append("[displays]\n");
+  result.appendFormat("count = %zu\n", displays_.size());
+  for (size_t i = 0; i < displays_.size(); ++i) {
+    result.appendFormat("  display_id = %" PRId32 "\n", displays_[i]->id());
+    result.appendFormat("    size=%fx%f\n",
+                        displays_[i]->size().x(), displays_[i]->size().y());
+  }
+
+  result.append("\n");
+}
+
+void ShellView::Set2DMode(bool mode) {
+  if (!displays_.empty())
+    displays_[0]->set_2dmode(mode);
+}
+
+void ShellView::SetRotation(int angle) {
+  mat4 m(Eigen::AngleAxisf(M_PI * -0.5f * angle, vec3::UnitZ()));
+  for (auto& d: displays_)
+    d->set_rotation(m);
+}
+
+void ShellView::OnDrawFrame() {
+  bool visible = false;
+
+  {
+    std::unique_lock<std::mutex> l(display_frame_mutex_);
+
+    // Move any new displays into the list.
+    if (!new_displays_.empty()) {
+      for (auto& display : new_displays_) {
+        display->Recenter(GetHorizontallyAlignedMatrixFromPose(last_pose_));
+        display->SetPrograms(program_.get(), overlay_program_.get());
+        displays_.emplace_back(display.release());
+      }
+      new_displays_.clear();
+    }
+
+    // Remove any old displays from the list now.
+    if (!removed_displays_.empty()) {
+      for (auto& display : removed_displays_) {
+        displays_.erase(std::find_if(
+            displays_.begin(), displays_.end(),
+            [display](auto& ptr) { return display == ptr.get(); }));
+      }
+      removed_displays_.clear();
+    }
+
+    for (auto& display : displays_) {
+      display->AdvanceFrame();
+      visible = visible || display->visible();
+    }
+  }
+
+  if (!debug_mode_ && visible != is_visible_) {
+    SetVisibility(visible);
+  }
+
+  for (auto& display : displays_) {
+    display->OnDrawFrame(surface_flinger_view_.get(), debug_mode_);
+  }
+}
+
+void ShellView::OnEndFrame() {
+  std::unique_lock<std::mutex> l(display_frame_mutex_);
+  for (auto& display : displays_) {
+    display->UpdateReleaseFence();
+  }
+}
+
+DisplayView* ShellView::FindOrCreateDisplay(uint32_t id) {
+  for (auto& display : displays_) {
+    if (display->id() == id) {
+      return display.get();
+    }
+  }
+
+  // It might be pending addition.
+  for (auto& display : new_displays_) {
+    if (display->id() == id) {
+      return display.get();
+    }
+  }
+
+  auto display = new DisplayView(id, GetTouchIdForDisplay(id));
+  // Virtual displays only ever have 2D apps so force it.
+  if (id != kPrimaryDisplayId)
+    display->set_always_2d(true);
+  new_displays_.emplace_back(display);
+  return display;
+}
+
+base::unique_fd ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
+  std::unique_lock<std::mutex> l(display_frame_mutex_);
+  DisplayView* display = FindOrCreateDisplay(frame->display_id());
+
+  if (frame->removed()) {
+    removed_displays_.push_back(display);
+    return base::unique_fd();
+  }
+
+  bool showing = false;
+
+  // This is a temporary fix for now. These APIs will be changed when everything
+  // is moved into vrcore.
+  // Do this on demand in case vr_flinger crashed and we are reconnecting.
+  if (!display_client_.get()) {
+    int error = 0;
+    display_client_ = DisplayClient::Create(&error);
+
+    if (error) {
+      ALOGE("Could not connect to display service : %s(%d)", strerror(error),
+            error);
+      return base::unique_fd();
+    }
+  }
+
+  // TODO(achaulk): change when moved into vrcore.
+  bool vr_running = display_client_->IsVrAppRunning();
+
+  base::unique_fd fd(
+      display->OnFrame(std::move(frame), debug_mode_, vr_running, &showing));
+
+  if (showing)
+    QueueTask(MainThreadTask::Show);
+
+  return fd;
+}
+
+void ShellView::DrawEye(EyeType eye, const mat4& perspective,
+                        const mat4& eye_matrix, const mat4& head_matrix) {
+  if (should_recenter_ && !displays_.empty()) {
+    // Position the quad horizontally aligned in the direction the user
+    // is facing, effectively taking out head roll.
+    displays_[0]->Recenter(GetHorizontallyAlignedMatrixFromPose(last_pose_));
+  }
+
+  for (auto& display : displays_) {
+    if (display->visible()) {
+      display->DrawEye(eye, perspective, eye_matrix, head_matrix, fade_value_);
+    }
+  }
+
+  // TODO(alexst): Replicate controller rendering from VR Home.
+  // Current approach in the function below is a quick visualization.
+  DrawController(perspective, eye_matrix, head_matrix);
+
+  DrawReticle(perspective, eye_matrix, head_matrix);
+}
+
+void ShellView::OnVisibilityChanged(bool visible) {
+  should_recenter_ = visible;
+  Application::OnVisibilityChanged(visible);
+}
+
+bool ShellView::OnClick(bool down) {
+  if (down) {
+    if (!is_touching_ && active_display_ && active_display_->allow_input()) {
+      is_touching_ = true;
+    }
+  } else {
+    is_touching_ = false;
+  }
+  Touch();
+  return true;
+}
+
+void ShellView::DrawReticle(const mat4& perspective, const mat4& eye_matrix,
+                            const mat4& head_matrix) {
+  reticle_->Hide();
+
+  vec3 pointer_location = last_pose_.GetPosition();
+  quat view_quaternion = last_pose_.GetRotation();
+
+  if (shmem_controller_active_) {
+    view_quaternion = controller_orientation_;
+    vec4 controller_location = controller_translate_ * vec4(0, 0, 0, 1);
+    pointer_location = vec3(controller_location.x(), controller_location.y(),
+                            controller_location.z());
+
+    if (shmem_controller_active_) {
+      uint64_t buttons = shmem_controller_buttons_;
+      shmem_controller_buttons_ = 0;
+      while (buttons) {
+        switch (buttons & 0xF) {
+          case 0x1:
+            OnClick(false);
+            break;
+          case 0x3:
+            OnTouchpadButton(false, AMOTION_EVENT_BUTTON_BACK);
+            break;
+          case 0x4:
+            should_recenter_ = true;
+            break;
+          case 0x9:
+            OnClick(true);
+            break;
+          case 0xB:
+            OnTouchpadButton(true, AMOTION_EVENT_BUTTON_BACK);
+            break;
+          default:
+            break;
+        }
+        buttons >>= 4;
+      }
+    }
+  }
+
+  vec3 hit_location;
+  active_display_ =
+      FindActiveDisplay(pointer_location, view_quaternion, &hit_location);
+
+  if (active_display_) {
+    reticle_->ShowAt(
+        Eigen::Translation3f(hit_location) * view_quaternion.matrix(),
+        active_display_->allow_input() ? vec3(1, 0, 0) : vec3(0, 0, 0));
+    Touch();
+  }
+
+  reticle_->Draw(perspective, eye_matrix, head_matrix);
+}
+
+DisplayView* ShellView::FindActiveDisplay(const vec3& position,
+                                          const quat& quaternion,
+                                          vec3* hit_location) {
+  vec3 direction = vec3(quaternion * vec3(0, 0, -1));
+  vec3 temp_hit;
+
+  DisplayView* best_display = nullptr;
+  vec3 best_hit;
+
+  auto is_better = [&best_hit, &position](DisplayView*, const vec3& hit) {
+    return (hit - position).squaredNorm() < (best_hit - position).squaredNorm();
+  };
+
+  for (auto& display : displays_) {
+    if (display->UpdateHitInfo(position, direction, &temp_hit)) {
+      if (!best_display || is_better(display.get(), temp_hit)) {
+        best_display = display.get();
+        best_hit = temp_hit;
+      }
+    }
+  }
+
+  if (best_display)
+    *hit_location = best_hit;
+  return best_display;
+}
+
+void ShellView::DrawController(const mat4& perspective, const mat4& eye_matrix,
+                               const mat4& head_matrix) {
+  if (!shmem_controller_active_)
+    return;
+
+  controller_program_->Use();
+  mat4 mvp = perspective * eye_matrix * head_matrix;
+
+  GLint view_projection_location = glGetUniformLocation(
+      controller_program_->GetProgram(), "uViewProjection");
+  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+  quat view_quaternion = controller_orientation_;
+  view_quaternion.toRotationMatrix();
+
+  vec3 world_pos = last_pose_.GetPosition() + controller_position_;
+
+  controller_translate_ =
+      Eigen::Translation3f(world_pos.x(), world_pos.y(), world_pos.z());
+
+  mat4 transform = controller_translate_ * view_quaternion *
+                   mat4(Eigen::Scaling<float>(1, 1, 3.0));
+  GLint transform_location =
+      glGetUniformLocation(controller_program_->GetProgram(), "uTransform");
+  glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+  controller_mesh_->Draw();
+}
+
+void ShellView::Touch() {
+  if (!virtual_touchpad_) {
+    ALOGE("missing virtual touchpad");
+    return;
+  }
+
+  if (!active_display_)
+    return;
+
+  const vec2& hit_location = active_display_->hit_location();
+  const vec2 size = active_display_->size();
+
+  float x = hit_location.x() / size.x();
+  float y = hit_location.y() / size.y();
+
+  // Device is portrait, but in landscape when in VR.
+  // Rotate touch input appropriately.
+  const android::status_t status = dvrVirtualTouchpadTouch(
+      virtual_touchpad_.get(), active_display_->touchpad_id(),
+      x, y, is_touching_ ? 1.0f : 0.0f);
+  if (status != OK) {
+    ALOGE("touch failed: %d", status);
+  }
+}
+
+bool ShellView::OnTouchpadButton(bool down, int button) {
+  int buttons = touchpad_buttons_;
+  if (down) {
+    if (active_display_ && active_display_->allow_input()) {
+      buttons |= button;
+    }
+  } else {
+    buttons &= ~button;
+  }
+  if (buttons == touchpad_buttons_) {
+    return true;
+  }
+  touchpad_buttons_ = buttons;
+  if (!virtual_touchpad_) {
+    ALOGE("missing virtual touchpad");
+    return false;
+  }
+
+  if (!active_display_)
+    return true;
+
+  const android::status_t status = dvrVirtualTouchpadButtonState(
+      virtual_touchpad_.get(), active_display_->touchpad_id(),
+      touchpad_buttons_);
+  if (status != OK) {
+    ALOGE("touchpad button failed: %d %d", touchpad_buttons_, status);
+  }
+  return true;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
new file mode 100644
index 0000000..d90e833
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -0,0 +1,103 @@
+#ifndef VR_WINDOW_MANAGER_SHELL_VIEW_H_
+#define VR_WINDOW_MANAGER_SHELL_VIEW_H_
+
+#include <dvr/virtual_touchpad_client.h>
+#include <private/dvr/display_client.h>
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+
+#include <deque>
+
+#include "application.h"
+#include "display_view.h"
+#include "reticle.h"
+#include "shell_view_binder_interface.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+class ShellView : public Application,
+                  public android::dvr::ShellViewBinderInterface,
+                  public HwcCallback::Client {
+ public:
+  ShellView();
+  virtual ~ShellView();
+
+  int Initialize() override;
+
+  int AllocateResources() override;
+  void DeallocateResources() override;
+
+  // ShellViewBinderInterface:
+  void EnableDebug(bool debug) override;
+  void VrMode(bool mode) override;
+  void dumpInternal(String8& result) override;
+  void Set2DMode(bool mode) override;
+  void SetRotation(int angle) override;
+
+
+ protected:
+  void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
+               const mat4& head_matrix) override;
+  void OnDrawFrame() override;
+  void OnEndFrame() override;
+  void OnVisibilityChanged(bool visible) override;
+
+  void DrawReticle(const mat4& perspective, const mat4& eye_matrix,
+                   const mat4& head_matrix);
+  void DrawController(const mat4& perspective, const mat4& eye_matrix,
+                      const mat4& head_matrix);
+
+  void Touch();
+  bool OnTouchpadButton(bool down, int button);
+
+  bool OnClick(bool down);
+
+  DisplayView* FindActiveDisplay(const vec3& position, const quat& quaternion,
+                                 vec3* hit_location);
+
+  // HwcCallback::Client:
+  base::unique_fd OnFrame(std::unique_ptr<HwcCallback::Frame> frame) override;
+  DisplayView* FindOrCreateDisplay(uint32_t id);
+
+  std::unique_ptr<ShaderProgram> program_;
+  std::unique_ptr<ShaderProgram> overlay_program_;
+  std::unique_ptr<ShaderProgram> controller_program_;
+
+  std::unique_ptr<SurfaceFlingerView> surface_flinger_view_;
+  std::unique_ptr<Reticle> reticle_;
+
+  std::unique_ptr<DisplayClient> display_client_;
+
+  struct DvrVirtualTouchpadDeleter {
+    void operator()(DvrVirtualTouchpad* p) {
+      dvrVirtualTouchpadDetach(p);
+      dvrVirtualTouchpadDestroy(p);
+    }
+  };
+  std::unique_ptr<DvrVirtualTouchpad, DvrVirtualTouchpadDeleter>
+      virtual_touchpad_;
+
+  std::unique_ptr<Mesh<vec3, vec3, vec2>> controller_mesh_;
+
+  bool is_touching_ = false;
+  int touchpad_buttons_ = 0;
+
+  std::mutex display_frame_mutex_;
+
+  std::vector<std::unique_ptr<DisplayView>> displays_;
+  std::vector<std::unique_ptr<DisplayView>> new_displays_;
+  std::vector<DisplayView*> removed_displays_;
+  DisplayView* active_display_ = nullptr;
+
+  mat4 controller_translate_;
+
+  ShellView(const ShellView&) = delete;
+  void operator=(const ShellView&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_SHELL_VIEW_H_
diff --git a/services/vr/vr_window_manager/shell_view_binder_interface.h b/services/vr/vr_window_manager/shell_view_binder_interface.h
new file mode 100644
index 0000000..c66e4a1
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view_binder_interface.h
@@ -0,0 +1,22 @@
+#ifndef VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
+#define VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
+
+namespace android {
+namespace dvr {
+
+class ShellViewBinderInterface {
+ public:
+  ShellViewBinderInterface() {};
+  virtual ~ShellViewBinderInterface() {};
+
+  virtual void EnableDebug(bool debug) = 0;
+  virtual void VrMode(bool mode) = 0;
+  virtual void dumpInternal(String8& result) = 0;
+  virtual void Set2DMode(bool mode) = 0;
+  virtual void SetRotation(int angle) = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
diff --git a/services/vr/vr_window_manager/surface_flinger_view.cpp b/services/vr/vr_window_manager/surface_flinger_view.cpp
new file mode 100644
index 0000000..b41de03
--- /dev/null
+++ b/services/vr/vr_window_manager/surface_flinger_view.cpp
@@ -0,0 +1,77 @@
+#include "surface_flinger_view.h"
+
+#include <android/dvr/IVrComposer.h>
+#include <binder/IServiceManager.h>
+#include <private/dvr/native_buffer.h>
+
+#include "hwc_callback.h"
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+SurfaceFlingerView::SurfaceFlingerView() {}
+
+SurfaceFlingerView::~SurfaceFlingerView() {}
+
+bool SurfaceFlingerView::Initialize(HwcCallback::Client *client) {
+  sp<IServiceManager> sm(defaultServiceManager());
+  vr_composer_ = interface_cast<IVrComposer>(
+      sm->getService(IVrComposer::SERVICE_NAME()));
+
+  String8 service_name(IVrComposer::SERVICE_NAME().string());
+  if (!vr_composer_.get()) {
+    ALOGE("Faild to connect to %s", service_name.c_str());
+    return false;
+  }
+
+  composer_callback_ = new HwcCallback(client);
+  binder::Status status = vr_composer_->registerObserver(composer_callback_);
+  if (!status.isOk()) {
+    ALOGE("Failed to register observer with %s", service_name.c_str());
+    return false;
+  }
+
+  return true;
+}
+
+bool SurfaceFlingerView::GetTextures(const HwcCallback::Frame& frame,
+                                     std::vector<TextureLayer>* texture_layers,
+                                     TextureLayer* ime_layer,
+                                     bool debug, bool skip_first_layer) const {
+  auto& layers = frame.layers();
+  texture_layers->clear();
+
+  size_t start = 0;
+  // Skip the second layer if it is from the VR app.
+  if (!debug && skip_first_layer) {
+    start = 2;
+  }
+
+  for (size_t i = start; i < layers.size(); ++i) {
+    if (!debug && layers[i].should_skip_layer())
+      continue;
+
+    std::unique_ptr<Texture> texture(new Texture());
+    if (!texture->Initialize(layers[i].buffer->getNativeBuffer())) {
+      ALOGE("Failed to create texture");
+      texture_layers->clear();
+      return false;
+    }
+
+    TextureLayer texture_layer = {
+        std::move(texture), layers[i].crop, layers[i].display_frame,
+        layers[i].blending, layers[i].alpha,
+    };
+    if (debug && layers[i].type == HwcCallback::HwcLayer::kInputMethod) {
+      *ime_layer = std::move(texture_layer);
+    } else {
+      texture_layers->emplace_back(std::move(texture_layer));
+    }
+  }
+
+  return true;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/surface_flinger_view.h b/services/vr/vr_window_manager/surface_flinger_view.h
new file mode 100644
index 0000000..1bea38d
--- /dev/null
+++ b/services/vr/vr_window_manager/surface_flinger_view.h
@@ -0,0 +1,46 @@
+#ifndef APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
+#define APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
+
+#include <memory>
+
+#include "hwc_callback.h"
+
+namespace android {
+namespace dvr {
+
+class IDisplay;
+class IVrComposer;
+class Texture;
+
+struct TextureLayer {
+  std::unique_ptr<Texture> texture;
+  Rectf crop;
+  Recti display_frame;
+  int32_t blending;
+  float alpha;
+};
+
+class SurfaceFlingerView {
+ public:
+  SurfaceFlingerView();
+  ~SurfaceFlingerView();
+
+  bool Initialize(HwcCallback::Client *client);
+
+  bool GetTextures(const HwcCallback::Frame& layers,
+                   std::vector<TextureLayer>* texture_layers,
+                   TextureLayer* ime_layer, bool debug,
+                   bool skip_first_layer) const;
+
+ private:
+  sp<IVrComposer> vr_composer_;
+  sp<HwcCallback> composer_callback_;
+
+  SurfaceFlingerView(const SurfaceFlingerView&) = delete;
+  void operator=(const SurfaceFlingerView&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
diff --git a/services/vr/vr_window_manager/texture.cpp b/services/vr/vr_window_manager/texture.cpp
new file mode 100644
index 0000000..2229efa
--- /dev/null
+++ b/services/vr/vr_window_manager/texture.cpp
@@ -0,0 +1,41 @@
+#include "texture.h"
+
+#include <GLES/glext.h>
+#include <log/log.h>
+#include <system/window.h>
+
+namespace android {
+namespace dvr {
+
+Texture::Texture() {}
+
+Texture::~Texture() {
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  if (id_)
+    glDeleteTextures(1, &id_);
+  if (image_)
+    eglDestroyImageKHR(display, image_);
+}
+
+bool Texture::Initialize(ANativeWindowBuffer* buffer) {
+  width_ = buffer->width;
+  height_ = buffer->height;
+
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  image_ = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+                             EGL_NATIVE_BUFFER_ANDROID, buffer, nullptr);
+  if (!image_) {
+    ALOGE("Failed to create eglImage");
+    return false;
+  }
+
+  glGenTextures(1, &id_);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, id_);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image_);
+
+  return true;
+}
+
+}  // namespace android
+}  // namespace dvr
diff --git a/services/vr/vr_window_manager/texture.h b/services/vr/vr_window_manager/texture.h
new file mode 100644
index 0000000..9840f19
--- /dev/null
+++ b/services/vr/vr_window_manager/texture.h
@@ -0,0 +1,37 @@
+#ifndef VR_WINDOW_MANAGER_TEXTURE_H_
+#define VR_WINDOW_MANAGER_TEXTURE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace dvr {
+
+class Texture {
+ public:
+  explicit Texture();
+  ~Texture();
+
+  bool Initialize(ANativeWindowBuffer* buffer);
+
+  GLuint id() const { return id_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+
+ private:
+  EGLImageKHR image_ = nullptr;
+  GLuint id_ = 0;
+  int width_ = 0;
+  int height_ = 0;
+
+  Texture(const Texture&) = delete;
+  void operator=(const Texture&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_TEXTURE_H_
diff --git a/services/vr/vr_window_manager/vr_window_manager.cpp b/services/vr/vr_window_manager/vr_window_manager.cpp
new file mode 100644
index 0000000..dd2cba7
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager.cpp
@@ -0,0 +1,47 @@
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <hwbinder/IPCThreadState.h>
+#include <impl/vr_hwc.h>
+
+#include "shell_view.h"
+#include "vr_window_manager_binder.h"
+
+using namespace android;
+using namespace android::dvr;
+
+int main(int /* argc */, char** /* argv */) {
+  android::ProcessState::self()->startThreadPool();
+
+  // ShellView needs to be created after vr_hwcomposer.
+  android::dvr::ShellView app;
+  const int app_status = app.Initialize();
+  LOG_ALWAYS_FATAL_IF(app_status != 0, "failed to initialize: %d", app_status);
+
+  // Create vr_wm_binder.
+  android::sp<android::service::vr::VrWindowManagerBinder> vr_wm_binder =
+      new android::service::vr::VrWindowManagerBinder(app);
+  const int status = vr_wm_binder->Initialize();
+  LOG_ALWAYS_FATAL_IF(status != 0, "initialization failed: %d", status);
+
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+  const android::status_t vr_wm_binder_status = sm->addService(
+      android::service::vr::VrWindowManagerBinder::SERVICE_NAME(),
+      vr_wm_binder, false /*allowIsolated*/);
+  LOG_ALWAYS_FATAL_IF(vr_wm_binder_status != android::OK,
+                      "vr_wm_binder service not added: %d",
+                      static_cast<int>(vr_wm_binder_status));
+
+  app.SetControllerDataProvider(vr_wm_binder.get());
+
+  android::hardware::ProcessState::self()->startThreadPool();
+
+  while (true) {
+    app.DrawFrame();
+  }
+
+  android::hardware::IPCThreadState::self()->joinThreadPool();
+  android::IPCThreadState::self()->joinThreadPool();
+
+  return 0;
+}
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.cpp b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
new file mode 100644
index 0000000..fdcb8b2
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
@@ -0,0 +1,167 @@
+#include "vr_window_manager_binder.h"
+
+#include <inttypes.h>
+#include <sys/mman.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Errors.h>
+
+namespace android {
+namespace service {
+namespace vr {
+
+namespace {
+const String16 kDumpPermission("android.permission.DUMP");
+const String16 kSendMeControllerInputPermission(
+    "android.permission.RESTRICTED_VR_ACCESS");
+}  // anonymous namespace
+
+constexpr size_t AshmemControllerDataProvider::kRegionLength;
+
+status_t AshmemControllerDataProvider::Connect(const int in_fd) {
+  if (in_fd < 0) {
+    return BAD_VALUE;
+  }
+  if (fd_.get() >= 0) {
+    // The VrCore is dead. Long live the VrCore.
+    Disconnect();
+  }
+  void* const shared_region =
+      ::mmap(nullptr, kRegionLength, PROT_READ, MAP_SHARED, in_fd, 0);
+  if (shared_region == MAP_FAILED) {
+    shared_region_ = nullptr;
+    return NO_MEMORY;
+  }
+
+  errno = 0;
+  const int fd = ::fcntl(in_fd, F_DUPFD_CLOEXEC, 0);
+  if (fd < 0) {
+    ::munmap(shared_region, kRegionLength);
+    return -errno;
+  }
+  fd_.reset(fd);
+  ALOGI("controller connected %d -> %d @ %p", in_fd, fd, shared_region);
+
+  std::lock_guard<std::mutex> guard(mutex_);
+  shared_region_ = shared_region;
+  return OK;
+}
+
+status_t AshmemControllerDataProvider::Disconnect() {
+  if (shared_region_ == nullptr || fd_.get() < 0) {
+    return INVALID_OPERATION;
+  }
+  std::lock_guard<std::mutex> guard(mutex_);
+  ::munmap(shared_region_, kRegionLength);
+  shared_region_ = nullptr;
+  fd_.reset();
+  ALOGI("controller disconnected");
+  return OK;
+}
+
+const void* AshmemControllerDataProvider::LockControllerData() {
+  mutex_.lock();
+  if (!shared_region_) {
+    mutex_.unlock();
+    return nullptr;
+  }
+  return shared_region_;
+}
+
+void AshmemControllerDataProvider::UnlockControllerData() { mutex_.unlock(); }
+
+void AshmemControllerDataProvider::dumpInternal(String8& result) {
+  result.appendFormat("[controller]\nfd = %d\n", fd_.get());
+  if (shared_region_) {
+    int32_t* p = reinterpret_cast<int32_t*>(shared_region_);
+    result.appendFormat("header = ");
+    for (int i = 0; i < 8; ++i) {
+      result.appendFormat("%c 0x%08" PRIX32, i ? ',' : '[', p[i]);
+    }
+    result.appendFormat(" ]\n\n");
+  }
+}
+
+int VrWindowManagerBinder::Initialize() { return 0; }
+
+binder::Status VrWindowManagerBinder::connectController(
+    const ::android::base::unique_fd& in_fd) {
+  // TODO(kpschoedel): check permission
+#if 0
+  int32_t pid, uid;
+  if (!PermissionCache::checkCallingPermission(kSendMeControllerInputPermission,
+                                               &pid, &uid)) {
+    ALOGE("permission denied to pid=%" PRId32 " uid=%" PRId32, pid, uid);
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+#endif
+  return binder::Status::fromStatusT(Connect(in_fd.get()));
+}
+
+binder::Status VrWindowManagerBinder::disconnectController() {
+  // TODO(kpschoedel): check permission
+#if 0
+  int32_t pid, uid;
+  if (!PermissionCache::checkCallingPermission(kSendMeControllerInputPermission,
+                                               &pid, &uid)) {
+    ALOGE("permission denied to pid=%" PRId32 " uid=%" PRId32, pid, uid);
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+#endif
+  return binder::Status::fromStatusT(Disconnect());
+}
+
+binder::Status VrWindowManagerBinder::enterVrMode() {
+  // TODO(kpschoedel): check permission
+  app_.VrMode(true);
+  return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::exitVrMode() {
+  // TODO(kpschoedel): check permission
+  app_.VrMode(false);
+  return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::setDebugMode(int32_t mode) {
+  // TODO(kpschoedel): check permission
+  app_.EnableDebug(static_cast<bool>(mode));
+  return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::set2DMode(int32_t mode) {
+  app_.Set2DMode(static_cast<bool>(mode));
+  return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::setRotation(int32_t angle) {
+  app_.SetRotation(angle);
+  return binder::Status::ok();
+}
+
+status_t VrWindowManagerBinder::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=%d, uid=%d\n",
+                        pid, uid);
+  } else {
+    app_.dumpInternal(result);
+    AshmemControllerDataProvider::dumpInternal(result);
+  }
+  write(fd, result.string(), result.size());
+  return OK;
+}
+
+}  // namespace vr
+}  // namespace service
+}  // namespace android
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.h b/services/vr/vr_window_manager/vr_window_manager_binder.h
new file mode 100644
index 0000000..9d0f0b2
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager_binder.h
@@ -0,0 +1,79 @@
+#ifndef VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
+#define VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
+
+#include <android/service/vr/BnVrWindowManager.h>
+
+#include <mutex>
+
+#include "controller_data_provider.h"
+#include "shell_view_binder_interface.h"
+
+namespace android {
+namespace service {
+namespace vr {
+
+class AshmemControllerDataProvider : public dvr::ControllerDataProvider {
+ public:
+  AshmemControllerDataProvider() {}
+  virtual ~AshmemControllerDataProvider() {}
+
+  status_t Connect(int fd);
+  status_t Disconnect();
+
+  // ControllerDataProvider:
+  const void* LockControllerData() override;
+  void UnlockControllerData() override;
+
+ protected:
+  void dumpInternal(String8& result);
+
+ private:
+  static constexpr size_t kRegionLength = 8192;  // TODO(kpschoedel)
+  ::android::base::unique_fd fd_;
+
+  // Mutex for guarding shared_region_.
+  std::mutex mutex_;
+  void* shared_region_ = nullptr;
+
+  AshmemControllerDataProvider(const AshmemControllerDataProvider&) = delete;
+  void operator=(const AshmemControllerDataProvider&) = delete;
+};
+
+class VrWindowManagerBinder : public BnVrWindowManager,
+                              public AshmemControllerDataProvider {
+ public:
+  VrWindowManagerBinder(android::dvr::ShellViewBinderInterface& app)
+      : app_(app) {}
+  virtual ~VrWindowManagerBinder() {}
+
+  // Must be called before clients can connect.
+  // Returns 0 if initialization is successful.
+  int Initialize();
+  static char const* getServiceName() { return "vr_window_manager"; }
+
+ protected:
+  // Implements IVrWindowManagerBinder.
+  ::android::binder::Status connectController(
+      const ::android::base::unique_fd& fd) override;
+  ::android::binder::Status disconnectController() override;
+  ::android::binder::Status enterVrMode() override;
+  ::android::binder::Status exitVrMode() override;
+  ::android::binder::Status setDebugMode(int32_t mode) override;
+  ::android::binder::Status set2DMode(int32_t mode) override;
+  ::android::binder::Status setRotation(int32_t angle) override;
+
+  // Implements BBinder::dump().
+  status_t dump(int fd, const Vector<String16>& args) override;
+
+ private:
+  android::dvr::ShellViewBinderInterface& app_;
+
+  VrWindowManagerBinder(const VrWindowManagerBinder&) = delete;
+  void operator=(const VrWindowManagerBinder&) = delete;
+};
+
+}  // namespace vr
+}  // namespace service
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder_test.cpp b/services/vr/vr_window_manager/vr_window_manager_binder_test.cpp
new file mode 100644
index 0000000..f43e803
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager_binder_test.cpp
@@ -0,0 +1,29 @@
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/log.h>
+
+#include "vr_window_manager_binder.h"
+
+int main() {
+  ALOGI("Starting");
+  android::service::vr::VrWindowManagerBinder service;
+  const int status = service.Initialize();
+  LOG_ALWAYS_FATAL_IF(status != 0, "initialization failed: %d", status);
+
+  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::service::vr::VrWindowManagerBinder::SERVICE_NAME(), &service,
+      false /*allowIsolated*/);
+  LOG_ALWAYS_FATAL_IF(service_status != android::OK, "service not added: %d",
+                      static_cast<int>(service_status));
+
+  android::IPCThreadState::self()->joinThreadPool();
+  return 0;
+}
diff --git a/services/vr/vr_window_manager/vr_wm.rc b/services/vr/vr_window_manager/vr_wm.rc
new file mode 100644
index 0000000..e515bb7
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_wm.rc
@@ -0,0 +1,5 @@
+service vr_wm /system/bin/vr_wm
+  class core
+  user system
+  group system graphics input
+  writepid /dev/cpuset/system/tasks
diff --git a/services/vr/vr_window_manager/vr_wm_ctl.cpp b/services/vr/vr_window_manager/vr_wm_ctl.cpp
new file mode 100644
index 0000000..758e02b
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_wm_ctl.cpp
@@ -0,0 +1,52 @@
+#include <android/service/vr/BpVrWindowManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <inttypes.h>
+
+void usage() { fprintf(stderr, "usage: vr_wm_ctl [enter|exit|debug N]\n"); }
+
+int report(const android::binder::Status& status) {
+  if (status.isOk()) {
+    fprintf(stderr, "ok\n");
+    return 0;
+  }
+  fprintf(stderr, "failed (%" PRId32 ") %s\n", status.exceptionCode(),
+          status.exceptionMessage().string());
+  return (int)status.exceptionCode();
+}
+
+int main(int argc, char* argv[]) {
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+  if (sm == nullptr) {
+    fprintf(stderr, "service manager not found\n");
+    exit(1);
+  }
+
+  android::sp<android::service::vr::IVrWindowManager> vrwm =
+      android::interface_cast<android::service::vr::IVrWindowManager>(
+          sm->getService(
+              android::service::vr::IVrWindowManager::SERVICE_NAME()));
+  if (vrwm == nullptr) {
+    fprintf(stderr, "service not found\n");
+    exit(1);
+  }
+
+  android::binder::Status status;
+  if ((argc == 2) && (strcmp(argv[1], "enter") == 0)) {
+    exit(report(vrwm->enterVrMode()));
+  } else if ((argc == 2) && (strcmp(argv[1], "exit") == 0)) {
+    exit(report(vrwm->exitVrMode()));
+  } else if ((argc == 3) && (strcmp(argv[1], "debug") == 0)) {
+    exit(report(vrwm->setDebugMode(atoi(argv[2]))));
+  } else if ((argc == 3) && (strcmp(argv[1], "2d") == 0)) {
+    exit(report(vrwm->set2DMode(atoi(argv[2]))));
+  } else if ((argc == 3) && (strcmp(argv[1], "rotate") == 0)) {
+    exit(report(vrwm->setRotation(atoi(argv[2]))));
+  } else {
+    usage();
+    exit(2);
+  }
+
+  return 0;
+}
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index f5be518..91c270e 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -23,7 +23,7 @@
     license: "include/vulkan/NOTICE",
 }
 
-cc_library_static {
+cc_library_headers {
     name: "vulkan_headers",
     export_include_dirs: ["include"],
     vendor_available: true,
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 0616711..a19fcf1 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -89,7 +89,7 @@
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME             "VK_KHR_win32_surface"
 
 // 11
-@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     5
+@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"
 
 // 12
@@ -316,6 +316,14 @@
 @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"
@@ -400,6 +408,9 @@
 
     //@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 {
@@ -917,6 +928,8 @@
 
     //@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") // 12
     VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT     = 1000011000,
@@ -1073,6 +1086,14 @@
     //@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,
 
@@ -1164,6 +1185,10 @@
     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") // 1
@@ -1873,6 +1898,13 @@
 //bitfield VkWin32SurfaceCreateFlagBitsKHR {
 //}
 
+@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") // 12
@@ -3323,13 +3355,34 @@
 }
 
 @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_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
@@ -4208,6 +4261,34 @@
     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
@@ -6758,11 +6839,24 @@
 }
 
 @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") // 11
+@optional
+cmd VkResult vkGetSwapchainGrallocUsage2ANDROID(
+        VkDevice                                device,
+        VkFormat                                format,
+        VkImageUsageFlags                       imageUsage,
+        VkSwapchainImageUsageFlagsANDROID       swapchainImageUsage,
+        u64*                                    grallocConsumerUsage,
+        u64*                                    grallocProducerUsage) {
     return ?
 }
 
@@ -7327,6 +7421,12 @@
         VkDevice                                    device,
         VkSwapchainKHR                              swapchain,
         VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties) {
+    deviceObject := GetDevice(device)
+    swapchainObject := GetSwapchain(swapchain)
+
+    displayTimingProperties := ?
+    pDisplayTimingProperties[0] = displayTimingProperties
+
     return ?
 }
 
@@ -7355,6 +7455,30 @@
         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,
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/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 72f8049..6dc5eb5 100644
--- a/vulkan/include/vulkan/vk_platform.h
+++ b/vulkan/include/vulkan/vk_platform.h
@@ -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 ef0c246..67eba86 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -47,7 +47,7 @@
 
 
 #define VK_NULL_HANDLE 0
-        
+
 
 
 #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
@@ -60,7 +60,7 @@
         #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
 #endif
 #endif
-        
+
 
 
 typedef uint32_t VkFlags;
@@ -301,6 +301,10 @@
     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,
@@ -590,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),
@@ -3323,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),
@@ -3787,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"
@@ -3798,7 +3804,7 @@
     VkStructureType                   sType;
     const void*                       pNext;
     VkAndroidSurfaceCreateFlagsKHR    flags;
-    ANativeWindow*                    window;
+    struct ANativeWindow*             window;
 } VkAndroidSurfaceCreateInfoKHR;
 
 
@@ -5764,6 +5770,66 @@
     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
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index e1f164a..6149894 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -47,6 +47,7 @@
         "-Wno-c99-extensions",
         "-Wno-c++98-compat-pedantic",
         "-Wno-exit-time-destructors",
+        "-Wno-float-equal",
         "-Wno-global-constructors",
         "-Wno-zero-length-array",
     ],
@@ -63,19 +64,21 @@
         "vulkan_loader_data.cpp",
     ],
 
-    export_static_lib_headers: ["vulkan_headers"],
-    static_libs: [
+    export_header_lib_headers: ["vulkan_headers"],
+    header_libs: [
         "vulkan_headers",
-        "libziparchive",
     ],
     shared_libs: [
-        "libgui",
+        "libziparchive",
         "libhardware",
         "libsync",
         "libbase",
         "liblog",
+        "libui",
         "libutils",
         "libcutils",
         "libz",
+        "libnativewindow",
     ],
+    static_libs: ["libgrallocusage"],
 }
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 36755a2..e05ca5a 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -1050,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,
@@ -1075,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 ea6fadc..54c77f1 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -31,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;                                           \
         }                                                              \
@@ -43,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)
@@ -120,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;
@@ -151,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;
@@ -473,7 +473,9 @@
         "vkGetPhysicalDeviceQueueFamilyProperties2KHR",
         "vkGetPhysicalDeviceSparseImageFormatProperties",
         "vkGetPhysicalDeviceSparseImageFormatProperties2KHR",
+        "vkGetPhysicalDeviceSurfaceCapabilities2KHR",
         "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
+        "vkGetPhysicalDeviceSurfaceFormats2KHR",
         "vkGetPhysicalDeviceSurfaceFormatsKHR",
         "vkGetPhysicalDeviceSurfacePresentModesKHR",
         "vkGetPhysicalDeviceSurfaceSupportKHR",
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index b5d51c6..5bbe116 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -319,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}};
@@ -393,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;                                      \
       }                                                         \
@@ -419,6 +419,8 @@
     INIT_PROC(§
   {{end}}
 
+  {{if GetAnnotation $ "optional"}}false{{else}}true{{end}}, §
+
   {{if (Macro "IsInstanceDispatched" $)}}
     instance, §
   {{else}}
@@ -494,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)
@@ -680,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}}
 
 
@@ -770,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}}
@@ -796,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}}
 
@@ -958,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"}}
@@ -1132,8 +1153,9 @@
 
 {{/*
 ------------------------------------------------------------------------------
-  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/driver.cpp b/vulkan/libvulkan/driver.cpp
index f9d3de1..f2cd8e6 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -28,11 +28,20 @@
 
 #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*);
+}
+
+// Set to true to enable exposing unratified extensions for development
+static const bool kEnableUnratifiedExtensions = false;
+
 // #define ENABLE_ALLOC_CALLSTACKS 1
 #if ENABLE_ALLOC_CALLSTACKS
 #include <utils/CallStack.h>
@@ -146,14 +155,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) {
@@ -167,7 +174,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);
@@ -179,11 +186,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");
 
@@ -193,9 +213,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);
@@ -446,6 +478,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;
@@ -454,6 +488,7 @@
                 hook_extensions_.set(ext_bit);
                 break;
             case ProcHook::EXTENSION_UNKNOWN:
+            case ProcHook::KHR_get_physical_device_properties2:
                 // HAL's extensions
                 break;
             default:
@@ -467,6 +502,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;
@@ -662,12 +706,24 @@
     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});
+
+    if (kEnableUnratifiedExtensions) {
+        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,
     };
@@ -719,32 +775,107 @@
     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});
+
+    if (kEnableUnratifiedExtensions) {
+        // conditionally add shared_presentable_image if supportable
+        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;
@@ -862,7 +993,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 e058439..7f8ae98 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -102,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 e9cbceb..82b464e 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -75,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[] = {
@@ -220,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,
@@ -227,6 +276,13 @@
         nullptr,
     },
     {
+        "vkGetPhysicalDeviceSurfaceFormats2KHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_get_surface_capabilities2,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceFormats2KHR),
+        nullptr,
+    },
+    {
         "vkGetPhysicalDeviceSurfaceFormatsKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
@@ -248,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,
@@ -262,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,
@@ -275,6 +352,13 @@
         nullptr,
         nullptr,
     },
+    {
+        "vkSetHdrMetadataEXT",
+        ProcHook::DEVICE,
+        ProcHook::EXT_hdr_metadata,
+        reinterpret_cast<PFN_vkVoidFunction>(SetHdrMetadataEXT),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedSetHdrMetadataEXT),
+    },
     // clang-format on
 };
 
@@ -294,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,
@@ -326,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;
@@ -346,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 a54e89d..3b26a80 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -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 6e44126..05856d3 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -69,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/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index bfe7aa7..caa2674 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,10 +16,12 @@
 
 #include <algorithm>
 
+#include <grallocusage/GrallocUsageConversion.h>
 #include <log/log.h>
-#include <gui/BufferQueue.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,37 +621,148 @@
         {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;
-        *count = std::min(*count, kNumFormats);
-        std::copy(kFormats, kFormats + *count, 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 = kNumFormats;
+        *count = total_num_formats;
     }
     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;
-        *count = std::min(*count, kNumModes);
-        std::copy(kModes, kModes + *count, modes);
+        *count = std::min(*count, num_modes);
+        std::copy(present_modes.begin(), present_modes.begin() + int(*count), modes);
     } else {
-        *count = kNumModes;
+        *count = num_modes;
     }
     return result;
 }
@@ -413,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);
 
@@ -465,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(
@@ -521,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
@@ -541,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(
@@ -551,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;
@@ -563,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 --
@@ -624,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,
@@ -663,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;
@@ -677,6 +1092,12 @@
         image_native_buffer.stride = img.buffer->stride;
         image_native_buffer.format = img.buffer->format;
         image_native_buffer.usage = img.buffer->usage;
+        // TODO: Adjust once ANativeWindowBuffer supports gralloc1-style usage.
+        // For now, this is the same translation Gralloc1On0Adapter does.
+        image_native_buffer.usage2.consumer =
+            static_cast<uint64_t>(img.buffer->usage);
+        image_native_buffer.usage2.producer =
+            static_cast<uint64_t>(img.buffer->usage);
 
         result =
             dispatch.CreateImage(device, &image_create, nullptr, &img.image);
@@ -692,17 +1113,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);
+            }
         }
     }
 
@@ -728,6 +1151,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)
@@ -783,6 +1209,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);
@@ -790,7 +1226,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;
@@ -862,17 +1298,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;
@@ -890,6 +1365,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) {
@@ -904,6 +1454,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);
@@ -920,9 +1494,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
index ea3b781..3329609 100644
--- a/vulkan/nulldrv/Android.bp
+++ b/vulkan/nulldrv/Android.bp
@@ -42,6 +42,6 @@
         "null_driver_gen.cpp",
     ],
 
-    static_libs: ["vulkan_headers"],
+    header_libs: ["vulkan_headers"],
     shared_libs: ["liblog"],
 }
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index e966c06..6714779 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -269,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]);
 
@@ -286,7 +279,6 @@
     if (properties)
         std::copy(kExtensions, kExtensions + *count, properties);
     return *count < kExtensionsCount ? VK_INCOMPLETE : VK_SUCCESS;
-#endif
 }
 
 VKAPI_ATTR
@@ -310,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]);
@@ -514,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,
@@ -529,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) {
@@ -544,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{
@@ -605,6 +638,10 @@
     };
 }
 
+void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physical_device, VkPhysicalDeviceFeatures2KHR* features) {
+    GetPhysicalDeviceFeatures(physical_device, &features->features);
+}
+
 // -----------------------------------------------------------------------------
 // Device
 
@@ -887,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,
@@ -1073,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;
@@ -1130,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;
diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl
index 209d61d..ce15517 100644
--- a/vulkan/nulldrv/null_driver.tmpl
+++ b/vulkan/nulldrv/null_driver.tmpl
@@ -205,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 fd87161..25ee65a 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -171,15 +171,23 @@
     {"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))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 43cd45e..8a9a963 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -165,12 +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, int* grallocUsage);
+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/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 801eca8..89bc926 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -176,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[] = {