Merge "Merge "Fix security vulnerability" into nyc-dev am: 2188ad799e am: 80672db6b8 am: 900cd6a1d2 am: c1038b66dd am: 64cae76fe0" into nyc-mr2-dev-plus-aosp
am: 0ad3235330

Change-Id: Ibf1f56258c1421760eb4c7044fa0e85c7e1af248
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 64f7b82..4be0432 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -383,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;
@@ -1040,7 +1040,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/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 a352259..efc050b 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,20 +1,182 @@
 LOCAL_PATH:= $(call my-dir)
 
+# ================#
+# Common settings #
+# ================#
+# ZipArchive support, the order matters here to get all symbols.
+COMMON_ZIP_LIBRARIES := libziparchive libz libcrypto
+
+# TODO: ideally the tests should depend on a shared dumpstate library, but currently libdumpstate
+# is used to define the device-specific HAL library. Instead, both dumpstate and dumpstate_test
+# shares a lot of common settings
+COMMON_LOCAL_CFLAGS := \
+       -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+COMMON_SRC_FILES := \
+        DumpstateInternal.cpp \
+        utils.cpp
+COMMON_SHARED_LIBRARIES := \
+        android.hardware.dumpstate@1.0 \
+        libhidlbase \
+        libbase \
+        libbinder \
+        libcutils \
+        libdebuggerd_client \
+        libdumpstateaidl \
+        libdumpstateutil \
+        liblog \
+        libselinux \
+        libutils \
+        $(COMMON_ZIP_LIBRARIES)
+
+# ====================#
+# libdumpstateutil #
+# ====================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdumpstateutil
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_SRC_FILES := \
+        DumpstateInternal.cpp \
+        DumpstateUtil.cpp
+LOCAL_SHARED_LIBRARIES := \
+        libbase \
+        liblog \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ====================#
+# libdumpstateheaders #
+# ====================#
+# TODO: this module is necessary so the device-specific libdumpstate implementations do not
+# need to add any other dependency (like libbase). Should go away once dumpstate HAL changes.
+include $(CLEAR_VARS)
+
+LOCAL_EXPORT_C_INCLUDE_DIRS = $(LOCAL_PATH)
+LOCAL_MODULE := libdumpstateheaders
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+        $(COMMON_SHARED_LIBRARIES)
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
+        $(COMMON_STATIC_LIBRARIES)
+# Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_
+LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS)
+LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ================ #
+# libdumpstateaidl #
+# =================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdumpstateaidl
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+        libbinder \
+        libutils
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/binder
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_SRC_FILES := \
+        binder/android/os/IDumpstate.aidl \
+        binder/android/os/IDumpstateListener.aidl \
+        binder/android/os/IDumpstateToken.aidl
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ==========#
+# dumpstate #
+# ==========#
 include $(CLEAR_VARS)
 
 ifdef BOARD_WLAN_DEVICE
 LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
 endif
 
-LOCAL_SRC_FILES := dumpstate.cpp utils.cpp
+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) \
+    android.hardware.vibrator@1.0
+
+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..5430956
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -0,0 +1,104 @@
+/**
+ * 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());
+
+    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 a2a7def..6dbb967 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,40 @@
 #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 <android/hardware/vibrator/1.0/IVibrator.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::hardware::vibrator::V1_0::IVibrator;
+using VibratorStatus = ::android::hardware::vibrator::V1_0::Status;
 
-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 +95,54 @@
 
 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 constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
+static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
+static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
+
+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,288 @@
 
     // 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", "-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", "-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", "-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", "-d", "*:v"});
 }
 
-static void dumpstate(const std::string& screenshot_path, const std::string& version) {
+static void DumpIpTables() {
+    RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
+    RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
+    RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
+    /* no ip6 nat */
+    RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
+    RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
+    RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
+    RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
+}
+
+static void AddAnrTraceFiles() {
+    bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+    std::string dump_traces_dir;
+
+    /* show the traces we collected in main(), if that was done */
+    if (dump_traces_path != nullptr) {
+        if (add_to_zip) {
+            dump_traces_dir = dirname(dump_traces_path);
+            MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
+            ds.AddDir(dump_traces_dir, true);
+        } else {
+            MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
+                   dump_traces_path);
+            ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
+        }
+    }
+
+    std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+    std::string anr_traces_dir = dirname(anr_traces_path.c_str());
+
+    // Make sure directory is not added twice.
+    // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
+    // generated by dump_traces() -  and anr_traces_path - which is retrieved from a system
+    // property - but in reality they're the same path (although the former could be nullptr).
+    // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
+    // be revisited.
+    bool already_dumped = anr_traces_dir == dump_traces_dir;
+
+    MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
+           dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
+
+    if (anr_traces_path.empty()) {
+        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
+    } else {
+        int fd = TEMP_FAILURE_RETRY(
+            open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+        if (fd < 0) {
+            printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
+                   strerror(errno));
+        } else {
+            if (add_to_zip) {
+                if (!already_dumped) {
+                    MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
+                           anr_traces_dir.c_str());
+                    ds.AddDir(anr_traces_dir, true);
+                    already_dumped = true;
+                }
+            } else {
+                MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
+                       anr_traces_path.c_str());
+                dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
+            }
+        }
+    }
+
+    if (add_to_zip && already_dumped) {
+        MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
+        return;
+    }
+
+    /* slow traces for slow operations */
+    struct stat st;
+    if (!anr_traces_path.empty()) {
+        int tail = anr_traces_path.size() - 1;
+        while (tail > 0 && anr_traces_path.at(tail) != '/') {
+            tail--;
+        }
+        int i = 0;
+        while (1) {
+            anr_traces_path = anr_traces_path.substr(0, tail + 1) +
+                              android::base::StringPrintf("slow%02d.txt", i);
+            if (stat(anr_traces_path.c_str(), &st)) {
+                // No traces file at this index, done with the files.
+                break;
+            }
+            ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
+            i++;
+        }
+    }
+}
+
+static void dumpstate() {
     DurationReporter duration_reporter("DUMPSTATE");
 
     dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
-    run_command("UPTIME", 10, "uptime", NULL);
+    RunCommand("UPTIME", {"uptime"});
     dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
     dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
-    dump_file("MEMORY INFO", "/proc/meminfo");
-    run_command("CPU INFO", 10, "top", "-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");
+    DumpFile("MEMORY INFO", "/proc/meminfo");
+    RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
+                            "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
+    RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20);
+    DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
+    DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
+    DumpFile("SLAB INFO", "/proc/slabinfo");
+    DumpFile("ZONEINFO", "/proc/zoneinfo");
+    DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo");
+    DumpFile("BUDDYINFO", "/proc/buddyinfo");
+    DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
 
-    dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
-    dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
-    dump_file("KERNEL SYNC", "/d/sync");
+    DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources");
+    DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
+    DumpFile("KERNEL SYNC", "/d/sync");
 
-    run_command("PROCESSES AND THREADS", 10, "ps", "-A", "-T", "-Z",
-                "-O", "pri,nice,rtprio,sched,pcy", NULL);
-    run_command("LIBRANK", 10, SU_PATH, "root", "librank", NULL);
+    RunCommand("PROCESSES AND THREADS",
+               {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
+    RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT);
 
-    run_command("PRINTENV", 10, "printenv", NULL);
-    run_command("NETSTAT", 10, "netstat", "-nW", NULL);
-    run_command("LSMOD", 10, "lsmod", NULL);
+    RunCommand("HARDWARE HALS", {"lshal"});
+
+    RunCommand("PRINTENV", {"printenv"});
+    RunCommand("NETSTAT", {"netstat", "-nW"});
+    struct stat s;
+    if (stat("/proc/modules", &s) != 0) {
+        MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
+    } else {
+        RunCommand("LSMOD", {"lsmod"});
+    }
 
     do_dmesg();
 
-    run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
+    RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
     for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
     for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
     for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
 
     /* Dump Bluetooth HCI logs */
-    add_dir("/data/misc/bluetooth/logs", true);
+    ds.AddDir("/data/misc/bluetooth/logs", true);
 
-    if (!screenshot_path.empty()) {
+    if (!ds.do_early_screenshot_) {
         MYLOGI("taking late screenshot\n");
-        take_screenshot(screenshot_path);
-        MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
+        ds.TakeScreenshot();
     }
 
-    do_logcat();
+    DoLogcat();
 
-    /* show the traces we collected in main(), if that was done */
-    if (dump_traces_path != NULL) {
-        dump_file("VM TRACES JUST NOW", dump_traces_path);
-    }
-
-    /* only show ANR traces if they're less than 15 minutes old */
-    struct stat st;
-    char anr_traces_path[PATH_MAX];
-    property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
-    if (!anr_traces_path[0]) {
-        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
-    } else {
-      int fd = TEMP_FAILURE_RETRY(open(anr_traces_path,
-                                       O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
-      if (fd < 0) {
-          printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
-      } else {
-          dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
-      }
-    }
-
-    /* slow traces for slow operations */
-    if (anr_traces_path[0] != 0) {
-        int tail = strlen(anr_traces_path)-1;
-        while (tail > 0 && anr_traces_path[tail] != '/') {
-            tail--;
-        }
-        int i = 0;
-        while (1) {
-            sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
-            if (stat(anr_traces_path, &st)) {
-                // No traces file at this index, done with the files.
-                break;
-            }
-            dump_file("VM TRACES WHEN SLOW", anr_traces_path);
-            i++;
-        }
-    }
+    AddAnrTraceFiles();
 
     int dumped = 0;
     for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -969,8 +999,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,214 +1014,291 @@
         printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
     }
 
-    dump_file("NETWORK DEV INFO", "/proc/net/dev");
-    dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
-    dump_file("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
-    dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
-    dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
+    DumpFile("NETWORK DEV INFO", "/proc/net/dev");
+    DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
+    DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
+    DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
+    DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
 
-    do_kmsg();
+    DoKmsg();
 
     /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
 
-    run_command("NETWORK INTERFACES", 10, "ip", "link", NULL);
+    RunCommand("NETWORK INTERFACES", {"ip", "link"});
 
-    run_command("IPv4 ADDRESSES", 10, "ip", "-4", "addr", "show", NULL);
-    run_command("IPv6 ADDRESSES", 10, "ip", "-6", "addr", "show", NULL);
+    RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
+    RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
 
-    run_command("IP RULES", 10, "ip", "rule", "show", NULL);
-    run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
+    RunCommand("IP RULES", {"ip", "rule", "show"});
+    RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
 
     dump_route_tables();
 
-    run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL);
-    run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL);
-    run_command("MULTICAST ADDRESSES", 10, "ip", "maddr", NULL);
-    run_command("WIFI NETWORKS", 20, "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
+    RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"});
+    RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
+    RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
+    RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
+               CommandOptions::WithTimeout(20).Build());
 
 #ifdef FWDUMP_bcmdhd
-    run_command("ND OFFLOAD TABLE", 5,
-            SU_PATH, "root", WLUTIL, "nd_hostip", NULL);
+    RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT);
 
-    run_command("DUMP WIFI INTERNAL COUNTERS (1)", 20,
-            SU_PATH, "root", WLUTIL, "counters", NULL);
+    RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, AS_ROOT_20);
 
-    run_command("ND OFFLOAD STATUS (1)", 5,
-            SU_PATH, "root", WLUTIL, "nd_status", NULL);
+    RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT);
 
 #endif
-    dump_file("INTERRUPTS (1)", "/proc/interrupts");
+    DumpFile("INTERRUPTS (1)", "/proc/interrupts");
 
-    run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "-t", "10", "connectivity", "--diag", NULL);
+    RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
+               CommandOptions::WithTimeout(10).Build());
 
 #ifdef FWDUMP_bcmdhd
-    run_command("DUMP WIFI STATUS", 20,
-            SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", NULL);
+    RunCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, AS_ROOT_20);
 
-    run_command("DUMP WIFI INTERNAL COUNTERS (2)", 20,
-            SU_PATH, "root", WLUTIL, "counters", NULL);
+    RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, AS_ROOT_20);
 
-    run_command("ND OFFLOAD STATUS (2)", 5,
-            SU_PATH, "root", WLUTIL, "nd_status", NULL);
+    RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT);
 #endif
-    dump_file("INTERRUPTS (2)", "/proc/interrupts");
+    DumpFile("INTERRUPTS (2)", "/proc/interrupts");
 
     print_properties();
 
-    run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
-    run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
+    RunCommand("VOLD DUMP", {"vdc", "dump"});
+    RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
 
-    run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
+    RunCommand("STORAGED TASKIOINFO", {"storaged", "-u"}, CommandOptions::WithTimeout(10).Build());
 
-    run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
+    RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
+
+    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", "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.
+    // 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("dumpstate"));
+    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;
@@ -1233,7 +1340,6 @@
 }
 
 int main(int argc, char *argv[]) {
-    struct sigaction sigact;
     int do_add_date = 0;
     int do_zip_file = 0;
     int do_vibrate = 1;
@@ -1242,33 +1348,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);
@@ -1282,60 +1370,132 @@
     }
 
     /* 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, "");
+    }
+
+    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.
@@ -1345,98 +1505,77 @@
 
     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.device", "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
+
+                // NOTE: flag must be kept in sync when the value of
+                // FLAG_RECEIVER_INCLUDE_BACKGROUND is changed.
                 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),
+                     "-f", "0x01000000",
+                     "--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);
             }
             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());
             }
         }
     }
@@ -1448,66 +1587,75 @@
         fclose(cmdline);
     }
 
-    /* open the vibrator before dropping root */
-    std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
+    ::android::sp<IVibrator> vibrator = nullptr;
     if (do_vibrate) {
-        vibrator.reset(fopen("/sys/class/timed_output/vibrator/enable", "we"));
-        if (vibrator) {
-            vibrate(vibrator.get(), 150);
-        }
-    }
+        vibrator = IVibrator::getService();
 
-    if (do_fb && do_early_screenshot) {
-        if (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));
+        if (vibrator != nullptr) {
+            // cancel previous vibration if any
+            ::android::hardware::Return<VibratorStatus> offStatus = vibrator->off();
+            if (!offStatus.isOk() || offStatus != VibratorStatus::OK) {
+                MYLOGE("Vibrator off failed.");
+            } else {
+                ::android::hardware::Return<VibratorStatus> onStatus = vibrator->on(150);
+                if (!onStatus.isOk() || onStatus != VibratorStatus::OK) {
+                    MYLOGE("Vibrator on failed.");
+                }
             }
         }
     }
 
+    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");
+            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
@@ -1516,40 +1664,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 */
@@ -1561,106 +1713,119 @@
     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);
+    if (vibrator != nullptr) {
+        // in case dumpstate magically completes before the above vibration
+        ::android::hardware::Return<VibratorStatus> offStatus = vibrator->off();
+        if (!offStatus.isOk() || offStatus != VibratorStatus::OK) {
+            MYLOGE("Vibrator off failed.");
+        } else {
+            for (int i = 0; i < 3; i++) {
+                ::android::hardware::Return<VibratorStatus> onStatus = vibrator->on(75);
+                if (!onStatus.isOk() || onStatus != VibratorStatus::OK) {
+                    MYLOGE("Vibrator on failed.");
+                    break;
+                }
+                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
+
+            // NOTE: flag must be kept in sync when the value of
+            // FLAG_RECEIVER_INCLUDE_BACKGROUND is changed.
             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
+                 "-f", "0x01000000",
+                 "--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 (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));
+                am_args.push_back(SHA256_file_hash(ds.path_));
                 send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
             } else {
                 send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
@@ -1670,16 +1835,18 @@
         }
     }
 
-    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 1b28c49..b2cd241 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -14,93 +14,337 @@
  * 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_;
+
+  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,35 +360,12 @@
  * 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();
+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));
 
 /* sends a broadcast using Activity Manager */
 void send_broadcast(const std::string& action, const std::vector<std::string>& args);
 
-/* updates the overall progress of dumpstate by the given weight increment */
-void update_progress(int weight);
-
 /* prints all the system properties */
 void print_properties();
 
@@ -154,9 +375,12 @@
 /* redirect output to a service control socket */
 void redirect_to_socket(FILE *redirect, const char *service);
 
-/* redirect output to a file */
+/* redirect output to a new file */
 void redirect_to_file(FILE *redirect, char *path);
 
+/* redirect output to an existing file */
+void redirect_to_existing_file(FILE *redirect, char *path);
+
 /* create leading directories, if necessary */
 void create_parent_dirs(const char *path);
 
@@ -187,15 +411,6 @@
 /* Play a sound via Stagefright */
 void play_sound(const char *path);
 
-/* Implemented by libdumpstate_board to dump board-specific info */
-void dumpstate_board();
-
-/* Takes a screenshot and save it to the given file */
-void take_screenshot(const std::string& path);
-
-/* Vibrates for a given durating (in milliseconds). */
-void vibrate(FILE* vibrator, int ms);
-
 /* Checks if a given path is a directory. */
 bool is_dir(const char* pathname);
 
@@ -208,34 +423,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 1ee1bac..c6dfa37 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>
@@ -40,17 +42,33 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.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;
+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.
@@ -68,37 +86,169 @@
         NULL,
 };
 
-DurationReporter::DurationReporter(const char *title) : DurationReporter(title, stdout) {}
+// Reasonable value for max stats.
+static const int STATS_MAX_N_RUNS = 1000;
+static const long STATS_MAX_AVERAGE = 100000;
 
-DurationReporter::DurationReporter(const char *title, FILE *out) {
-    title_ = title;
-    if (title) {
-        started_ = DurationReporter::nanotime();
+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 +330,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 +387,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 +464,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 +522,7 @@
     if (iotime) {
         snprdec(buffer, sizeof(buffer), 79, permille);
     }
-    puts(buffer); // adds a trailing newline
+    puts(buffer);  // adds a trailing newline
 
     return;
 }
@@ -371,7 +532,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 +563,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 +603,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 +612,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 +646,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 +661,7 @@
         (*dump_from_fd)(NULL, newpath, fd);
     }
     closedir(dirp);
-    if (title) {
+    if (!title.empty()) {
         printf("\n");
     }
     return retval;
@@ -578,7 +672,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 +684,43 @@
         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 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);
 }
 
 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);
+    std::vector<std::string> am = {"/system/bin/am", "broadcast", "--user", "0", "-a", action};
+
+    am.insert(am.end(), args.begin(), args.end());
+
+    RunCommand("", am, CommandOptions::WithTimeout(20)
+                           .Log("Sending broadcast: '%s'\n")
+                           .Always()
+                           .DropRoot()
+                           .RedirectStderr()
+                           .Build());
 }
 
 size_t num_props = 0;
@@ -903,7 +744,7 @@
     const char* title = "SYSTEM PROPERTIES";
     DurationReporter duration_reporter(title);
     printf("------ %s ------\n", title);
-    ON_DRY_RUN_RETURN();
+    if (PropertiesHelper::IsDryRun()) return;
     size_t i;
     num_props = 0;
     property_list(print_prop, NULL);
@@ -974,11 +815,11 @@
     }
 }
 
-/* redirect output to a file */
-void redirect_to_file(FILE *redirect, char *path) {
+void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) {
     create_parent_dirs(path);
 
-    int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+    int fd = TEMP_FAILURE_RETRY(open(path,
+                                     O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW,
                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
     if (fd < 0) {
         MYLOGE("%s: %s\n", path, strerror(errno));
@@ -989,6 +830,14 @@
     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_native_traces(const char* path) {
     for (const char** p = native_processes_to_dump; *p; p++) {
         if (!strcmp(*p, path)) {
@@ -1000,37 +849,33 @@
 
 /* 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 */
@@ -1051,9 +896,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;
     }
 
@@ -1086,7 +931,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;
@@ -1094,7 +939,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) {
@@ -1107,8 +952,8 @@
             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)) {
             /* dump native process if appropriate */
@@ -1116,7 +961,7 @@
                 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) {
@@ -1127,8 +972,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);
             }
         }
     }
@@ -1137,17 +982,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);
@@ -1158,9 +1003,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));
@@ -1171,72 +1016,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) {
@@ -1280,19 +1120,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;
@@ -1306,8 +1143,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;
     }
 
@@ -1317,11 +1153,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;
@@ -1350,48 +1185,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/installd/Android.mk b/cmds/installd/Android.mk
index 8bf0b53..f567a10 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.
@@ -35,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/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index cdef7e1..535d060 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";
@@ -79,10 +81,6 @@
 static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
 static constexpr const char* IDMAP_SUFFIX = "@idmap";
 
-// NOTE: keep in sync with StorageManager
-static constexpr int FLAG_STORAGE_DE = 1 << 0;
-static constexpr int FLAG_STORAGE_CE = 1 << 1;
-
 // NOTE: keep in sync with Installer
 static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
 static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
@@ -90,7 +88,6 @@
 static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
 static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 14;
 
-#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
 namespace {
 
 constexpr const char* kDump = "android.permission.DUMP";
@@ -285,73 +282,6 @@
     return 0;
 }
 
-/**
- * Prepare an app cache directory, which offers to fix-up the GID and
- * directory mode flags during a platform upgrade.
- */
-static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
-        uid_t uid, gid_t gid) {
-    auto path = StringPrintf("%s/%s", parent.c_str(), name);
-    struct stat st;
-    if (stat(path.c_str(), &st) != 0) {
-        if (errno == ENOENT) {
-            // This is fine, just create it
-            if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
-                PLOG(ERROR) << "Failed to prepare " << path;
-                return -1;
-            } else {
-                return 0;
-            }
-        } else {
-            PLOG(ERROR) << "Failed to stat " << path;
-            return -1;
-        }
-    }
-
-    mode_t actual_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
-    if (st.st_uid != uid) {
-        // Mismatched UID is real trouble; we can't recover
-        LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid
-                << " but expected " << uid;
-        return -1;
-    } else if (st.st_gid == gid && actual_mode == target_mode) {
-        // Everything looks good!
-        return 0;
-    }
-
-    // Directory is owned correctly, but GID or mode mismatch means it's
-    // probably a platform upgrade so we need to fix them
-    FTS *fts;
-    FTSENT *p;
-    char *argv[] = { (char*) path.c_str(), nullptr };
-    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_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) {
-                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) {
-                PLOG(WARNING) << "Failed to chown " << p->fts_path;
-            }
-            break;
-        case FTS_SL:
-        case FTS_SLNONE:
-            if (lchown(p->fts_accpath, -1, gid) != 0) {
-                PLOG(WARNING) << "Failed to chown " << p->fts_path;
-            }
-            break;
-        }
-    }
-    fts_close(fts);
-    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) {
@@ -960,6 +890,10 @@
             add_cache_files(cache,
                     StringPrintf("%s/Android/data", create_data_media_path(uuid_, user).c_str()));
         }
+        // Add files from /data/preloads/file_cache
+        if (uuid == nullptr) {
+            add_preloads_file_cache(cache, uuid_);
+        }
         ATRACE_END();
 
         ATRACE_BEGIN("clear");
@@ -1889,6 +1823,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) {
@@ -2013,6 +1963,20 @@
     return res ? ok() : error();
 }
 
+binder::Status InstalldNativeService::reconcileSecondaryDexFile(
+        const std::string& dexPath, const std::string& packageName, int32_t uid,
+        const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
+        int32_t storage_flag, bool* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(volumeUuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+    bool result = android::installd::reconcile_secondary_dex_file(
+            dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return);
+    return result ? ok() : error();
+}
+
 binder::Status InstalldNativeService::invalidateMounts() {
     ENFORCE_UID(AID_SYSTEM);
     std::lock_guard<std::recursive_mutex> lock(mLock);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 0a9f12f..b3dbaf4 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -92,6 +92,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,
@@ -105,6 +106,9 @@
             const std::string& outputPath);
     binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
             const std::string& outputPath);
+    binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
+        const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
+        const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
 
     binder::Status invalidateMounts();
 
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index aa5e4f2..b45df87 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -58,6 +58,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);
@@ -71,5 +72,9 @@
     void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
             @utf8InCpp String outputPath);
 
+    boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
+        int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
+        int storage_flag);
+
     void invalidateMounts();
 }
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 5d84157..aa32d6b 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -31,6 +31,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <cutils/fs.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <log/log.h>               // TODO: Move everything to base/logging.
@@ -875,7 +876,7 @@
 }
 
 static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
-            const char* oat_dir, /*out*/ char* out_oat_path) {
+            const char* oat_dir, bool is_secondary_dex, /*out*/ char* out_oat_path) {
     // Early best-effort check whether we can fit the the path into our buffers.
     // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
     // without a swap file, if necessary. Reference profiles file also add an extra ".prof"
@@ -886,7 +887,8 @@
     }
 
     if (!IsOutputDalvikCache(oat_dir)) {
-        if (validate_apk_path(oat_dir)) {
+        // Oat dirs for secondary dex files are already validated.
+        if (!is_secondary_dex && validate_apk_path(oat_dir)) {
             ALOGE("cannot validate apk path with oat_dir '%s'\n", oat_dir);
             return false;
         }
@@ -1079,10 +1081,11 @@
 // Opens the reference profiles if needed.
 // Note that the reference profile might not exist so it's OK if the fd will be -1.
 Dex2oatFileWrapper maybe_open_reference_profile(const char* pkgname, bool profile_guided,
-        bool is_public, int uid) {
+        bool is_public, int uid, bool is_secondary_dex) {
     // Public apps should not be compiled with profile information ever. Same goes for the special
     // package '*' used for the system server.
-    if (profile_guided && !is_public && (pkgname[0] != '*')) {
+    // TODO(calin): add support for writing profiles for secondary dex files
+    if (profile_guided && !is_secondary_dex && !is_public && (pkgname[0] != '*')) {
         // Open reference profile in read only mode as dex2oat does not get write permissions.
         const std::string pkgname_str(pkgname);
         return Dex2oatFileWrapper(
@@ -1173,8 +1176,9 @@
 // Opens the output oat file for the given apk.
 // If successful it stores the output path into out_oat_path and returns true.
 Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir,
-        bool is_public, int uid, const char* instruction_set, char* out_oat_path) {
-    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_oat_path)) {
+        bool is_public, int uid, const char* instruction_set, bool is_secondary_dex,
+        char* out_oat_path) {
+    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
         return Dex2oatFileWrapper();
     }
     const std::string out_oat_path_str(out_oat_path);
@@ -1182,7 +1186,7 @@
             open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
             [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
     if (wrapper_fd.get() < 0) {
-        ALOGE("installd cannot open '%s' for output during dexopt\n", out_oat_path);
+        PLOG(ERROR) << "installd cannot open output during dexopt" <<  out_oat_path;
     } else if (!set_permissions_and_ownership(wrapper_fd.get(), is_public, uid, out_oat_path)) {
         ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
         wrapper_fd.reset(-1);
@@ -1207,9 +1211,189 @@
     }
 }
 
-int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set,
-        int dexopt_needed, const char* oat_dir, int dexopt_flags,const char* compiler_filter,
-        const char* volume_uuid ATTRIBUTE_UNUSED, const char* shared_libraries) {
+// Runs (execv) dexoptanalyzer on the given arguments.
+static void exec_dexoptanalyzer(const char* dex_file, const char* instruction_set,
+        const char* compiler_filter) {
+    static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer";
+    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
+
+    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
+        ALOGE("Instruction set %s longer than max length of %d",
+              instruction_set, MAX_INSTRUCTION_SET_LEN);
+        return;
+    }
+
+    char dex_file_arg[strlen("--dex-file=") + PKG_PATH_MAX];
+    char isa_arg[strlen("--isa=") + MAX_INSTRUCTION_SET_LEN];
+    char compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
+
+    sprintf(dex_file_arg, "--dex-file=%s", dex_file);
+    sprintf(isa_arg, "--isa=%s", instruction_set);
+    sprintf(compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
+
+    // program name, dex file, isa, filter, the final NULL
+    const char* argv[5];
+    int i = 0;
+    argv[i++] = DEXOPTANALYZER_BIN;
+    argv[i++] = dex_file_arg;
+    argv[i++] = isa_arg;
+    argv[i++] = compiler_filter_arg;
+    argv[i] = NULL;
+
+    execv(DEXOPTANALYZER_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno));
+}
+
+// Prepares the oat dir for the secondary dex files.
+static bool prepare_secondary_dex_oat_dir(const char* dex_path, int uid,
+         const char* instruction_set, std::string* oat_dir_out) {
+    std::string apk_path_str(dex_path);
+    unsigned long dirIndex = apk_path_str.rfind('/');
+    if (dirIndex == std::string::npos) {
+        LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path;
+        return false;
+    }
+    std::string apk_dir = apk_path_str.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(apk_dir, "oat", 02711, uid, cache_gid) != 0) {
+        LOG(ERROR) << "Could not prepare oat dir for secondary dex: " << dex_path;
+        return false;
+    }
+
+    char oat_dir[PKG_PATH_MAX];
+    snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+    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) {
+        LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << dex_path;
+        return false;
+    }
+
+    return true;
+}
+
+static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200;
+
+// Verifies the result of dexoptanalyzer executed for the apk_path.
+// If the result is valid returns true and sets dexopt_needed_out to a valid value.
+// Returns false for errors or unexpected result values.
+static bool process_dexoptanalyzer_result(const char* dex_path, int result,
+            int* dexopt_needed_out) {
+    // The result values are defined in dexoptanalyzer.
+    switch (result) {
+        case 0:  // no_dexopt_needed
+            *dexopt_needed_out = NO_DEXOPT_NEEDED; return true;
+        case 1:  // dex2oat_from_scratch
+            *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true;
+        case 5:  // dex2oat_for_bootimage_odex
+            *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true;
+        case 6:  // dex2oat_for_filter_odex
+            *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true;
+        case 7:  // dex2oat_for_relocation_odex
+            *dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true;
+        case 2:  // dex2oat_for_bootimage_oat
+        case 3:  // dex2oat_for_filter_oat
+        case 4:  // dex2oat_for_relocation_oat
+            LOG(ERROR) << "Dexoptnalyzer return the status of an oat file."
+                    << " Expected odex file status for secondary dex " << dex_path
+                    << " : dexoptanalyzer result=" << result;
+            return false;
+        default:
+            LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path
+                    << " exec_dexoptanalyzer result=" << result;
+            return false;
+    }
+}
+
+// Processes the dex_path as a secondary dex files and return true if the path dex file should
+// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
+// successfully.
+// When returning true, dexopt_needed_out is assigned a valid OatFileAsssitant::DexOptNeeded
+// code and aot_dir_out is assigned the oat dir path where the oat file should be stored.
+static bool process_secondary_dex_dexopt(const char* 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* aot_dir_out) {
+    int storage_flag;
+
+    if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
+        storage_flag = FLAG_STORAGE_CE;
+        if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+            LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
+            return false;
+        }
+    } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+        storage_flag = FLAG_STORAGE_DE;
+    } else {
+        LOG(ERROR) << "Secondary dex storage flag must be set";
+        return false;
+    }
+
+    if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
+        LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+        return false;
+    }
+
+    // Check if the path exist. If not, there's nothing to do.
+    if (access(dex_path, F_OK) != 0) {
+        if (errno == ENOENT) {
+            // Secondary dex files might be deleted any time by the app.
+            // Nothing to do if that's the case
+            ALOGV("Secondary dex does not exist %s", dex_path);
+            return NO_DEXOPT_NEEDED;
+        } else {
+            PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+        }
+    }
+
+    // Prepare the oat directories.
+    if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, aot_dir_out)) {
+        return false;
+    }
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        // child -- drop privileges before continuing.
+        drop_capabilities(uid);
+        // Run dexoptanalyzer to get dexopt_needed code.
+        exec_dexoptanalyzer(dex_path, instruction_set, compiler_filter);
+        exit(DEXOPTANALYZER_BIN_EXEC_ERROR);
+    }
+
+    /* parent */
+
+    int result = wait_child(pid);
+    if (!WIFEXITED(result)) {
+        LOG(ERROR) << "dexoptanalyzer failed for path " << dex_path << ": " << result;
+        return false;
+    }
+    result = WEXITSTATUS(result);
+    bool success = process_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+    // Run dexopt only if needed or forced.
+    // Note that dexoptanalyzer is executed even if force compilation is enabled.
+    // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result)
+    // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not
+    // for oat files from dalvik-cache.
+    if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) {
+        *dexopt_needed_out = DEX2OAT_FROM_SCRATCH;
+    }
+
+    return success;
+}
+
+int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set,
+        int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
+        const char* volume_uuid, const char* shared_libraries) {
     CHECK(pkgname != nullptr);
     CHECK(pkgname[0] != 0);
     if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
@@ -1221,18 +1405,38 @@
     bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
     bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
     bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+    bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0;
+
+    // Check if we're dealing with a secondary dex file and if we need to compile it.
+    std::string oat_dir_str;
+    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)) {
+            oat_dir = oat_dir_str.c_str();
+            if (dexopt_needed == NO_DEXOPT_NEEDED) {
+                return 0;  // Nothing to do, report success.
+            }
+        } else {
+            return -1;  // We had an error, logged in the process method.
+        }
+    } else {
+        // Currently these flags are only use for secondary dex files.
+        // Verify that they are not set for primary apks.
+        CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0);
+        CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0);
+    }
 
     // Open the input file.
-    base::unique_fd input_fd(open(apk_path, O_RDONLY, 0));
+    base::unique_fd input_fd(open(dex_path, O_RDONLY, 0));
     if (input_fd.get() < 0) {
-        ALOGE("installd cannot open '%s' for input during dexopt\n", apk_path);
+        ALOGE("installd cannot open '%s' for input during dexopt\n", dex_path);
         return -1;
     }
 
     // Create the output OAT file.
     char out_oat_path[PKG_PATH_MAX];
-    Dex2oatFileWrapper out_oat_fd = open_oat_out_file(apk_path, oat_dir, is_public, uid,
-            instruction_set, out_oat_path);
+    Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
+            instruction_set, is_secondary_dex, out_oat_path);
     if (out_oat_fd.get() < 0) {
         return -1;
     }
@@ -1240,7 +1444,7 @@
     // Open vdex files.
     Dex2oatFileWrapper in_vdex_fd;
     Dex2oatFileWrapper out_vdex_fd;
-    if (!open_vdex_files(apk_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
+    if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
             &in_vdex_fd, &out_vdex_fd)) {
         return -1;
     }
@@ -1254,9 +1458,9 @@
 
     // Open the reference profile if needed.
     Dex2oatFileWrapper reference_profile_fd =
-            maybe_open_reference_profile(pkgname, profile_guided, is_public, uid);
+        maybe_open_reference_profile(pkgname, profile_guided, is_public, uid, is_secondary_dex);
 
-    ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
+    ALOGV("DexInv: --- BEGIN '%s' ---\n", dex_path);
 
     pid_t pid = fork();
     if (pid == 0) {
@@ -1270,7 +1474,7 @@
         }
 
         // Pass dex2oat the relative path to the input file.
-        const char *input_file_name = get_location_from_path(apk_path);
+        const char *input_file_name = get_location_from_path(dex_path);
         run_dex2oat(input_fd.get(),
                     out_oat_fd.get(),
                     in_vdex_fd.get(),
@@ -1290,14 +1494,14 @@
     } else {
         int res = wait_child(pid);
         if (res == 0) {
-            ALOGV("DexInv: --- END '%s' (success) ---\n", apk_path);
+            ALOGV("DexInv: --- END '%s' (success) ---\n", dex_path);
         } else {
-            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", apk_path, res);
+            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", dex_path, res);
             return -1;
         }
     }
 
-    update_out_oat_access_times(apk_path, out_oat_path);
+    update_out_oat_access_times(dex_path, out_oat_path);
 
     // We've been successful, don't delete output.
     out_oat_fd.SetCleanup(false);
@@ -1308,6 +1512,115 @@
     return 0;
 }
 
+// Try to remove the given directory. Log an error if the directory exists
+// and is empty but could not be removed.
+static bool rmdir_if_empty(const char* dir) {
+    if (rmdir(dir) == 0) {
+        return true;
+    }
+    if (errno == ENOENT || errno == ENOTEMPTY) {
+        return true;
+    }
+    PLOG(ERROR) << "Failed to remove dir: " << dir;
+    return false;
+}
+
+// Try to unlink the given file. Log an error if the file exists and could not
+// be unlinked.
+static bool unlink_if_exists(const std::string& file) {
+    if (unlink(file.c_str()) == 0) {
+        return true;
+    }
+    if (errno == ENOENT) {
+        return true;
+
+    }
+    PLOG(ERROR) << "Could not unlink: " << file;
+    return false;
+}
+
+// Create the oat file structure for the secondary dex 'dex_path' and assign
+// the individual path component to the 'out_' parameters.
+static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
+        /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) {
+    size_t dirIndex = dex_path.rfind('/');
+    if (dirIndex == std::string::npos) {
+        LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
+        return false;
+    }
+    // TODO(calin): we have similar computations in at lest 3 other places
+    // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
+    // use string append.
+    std::string apk_dir = dex_path.substr(0, dirIndex);
+    snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+    snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
+
+    if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
+            /*is_secondary_dex*/ true, out_oat_path)) {
+        LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
+        return false;
+    }
+    return true;
+}
+
+// Reconcile the secondary dex 'dex_path' and its generated oat files.
+// Return true if all the parameters are valid and the secondary dex file was
+//   processed successfully (i.e. the dex_path either exists, or if not, its corresponding
+//   oat/vdex/art files where deleted successfully). In this case, out_secondary_dex_exists
+//   will be true if the secondary dex file still exists. If the secondary dex file does not exist,
+//   the method cleans up any previously generated compiler artifacts (oat, vdex, art).
+// Return false if there were errors during processing. In this case
+//   out_secondary_dex_exists will be set to false.
+bool reconcile_secondary_dex_file(const std::string& dex_path,
+        const std::string& pkgname, int uid, const std::vector<std::string>& isas,
+        const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+        /*out*/bool* out_secondary_dex_exists) {
+    // Set out to false to start with, just in case we have validation errors.
+    *out_secondary_dex_exists = false;
+    if (isas.size() == 0) {
+        LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector";
+        return false;
+    }
+
+    const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+    if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+            uid, storage_flag)) {
+        LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+        return false;
+    }
+
+    if (access(dex_path.c_str(), F_OK) == 0) {
+        // The path exists, nothing to do. The odex files (if any) will be left untouched.
+        *out_secondary_dex_exists = true;
+        return true;
+    } else if (errno != ENOENT) {
+        PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path;
+        return false;
+    }
+
+    // The secondary dex does not exist anymore. Clear any generated files.
+    char oat_path[PKG_PATH_MAX];
+    char oat_dir[PKG_PATH_MAX];
+    char oat_isa_dir[PKG_PATH_MAX];
+    bool result = true;
+    for (size_t i = 0; i < isas.size(); i++) {
+        if (!create_secondary_dex_oat_layout(dex_path, isas[i], oat_dir, oat_isa_dir, oat_path)) {
+            LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
+            result = false;
+            continue;
+        }
+        result = unlink_if_exists(oat_path) && result;
+        result = unlink_if_exists(create_vdex_filename(oat_path)) && result;
+        result = unlink_if_exists(create_image_filename(oat_path)) && result;
+
+        // Try removing the directories as well, they might be empty.
+        result = rmdir_if_empty(oat_isa_dir) && result;
+        result = rmdir_if_empty(oat_dir) && result;
+    }
+
+    return result;
+}
+
 // Helper for move_ab, so that we can have common failure-case cleanup.
 static bool unlink_and_rename(const char* from, const char* to) {
     // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
@@ -1439,7 +1752,8 @@
 bool delete_odex(const char* apk_path, const char* instruction_set, const char* oat_dir) {
     // Delete the oat/odex file.
     char out_path[PKG_PATH_MAX];
-    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
+    if (!create_oat_out_path(apk_path, instruction_set, oat_dir,
+            /*is_secondary_dex*/ false, out_path)) {
         return false;
     }
 
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 1115c78..7bb6eee 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -25,6 +25,7 @@
 namespace installd {
 
 /* dexopt needed flags matching those in dalvik.system.DexFile */
+static constexpr int NO_DEXOPT_NEEDED            = 0;
 static constexpr int DEX2OAT_FROM_SCRATCH        = 1;
 static constexpr int DEX2OAT_FOR_BOOT_IMAGE      = 2;
 static constexpr int DEX2OAT_FOR_FILTER          = 3;
@@ -44,6 +45,11 @@
 
 bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
 
+bool reconcile_secondary_dex_file(const std::string& dex_path,
+        const std::string& pkgname, int uid, const std::vector<std::string>& isas,
+        const std::unique_ptr<std::string>& volumeUuid, int storage_flag,
+        /*out*/bool* out_secondary_dex_exists);
+
 int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
         const char* volume_uuid, const char* shared_libraries);
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/installd_constants.h b/cmds/installd/installd_constants.h
index 401e581..d8a754c 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -42,6 +42,14 @@
 constexpr int DEXOPT_DEBUGGABLE     = 1 << 3;
 constexpr int DEXOPT_BOOTCOMPLETE   = 1 << 4;
 constexpr int DEXOPT_PROFILE_GUIDED = 1 << 5;
+constexpr int DEXOPT_SECONDARY_DEX  = 1 << 6;
+// DEXOPT_FORCE, DEXOPT_STORAGE_CE, DEXOPT_STORAGE_DE are exposed for secondary
+// dex files only. Primary apks are analyzed in PackageManager and installd
+// does not need to know if the compilation is forced or on what kind of storage
+// the dex files are.
+constexpr int DEXOPT_FORCE          = 1 << 7;
+constexpr int DEXOPT_STORAGE_CE     = 1 << 8;
+constexpr int DEXOPT_STORAGE_DE     = 1 << 9;
 
 /* all known values for dexopt flags */
 constexpr int DEXOPT_MASK =
@@ -49,7 +57,15 @@
     | DEXOPT_SAFEMODE
     | DEXOPT_DEBUGGABLE
     | DEXOPT_BOOTCOMPLETE
-    | DEXOPT_PROFILE_GUIDED;
+    | DEXOPT_PROFILE_GUIDED
+    | DEXOPT_SECONDARY_DEX
+    | DEXOPT_FORCE
+    | DEXOPT_STORAGE_CE
+    | DEXOPT_STORAGE_DE;
+
+// NOTE: keep in sync with StorageManager
+constexpr int FLAG_STORAGE_DE = 1 << 0;
+constexpr int FLAG_STORAGE_CE = 1 << 1;
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
diff --git a/cmds/installd/matchgen.py b/cmds/installd/matchgen.py
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/utils.cpp b/cmds/installd/utils.cpp
index 0b1cd7e..37215ea 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -1003,6 +1003,21 @@
     closedir(d);
 }
 
+void add_preloads_file_cache(cache_t* cache, const char* volume_uuid) {
+    char dirname[PATH_MAX];
+    DIR* subdir;
+    auto cache_path = StringPrintf("%s/preloads/file_cache", create_data_path(volume_uuid).c_str());
+    strcpy(dirname, cache_path.c_str());
+    CACHE_NOISY(ALOGI("add_preloads_file_cache: dirname=%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);
+    }
+}
+
 static char *create_dir_path(char path[PATH_MAX], cache_dir_t* dir)
 {
     char *pos = path;
@@ -1171,6 +1186,25 @@
     return -1;
 }
 
+bool validate_secondary_dex_path(const char* pkgname, const char* path,
+        const char* volume_uuid, int uid, int storage_flag) {
+    CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE);
+
+    std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
+        ? create_data_user_ce_package_path(volume_uuid, multiuser_get_user_id(uid), pkgname)
+        : create_data_user_de_package_path(volume_uuid, multiuser_get_user_id(uid), pkgname);
+    dir_rec_t dir;
+    if (get_path_from_string(&dir, app_private_dir.c_str()) != 0) {
+        LOG(WARNING) << "Could not get dir rec for " << app_private_dir;
+        return false;
+    }
+    // Usually secondary dex files have a nested directory structure.
+    // Pick at most 10 subdirectories when validating (arbitrary value).
+    // If the secondary dex file is >10 directory nested then validation will
+    // fail and the file will not be compiled.
+    return validate_path(&dir, path, /*max_subdirs*/ 10) == 0;
+}
+
 /**
  * Get the contents of a environment variable that contains a path. Caller
  * owns the string that is inserted into the directory record. Returns
@@ -1370,5 +1404,73 @@
     }
 }
 
+/**
+ * Prepare an app cache directory, which offers to fix-up the GID and
+ * directory mode flags during a platform upgrade.
+ * The app cache directory path will be 'parent'/'name'.
+ */
+int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+        uid_t uid, gid_t gid) {
+    auto path = StringPrintf("%s/%s", parent.c_str(), name);
+    struct stat st;
+    if (stat(path.c_str(), &st) != 0) {
+        if (errno == ENOENT) {
+            // This is fine, just create it
+            if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
+                PLOG(ERROR) << "Failed to prepare " << path;
+                return -1;
+            } else {
+                return 0;
+            }
+        } else {
+            PLOG(ERROR) << "Failed to stat " << path;
+            return -1;
+        }
+    }
+
+    mode_t actual_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
+    if (st.st_uid != uid) {
+        // Mismatched UID is real trouble; we can't recover
+        LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid
+                << " but expected " << uid;
+        return -1;
+    } else if (st.st_gid == gid && actual_mode == target_mode) {
+        // Everything looks good!
+        return 0;
+    }
+
+    // Directory is owned correctly, but GID or mode mismatch means it's
+    // probably a platform upgrade so we need to fix them
+    FTS *fts;
+    FTSENT *p;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_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) {
+                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) {
+                PLOG(WARNING) << "Failed to chown " << p->fts_path;
+            }
+            break;
+        case FTS_SL:
+        case FTS_SLNONE:
+            if (lchown(p->fts_accpath, -1, gid) != 0) {
+                PLOG(WARNING) << "Failed to chown " << p->fts_path;
+            }
+            break;
+        }
+    }
+    fts_close(fts);
+    return 0;
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 5e396c7..5226d1c 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -143,11 +143,15 @@
 
 void add_cache_files(cache_t* cache, const std::string& data_path);
 
+void add_preloads_file_cache(cache_t* cache, const char* volume_uuid);
+
 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 char* pkgname, const char* path,
+        const char* volume_uuid, int uid, int storage_flag);
 
 int get_path_from_env(dir_rec_t* rec, const char* var);
 
@@ -167,6 +171,9 @@
 
 int wait_child(pid_t pid);
 
+int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+        uid_t uid, gid_t gid);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/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..3ec3178
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -0,0 +1,73 @@
+# 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 \
+
+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 \
+
+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..a49da37
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -0,0 +1,704 @@
+/* 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 <binder/IMemory.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..f757fc3
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -0,0 +1,144 @@
+/*
+ * 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 <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..1657551
--- /dev/null
+++ b/cmds/vr/pose/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+  pose.cpp
+
+staticLibraries := \
+  libdvrcommon \
+  libsensor \
+  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)
+
+ifeq ($(TARGET_BUILD_VARIANT),eng)
+ALL_DEFAULT_INSTALLED_MODULES += pose
+all_modules: pose
+endif
+
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..2fa9155
--- /dev/null
+++ b/cmds/vr/vrscreencap/Android.mk
@@ -0,0 +1,28 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	vrscreencap.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+	libdisplay \
+	libimageio \
+	libpdx_default_transport \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libpng \
+	libsync
+
+LOCAL_MODULE := vrscreencap
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(TARGET_BUILD_VARIANT),eng)
+ALL_DEFAULT_INSTALLED_MODULES += vrscreencap
+all_modules: vrscreencap
+endif
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/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index f9464e8..467e0fd 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -42,6 +42,7 @@
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
     <feature name="android.software.input_methods" />
+    <feature name="android.software.picture_in_picture" />
     <feature name="android.software.print" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 8128165..7f545e6 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -42,6 +42,7 @@
     <feature name="android.software.backup" />
     <feature name="android.software.home_screen" />
     <feature name="android.software.input_methods" />
+    <feature name="android.software.picture_in_picture" />
     <feature name="android.software.print" />
 
     <!-- Feature to specify if the device supports adding device admins. -->
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.h b/include/android/hardware_buffer.h
new file mode 100644
index 0000000..24e217e
--- /dev/null
+++ b/include/android/hardware_buffer.h
@@ -0,0 +1,223 @@
+/*
+ * 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_SFLOAT = 0x16,
+
+    /**
+     * 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 read from and written to by the GPU */
+    AHARDWAREBUFFER_USAGE0_GPU_STORAGE_IMAGE      =
+            AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE |
+            AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT,
+    /* The buffer will be used as a cubemap texture */
+    AHARDWAREBUFFER_USAGE0_GPU_CUBEMAP            = 1ULL << 13,
+    /* 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,
+};
+
+typedef struct AHardwareBuffer_Desc {
+    uint32_t    width;
+    uint32_t    height;
+    uint32_t    layers;
+    uint64_t    usage0;     // Combination of AHARDWAREBUFFER_USAGE0_*
+    uint64_t    usage1;     // Initialize to zero, reserved for future use
+    uint32_t    format;     // One of AHARDWAREBUFFER_FORMAT_*
+} 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);
+
+// ----------------------------------------------------------------------------
+// Everything below here is part of the public NDK API, but is intended only
+// for use by device-specific graphics drivers.
+struct native_handle;
+const struct native_handle* AHardwareBuffer_getNativeHandle(
+        const AHardwareBuffer* buffer);
+
+__END_DECLS
+
+#endif // ANDROID_HARDWARE_BUFFER_H
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/native_window.h b/include/android/native_window.h
index b60b9f1..7d8d657 100644
--- a/include/android/native_window.h
+++ b/include/android/native_window.h
@@ -28,6 +28,7 @@
 
 #include <sys/cdefs.h>
 
+#include <android/hardware_buffer.h>
 #include <android/rect.h>
 
 #ifdef __cplusplus
@@ -35,15 +36,20 @@
 #endif
 
 /**
- * Pixel formats that a window can use.
+ * 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          = 1,
+    WINDOW_FORMAT_RGBA_8888          = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
     /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
-    WINDOW_FORMAT_RGBX_8888          = 2,
+    WINDOW_FORMAT_RGBX_8888          = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
     /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
-    WINDOW_FORMAT_RGB_565            = 4,
+    WINDOW_FORMAT_RGB_565            = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
+    /** Red: 16 bits, Green: 16 bits, Blue: 16 bits, Alpha: 16 bits. **/
+    WINDOW_FORMAT_RGBA_FP16          = 0x16,
 };
 
 struct ANativeWindow;
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..efcac74
--- /dev/null
+++ b/include/audiomanager/IPlayer.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_IPLAYER_H
+#define ANDROID_IPLAYER_H
+
+#include <stdint.h>
+#include <sys/types.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;
+
+};
+
+// ----------------------------------------------------------------------------
+
+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..30d7317 100644
--- a/include/batteryservice/BatteryService.h
+++ b/include/batteryservice/BatteryService.h
@@ -24,25 +24,7 @@
 
 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
-};
-
-// must be kept in sync with definitions in 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
-};
+#include "BatteryServiceConstants.h"
 
 // must be kept in sync with definitions in BatteryProperty.java
 enum {
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/binder/AppOpsManager.h b/include/binder/AppOpsManager.h
index 042927c..4212776 100644
--- a/include/binder/AppOpsManager.h
+++ b/include/binder/AppOpsManager.h
@@ -91,7 +91,8 @@
         OP_USE_SIP = 53,
         OP_PROCESS_OUTGOING_CALLS = 54,
         OP_USE_FINGERPRINT = 55,
-        OP_BODY_SENSORS = 56
+        OP_BODY_SENSORS = 56,
+        OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
     };
 
     AppOpsManager();
diff --git a/include/binder/IActivityManager.h b/include/binder/IActivityManager.h
new file mode 100644
index 0000000..5ad2180
--- /dev/null
+++ b/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/IBinder.h b/include/binder/IBinder.h
index 9097cb3..2e62957 100644
--- a/include/binder/IBinder.h
+++ b/include/binder/IBinder.h
@@ -38,6 +38,7 @@
 class IInterface;
 class Parcel;
 class IResultReceiver;
+class IShellCallback;
 
 /**
  * Base class and low-level protocol for a remotable object.
@@ -82,7 +83,7 @@
     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,
+                                         Vector<String16>& args, const sp<IShellCallback>& callback,
                                          const sp<IResultReceiver>& resultReceiver);
 
     virtual status_t        transact(   uint32_t code,
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
index be72d44..0f1fe5b 100644
--- a/include/binder/IInterface.h
+++ b/include/binder/IInterface.h
@@ -72,24 +72,24 @@
 // ----------------------------------------------------------------------
 
 #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;    \
+    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&                                            \
+    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> I##INTERFACE::asInterface(              \
+            const ::android::sp<::android::IBinder>& obj)               \
     {                                                                   \
-        android::sp<I##INTERFACE> intr;                                 \
+        ::android::sp<I##INTERFACE> intr;                               \
         if (obj != NULL) {                                              \
             intr = static_cast<I##INTERFACE*>(                          \
                 obj->queryLocalInterface(                               \
diff --git a/include/binder/IShellCallback.h b/include/binder/IShellCallback.h
new file mode 100644
index 0000000..fda9ee6
--- /dev/null
+++ b/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/include/binder/IpPrefix.h b/include/binder/IpPrefix.h
new file mode 100644
index 0000000..96ebaac
--- /dev/null
+++ b/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/include/binder/Map.h b/include/binder/Map.h
new file mode 100644
index 0000000..96a4f8a
--- /dev/null
+++ b/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/Parcel.h b/include/binder/Parcel.h
index b0d53ef..cf2fa47 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -31,6 +31,7 @@
 
 #include <binder/IInterface.h>
 #include <binder/Parcelable.h>
+#include <binder/Map.h>
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -43,6 +44,10 @@
 class String8;
 class TextOutput;
 
+namespace binder {
+class Value;
+};
+
 class Parcel {
     friend class IPCThreadState;
 public:
@@ -153,6 +158,8 @@
     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>
@@ -160,6 +167,8 @@
 
     status_t            writeParcelable(const Parcelable& parcelable);
 
+    status_t            writeValue(const binder::Value& value);
+
     template<typename T>
     status_t            write(const Flattenable<T>& val);
 
@@ -171,21 +180,29 @@
     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.
@@ -271,6 +288,8 @@
     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;
 
@@ -314,6 +333,9 @@
     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
@@ -332,6 +354,10 @@
     // 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;
@@ -468,6 +494,8 @@
     #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() { }
@@ -482,6 +510,9 @@
     #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;
@@ -855,7 +886,16 @@
         return this->writeInt32(-1);
     }
 
-    return unsafeWriteTypedVector(*val, &Parcel::writeParcelable);
+    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>);
 }
 
 // ---------------------------------------------------------------------------
diff --git a/include/binder/Value.h b/include/binder/Value.h
new file mode 100644
index 0000000..4dee3d8
--- /dev/null
+++ b/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/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/gfx/FloatRect.h b/include/gfx/FloatRect.h
new file mode 100644
index 0000000..6be5e22
--- /dev/null
+++ b/include/gfx/FloatRect.h
@@ -0,0 +1,42 @@
+/*
+ * 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 {
+namespace gfx {
+
+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 gfx
+}  // namespace android
diff --git a/include/gfx/SafeLog.h b/include/gfx/SafeLog.h
new file mode 100644
index 0000000..e45e541
--- /dev/null
+++ b/include/gfx/SafeLog.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.
+ */
+
+#pragma once
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Weverything"
+#include <fmt/format.h>
+#include <log/log.h>
+#pragma clang diagnostic pop
+
+#include <type_traits>
+
+namespace android {
+namespace gfx {
+
+/* SafeLog is a mix-in that can be used to easily add typesafe logging using fmtlib to any class.
+ * To use it, inherit from it using CRTP and implement the getLogTag method.
+ *
+ * For example:
+ *
+ * class Frobnicator : private SafeLog<Frobnicator> {
+ *     friend class SafeLog<Frobnicator>;  // Allows getLogTag to be private
+ *
+ *   public:
+ *     void frobnicate(int32_t i);
+ *
+ *   private:
+ *     // SafeLog does member access on the object calling alog*, so this tag can theoretically vary
+ *     // by instance unless getLogTag is made static
+ *     const char* getLogTag() { return "Frobnicator"; }
+ * };
+ *
+ * void Frobnicator::frobnicate(int32_t i) {
+ *     // Logs something like "04-16 21:35:46.811  3513  3513 I Frobnicator: frobnicating 42"
+ *     alogi("frobnicating {}", i);
+ * }
+ *
+ * See http://fmtlib.net for more information on the formatting API.
+ */
+
+template <typename T>
+class SafeLog {
+  protected:
+    template <typename... Args>
+#if LOG_NDEBUG
+    void alogv(Args&&... /*unused*/) const {
+    }
+#else
+    void alogv(Args&&... args) const {
+        alog<ANDROID_LOG_VERBOSE>(std::forward<Args>(args)...);
+    }
+#endif
+
+    template <typename... Args>
+    void alogd(Args&&... args) const {
+        alog<ANDROID_LOG_DEBUG>(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    void alogi(Args&&... args) const {
+        alog<ANDROID_LOG_INFO>(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    void alogw(Args&&... args) const {
+        alog<ANDROID_LOG_WARN>(std::forward<Args>(args)...);
+    }
+
+    template <typename... Args>
+    void aloge(Args&&... args) const {
+        alog<ANDROID_LOG_ERROR>(std::forward<Args>(args)...);
+    }
+
+  private:
+    // Suppresses clang-tidy check cppcoreguidelines-pro-bounds-array-to-pointer-decay
+    template <size_t strlen, typename... Args>
+    void write(fmt::MemoryWriter* writer, const char (&str)[strlen], Args&&... args) const {
+        writer->write(static_cast<const char*>(str), std::forward<Args>(args)...);
+    }
+
+    template <int priority, typename... Args>
+    void alog(Args&&... args) const {
+        static_assert(std::is_base_of<SafeLog<T>, T>::value, "Can't convert to SafeLog pointer");
+        fmt::MemoryWriter writer;
+        write(&writer, std::forward<Args>(args)...);
+        auto derivedThis = static_cast<const T*>(this);
+        android_writeLog(priority, derivedThis->getLogTag(), writer.c_str());
+    }
+};
+
+}  // namespace gfx
+}  // namespace android
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index 5232d0f..ab676cc 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -20,6 +20,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
+#include <ui/FenceTime.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -59,6 +60,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/BufferQueue.h b/include/gui/BufferQueue.h
index 266f0aa..a523cd8 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -66,8 +66,9 @@
         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;
+        virtual 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,7 +80,8 @@
     // needed gralloc buffers.
     static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
             sp<IGraphicBufferConsumer>* outConsumer,
-            const sp<IGraphicBufferAlloc>& allocator = NULL);
+            const sp<IGraphicBufferAlloc>& allocator = NULL,
+            bool consumerIsSurfaceFlinger = false);
 
 private:
     BufferQueue(); // Create through createBufferQueue
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index e2bafec..da574ec 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 {
 
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..ce85fc3 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -180,7 +180,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);
@@ -244,6 +244,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/FrameTimestamps.h b/include/gui/FrameTimestamps.h
index 4dc7467..46ca2c2 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -17,29 +17,300 @@
 #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,
+    GL_COMPOSITION_DONE,
+    DISPLAY_PRESENT,
+    DISPLAY_RETIRE,
+    DEQUEUE_READY,
+    RELEASE,
+    EVENT_COUNT, // Not an actual event.
 };
 
+
+// A collection of timestamps corresponding to a single frame.
+struct FrameEvents {
+    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 hasDisplayRetireInfo() const;
+    bool hasReleaseInfo() const;
+    bool hasDequeueReadyInfo() const;
+
+    void checkFencesForCompletion();
+    void dump(String8& outString) const;
+
+    static constexpr size_t EVENT_COUNT =
+            static_cast<size_t>(FrameEvent::EVENT_COUNT);
+    static_assert(EVENT_COUNT <= 32, "Event count sanity check failed.");
+
+    bool valid{false};
+    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 addRetireCalled{false};
+    bool addReleaseCalled{false};
+
+    nsecs_t postedTime{-1};
+    nsecs_t requestedPresentTime{-1};
+    nsecs_t latchTime{-1};
+    nsecs_t firstRefreshStartTime{-1};
+    nsecs_t lastRefreshStartTime{-1};
+    nsecs_t dequeueReadyTime{-1};
+
+    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> displayRetireFence{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE};
+};
+
+
+// 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;
+};
+
+
+// The producer's interface to FrameEventHistory
+class ProducerFrameEventHistory : public FrameEventHistory {
+public:
+    ~ProducerFrameEventHistory() override;
+
+    // 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 mRetireTimeline;
+    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 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);
+    void addRetire(uint64_t frameNumber,
+            const std::shared_ptr<FenceTime>& displayRetire);
+    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 mRetireOffset{0};
+    size_t mReleaseOffset{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 size_t minFlattenedSize();
+
+    size_t mIndex{0};
+    uint64_t mFrameNumber{0};
+
+    bool mAddPostCompositeCalled{0};
+    bool mAddRetireCalled{0};
+    bool mAddReleaseCalled{0};
+
+    nsecs_t mPostedTime{0};
+    nsecs_t mRequestedPresentTime{0};
+    nsecs_t mLatchTime{0};
+    nsecs_t mFirstRefreshStartTime{0};
+    nsecs_t mLastRefreshStartTime{0};
+    nsecs_t mDequeueReadyTime{0};
+
+    FenceTime::Snapshot mGpuCompositionDoneFence;
+    FenceTime::Snapshot mDisplayPresentFence;
+    FenceTime::Snapshot mDisplayRetireFence;
+    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), 4> {
+        return {{
+            &fed->mGpuCompositionDoneFence, &fed->mDisplayPresentFence,
+            &fed->mDisplayRetireFence, &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 size_t minFlattenedSize();
+
+    std::vector<FrameEventsDelta> mDeltas;
+};
+
+
 } // namespace android
 #endif
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 6267625..f8ded74 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.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.
     //
@@ -187,6 +191,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 +258,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 +406,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 +418,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;
diff --git a/include/gui/GraphicBufferAlloc.h b/include/gui/GraphicBufferAlloc.h
index 62e3877..b19a1ac 100644
--- a/include/gui/GraphicBufferAlloc.h
+++ b/include/gui/GraphicBufferAlloc.h
@@ -34,8 +34,9 @@
     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;
+            uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint32_t usage, std::string requestorName,
+            status_t* error) override;
 };
 
 
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index 460a03d..93dd4ac 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -49,7 +49,8 @@
     // 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.
+    // 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.
@@ -81,10 +82,11 @@
     // 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.
+    virtual void addAndGetFrameTimestamps(
+            const NewFrameEventsEntry* /*newTimestamps*/,
+            FrameEventHistoryDelta* /*outDelta*/) {}
 };
 
 
diff --git a/include/gui/IGraphicBufferAlloc.h b/include/gui/IGraphicBufferAlloc.h
index acc2f30..2a7690a 100644
--- a/include/gui/IGraphicBufferAlloc.h
+++ b/include/gui/IGraphicBufferAlloc.h
@@ -38,12 +38,14 @@
     /* 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;
+            PixelFormat format, uint32_t layerCount, 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);
+            PixelFormat format, uint32_t layerCount, uint32_t usage,
+            status_t* error) {
+        return createGraphicBuffer(w, h, format, layerCount, usage, "<Unknown>",
+                error);
     }
 };
 
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index c2dba50..258cd2f 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -190,7 +190,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 +296,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 +307,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 +334,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 +350,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,
@@ -581,13 +582,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
index 857444b..2ccd832 100644
--- a/include/gui/ISensorEventConnection.h
+++ b/include/gui/ISensorEventConnection.h
@@ -40,6 +40,7 @@
                                    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;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h
index 737c430..0c36c99 100644
--- a/include/gui/ISensorServer.h
+++ b/include/gui/ISensorServer.h
@@ -25,6 +25,8 @@
 
 #include <binder/IInterface.h>
 
+struct native_handle;
+typedef struct native_handle native_handle_t;
 namespace android {
 // ----------------------------------------------------------------------------
 
@@ -43,6 +45,9 @@
     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;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 555a0cc..8af4d46 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -32,6 +32,8 @@
 #include <gui/IGraphicBufferAlloc.h>
 #include <gui/ISurfaceComposerClient.h>
 
+#include <vector>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
@@ -43,6 +45,7 @@
 class IDisplayEventConnection;
 class IMemoryHeap;
 class Rect;
+enum class FrameEvent;
 
 /*
  * This class defines the Binder IPC interface for accessing various
@@ -75,6 +78,17 @@
      */
     virtual sp<ISurfaceComposerClient> createConnection() = 0;
 
+    /** 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<ISurfaceComposerClient> createScopedConnection(
+            const sp<IGraphicBufferProducer>& parent) = 0;
+
     /* create a graphic buffer allocator
      */
     virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() = 0;
@@ -112,6 +126,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 +168,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 +190,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;
 };
 
 // ----------------------------------------------------------------------------
@@ -189,6 +212,7 @@
         GET_BUILT_IN_DISPLAY,
         SET_TRANSACTION_STATE,
         AUTHENTICATE_SURFACE,
+        GET_SUPPORTED_FRAME_TIMESTAMPS,
         GET_DISPLAY_CONFIGS,
         GET_ACTIVE_CONFIG,
         SET_ACTIVE_CONFIG,
@@ -202,6 +226,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..1f4387d 100644
--- a/include/gui/ISurfaceComposerClient.h
+++ b/include/gui/ISurfaceComposerClient.h
@@ -60,6 +60,7 @@
     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;
 
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
index 7506835..d886b2b 100644
--- a/include/gui/Sensor.h
+++ b/include/gui/Sensor.h
@@ -91,6 +91,8 @@
     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
diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h
index 6c6230f..5b34ff4 100644
--- a/include/gui/SensorManager.h
+++ b/include/gui/SensorManager.h
@@ -34,10 +34,15 @@
 
 #include <gui/SensorEventQueue.h>
 
+#include <unordered_map>
+
 // ----------------------------------------------------------------------------
 // Concrete types for the NDK
 struct ASensorManager { };
 
+struct native_handle;
+typedef struct native_handle native_handle_t;
+
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
@@ -59,6 +64,9 @@
     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);
 
 private:
     // DeathRecipient interface
@@ -77,6 +85,8 @@
     Vector<Sensor> mSensors;
     sp<IBinder::DeathRecipient> mDeathObserver;
     const String16 mOpPackageName;
+    std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
+    int32_t mDirectConnectionHandle;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 489d5ea..750e653 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -33,6 +33,8 @@
 
 namespace android {
 
+class ISurfaceComposer;
+
 /*
  * An implementation of ANativeWindow that feeds graphics buffers into a
  * BufferQueue.
@@ -66,7 +68,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 +137,29 @@
     status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]);
 
+    /* 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);
+
     // See IGraphicBufferProducer::getFrameTimestamps
-    bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
-            nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
-            nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
-            nsecs_t* outReleaseTime);
+    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* outDisplayRetireTime,
+            nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime);
+    status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration);
 
     status_t getUniqueId(uint64_t* outId) const;
 
 protected:
     virtual ~Surface();
 
+    // Virtual for testing.
+    virtual sp<ISurfaceComposer> composerService() const;
+
 private:
     // can't be copied
     Surface& operator = (const Surface& rhs);
@@ -191,7 +206,9 @@
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSharedBufferMode(va_list args);
     int dispatchSetAutoRefresh(va_list args);
+    int dispatchEnableFrameTimestamps(va_list args);
     int dispatchGetFrameTimestamps(va_list args);
+    int dispatchGetDisplayRefreshCycleDuration(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -204,7 +221,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,6 +240,7 @@
     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;
@@ -237,7 +254,8 @@
     enum { NUM_BUFFER_SLOTS = BufferQueue::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,7 +397,17 @@
 
     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;
+    mutable bool mFrameTimestampsSupportsRetire;
+
+    // A cached copy of the FrameEventHistory maintained by the consumer.
+    bool mEnableFrameTimestamps = false;
+    std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory;
 };
 
 namespace view {
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index f2932f2..8302160 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -52,6 +52,7 @@
     friend class Composer;
 public:
                 SurfaceComposerClient();
+                SurfaceComposerClient(const sp<IGraphicBufferProducer>& parent);
     virtual     ~SurfaceComposerClient();
 
     // Always make sure we could initialize
@@ -105,7 +106,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 +135,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,7 +146,7 @@
     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    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    setPosition(const sp<IBinder>& id, float x, float y);
@@ -148,6 +156,8 @@
     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    reparentChildren(const sp<IBinder>& id,
+            const sp<IBinder>& newParentHandle);
     status_t    setOverrideScalingMode(const sp<IBinder>& id,
             int32_t overrideScalingMode);
     status_t    setGeometryAppliesWithResize(const sp<IBinder>& id);
@@ -195,6 +205,7 @@
                 status_t                    mStatus;
                 sp<ISurfaceComposerClient>  mClient;
                 Composer&                   mComposer;
+                wp<IGraphicBufferProducer>  mParent;
 };
 
 // ---------------------------------------------------------------------------
@@ -208,9 +219,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 +248,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..54c8fa9 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -61,7 +61,7 @@
     void        disconnect();
 
     status_t    setLayerStack(uint32_t layerStack);
-    status_t    setLayer(uint32_t layer);
+    status_t    setLayer(int32_t layer);
     status_t    setPosition(float x, float y);
     status_t    setSize(uint32_t w, uint32_t h);
     status_t    hide();
@@ -81,7 +81,9 @@
 
     // 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);
+    status_t deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
+    // Reparents all children of this layer to the new parent handle.
+    status_t reparentChildren(const sp<IBinder>& newParentHandle);
 
     // Set an override scaling mode as documented in <system/window.h>
     // the override scaling mode will take precedence over any client
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index 7ae07ad..4556c8f 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";
@@ -533,6 +534,8 @@
         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,6 +548,10 @@
         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";
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..d0ae867 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -62,6 +62,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 +81,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 +95,7 @@
     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_IndexExtOtherEndUnused,
 
     /* Time configurations */
     OMX_IndexExtTimeStartUnused = OMX_IndexKhronosExtensions + 0x00900000,
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/ParcelValTypes.h b/include/private/binder/ParcelValTypes.h
new file mode 100644
index 0000000..666d22a
--- /dev/null
+++ b/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/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 4b3fcc6..2a1801b 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -56,6 +56,7 @@
         eFinalCropChanged           = 0x00000400,
         eOverrideScalingModeChanged = 0x00000800,
         eGeometryAppliesWithResize  = 0x00001000,
+        eReparentChildren           = 0x00002000,
     };
 
     layer_state_t()
@@ -74,16 +75,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   dsdy{0};
+                float   dtdy{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;
@@ -95,6 +96,7 @@
             Rect            crop;
             Rect            finalCrop;
             sp<IBinder>     handle;
+            sp<IBinder>     reparentHandle;
             uint64_t        frameNumber;
             int32_t         overrideScalingMode;
             // non POD must be last. see write/read
diff --git a/include/ui/ColorSpace.h b/include/ui/ColorSpace.h
new file mode 100644
index 0000000..b1863df
--- /dev/null
+++ b/include/ui/ColorSpace.h
@@ -0,0 +1,230 @@
+/*
+ * 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 <ui/mat3.h>
+#include <ui/scalar.h>
+#include <ui/vec2.h>
+#include <ui/vec3.h>
+
+namespace android {
+
+class ColorSpace {
+public:
+    typedef std::function<float(float)> transfer_function;
+    typedef std::function<float(float)> clamping_function;
+
+    /**
+     * 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 = linearReponse,
+            transfer_function EOTF = linearReponse,
+            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 = linearReponse,
+            transfer_function EOTF = linearReponse,
+            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;
+    }
+
+    /**
+     * 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();
+
+    class Connector {
+    public:
+        Connector(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:
+        const ColorSpace& mSource;
+        const ColorSpace& mDestination;
+        mat3 mTransform;
+    };
+
+    static const Connector connect(const ColorSpace& src, const ColorSpace& dst) {
+        return Connector(src, dst);
+    }
+
+    // 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 linearReponse(float v) {
+        return v;
+    }
+
+    const std::string mName;
+
+    const mat3 mRGBtoXYZ;
+    const mat3 mXYZtoRGB;
+
+    const transfer_function mOETF;
+    const transfer_function mEOTF;
+    const clamping_function mClamper;
+
+    std::array<float2, 3> mPrimaries;
+    float2 mWhitePoint;
+};
+
+}; // namespace android
+
+#endif // ANDROID_UI_COLOR_SPACE
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
index 799944f..842806e 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -26,16 +26,16 @@
 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..58df24c 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -18,21 +18,17 @@
 #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 +38,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 +58,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,8 +101,8 @@
 
     // 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
@@ -130,11 +137,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/Gralloc1.h b/include/ui/Gralloc1.h
index cf8c173..64dacd7 100644
--- a/include/ui/Gralloc1.h
+++ b/include/ui/Gralloc1.h
@@ -49,6 +49,7 @@
         mWidth(0),
         mHeight(0),
         mFormat(static_cast<android_pixel_format_t>(0)),
+        mLayerCount(0),
         mProducerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
         mConsumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
 
@@ -58,6 +59,7 @@
 
     gralloc1_error_t setDimensions(uint32_t width, uint32_t height);
     gralloc1_error_t setFormat(android_pixel_format_t format);
+    gralloc1_error_t setLayerCount(uint32_t layerCount);
     gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage);
     gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage);
 
@@ -68,6 +70,7 @@
     uint32_t mWidth;
     uint32_t mHeight;
     android_pixel_format_t mFormat;
+    uint32_t mLayerCount;
     gralloc1_producer_usage_t mProducerUsage;
     gralloc1_consumer_usage_t mConsumerUsage;
 
@@ -178,6 +181,8 @@
                 GRALLOC1_FUNCTION_SET_DIMENSIONS> setDimensions;
         FunctionLoader<GRALLOC1_PFN_SET_FORMAT,
                 GRALLOC1_FUNCTION_SET_FORMAT> setFormat;
+        FunctionLoader<GRALLOC1_PFN_SET_LAYER_COUNT,
+                GRALLOC1_FUNCTION_SET_LAYER_COUNT> setLayerCount;
         FunctionLoader<GRALLOC1_PFN_SET_PRODUCER_USAGE,
                 GRALLOC1_FUNCTION_SET_PRODUCER_USAGE> setProducerUsage;
         FunctionLoader<GRALLOC1_PFN_GET_BACKING_STORE,
@@ -188,6 +193,8 @@
                 GRALLOC1_FUNCTION_GET_DIMENSIONS> getDimensions;
         FunctionLoader<GRALLOC1_PFN_GET_FORMAT,
                 GRALLOC1_FUNCTION_GET_FORMAT> getFormat;
+        FunctionLoader<GRALLOC1_PFN_GET_LAYER_COUNT,
+                GRALLOC1_FUNCTION_GET_LAYER_COUNT> getLayerCount;
         FunctionLoader<GRALLOC1_PFN_GET_PRODUCER_USAGE,
                 GRALLOC1_FUNCTION_GET_PRODUCER_USAGE> getProducerUsage;
         FunctionLoader<GRALLOC1_PFN_GET_STRIDE,
diff --git a/include/ui/Gralloc1On0Adapter.h b/include/ui/Gralloc1On0Adapter.h
index d523c4f..2508ce9 100644
--- a/include/ui/Gralloc1On0Adapter.h
+++ b/include/ui/Gralloc1On0Adapter.h
@@ -131,6 +131,7 @@
             width(0),
             height(0),
             format(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
+            layerCount(1),
             producerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
             consumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
 
@@ -145,6 +146,11 @@
             return GRALLOC1_ERROR_NONE;
         }
 
+        gralloc1_error_t setLayerCount(uint32_t lc) {
+            layerCount = lc;
+            return GRALLOC1_ERROR_NONE;
+        }
+
         gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage) {
             producerUsage = usage;
             return GRALLOC1_ERROR_NONE;
@@ -161,6 +167,7 @@
         uint32_t width;
         uint32_t height;
         int32_t format;
+        uint32_t layerCount;
         gralloc1_producer_usage_t producerUsage;
         gralloc1_consumer_usage_t consumerUsage;
     };
@@ -197,6 +204,12 @@
                 &Descriptor::setFormat, format);
     }
 
+    static int32_t setLayerCountHook(gralloc1_device_t* device,
+            gralloc1_buffer_descriptor_t descriptorId, uint32_t layerCount) {
+        return callDescriptorFunction(device, descriptorId,
+                &Descriptor::setLayerCount, layerCount);
+    }
+
     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);
@@ -246,6 +259,11 @@
             return GRALLOC1_ERROR_NONE;
         }
 
+        gralloc1_error_t getLayerCount(uint32_t* outLayerCount) const {
+            *outLayerCount = mDescriptor.layerCount;
+            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
diff --git a/include/ui/GrallocAllocator.h b/include/ui/GrallocAllocator.h
new file mode 100644
index 0000000..dd0f9e0
--- /dev/null
+++ b/include/ui/GrallocAllocator.h
@@ -0,0 +1,68 @@
+/*
+ * 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_ALLOCATOR_H
+#define ANDROID_UI_GRALLOC_ALLOCATOR_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+using hardware::graphics::allocator::V2_0::Error;
+using hardware::graphics::allocator::V2_0::ProducerUsage;
+using hardware::graphics::allocator::V2_0::ConsumerUsage;
+using hardware::graphics::allocator::V2_0::BufferDescriptor;
+using hardware::graphics::allocator::V2_0::Buffer;
+using hardware::graphics::allocator::V2_0::IAllocator;
+using hardware::graphics::allocator::V2_0::IAllocatorClient;
+using hardware::graphics::common::V1_0::PixelFormat;
+
+// Allocator is a wrapper to IAllocator, a proxy to server-side allocator.
+class Allocator {
+public:
+    Allocator();
+
+    // this will be removed and Allocator will be always valid
+    bool valid() const { return (mAllocator != nullptr); }
+
+    std::string dumpDebugInfo() const;
+
+    Error createBufferDescriptor(
+            const IAllocatorClient::BufferDescriptorInfo& descriptorInfo,
+            BufferDescriptor* outDescriptor) const;
+    void destroyBufferDescriptor(BufferDescriptor descriptor) const;
+
+    Error allocate(BufferDescriptor descriptor, Buffer* outBuffer) const;
+    void free(Buffer buffer) const;
+
+    Error exportHandle(BufferDescriptor descriptor, Buffer buffer,
+            native_handle_t** outBufferHandle) const;
+
+private:
+    sp<IAllocator> mAllocator;
+    sp<IAllocatorClient> mClient;
+};
+
+} // namespace Gralloc2
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_ALLOCATOR_H
diff --git a/include/ui/GrallocMapper.h b/include/ui/GrallocMapper.h
new file mode 100644
index 0000000..f533dfb
--- /dev/null
+++ b/include/ui/GrallocMapper.h
@@ -0,0 +1,70 @@
+/*
+ * 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_MAPPER_H
+#define ANDROID_UI_GRALLOC_MAPPER_H
+
+#include <memory>
+
+#include <android/hardware/graphics/mapper/2.0/IMapper.h>
+#include <system/window.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+using hardware::graphics::allocator::V2_0::Error;
+using hardware::graphics::allocator::V2_0::ProducerUsage;
+using hardware::graphics::allocator::V2_0::ConsumerUsage;
+using hardware::graphics::common::V1_0::PixelFormat;
+using hardware::graphics::mapper::V2_0::FlexLayout;
+using hardware::graphics::mapper::V2_0::BackingStore;
+using hardware::graphics::mapper::V2_0::IMapper;
+
+// Mapper is a wrapper to IMapper, a client-side graphics buffer mapper.
+class Mapper {
+public:
+    Mapper();
+
+    // this will be removed and Mapper will be always valid
+    bool valid() const { return (mMapper != nullptr); }
+
+    Error retain(buffer_handle_t handle) const;
+    void release(buffer_handle_t handle) const;
+
+    Error getStride(buffer_handle_t handle, uint32_t* outStride) const;
+
+    Error lock(buffer_handle_t handle,
+            uint64_t producerUsageMask,
+            uint64_t consumerUsageMask,
+            const IMapper::Rect& accessRegion,
+            int acquireFence, void** outData) const;
+    Error lock(buffer_handle_t handle,
+            uint64_t producerUsageMask,
+            uint64_t consumerUsageMask,
+            const IMapper::Rect& accessRegion,
+            int acquireFence, FlexLayout* outLayout) const;
+    int unlock(buffer_handle_t handle) const;
+
+private:
+    sp<IMapper> mMapper;
+};
+
+} // namespace Gralloc2
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_MAPPER_H
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 3e127a1..1bbcee2 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -76,10 +76,15 @@
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
             uint32_t inUsage, std::string requestorName = "<Unknown>");
 
+    // creates w * h buffer with a layer count
+    GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+            uint32_t inLayerCount, 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);
+            uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride,
+            native_handle_t* inHandle, bool keepOwnership);
 
     // create a buffer from an existing ANativeWindowBuffer
     GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership);
@@ -92,6 +97,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; }
 
@@ -101,10 +107,10 @@
     }
 
     status_t reallocate(uint32_t inWidth, uint32_t inHeight,
-            PixelFormat inFormat, uint32_t inUsage);
+            PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage);
 
     bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
-            PixelFormat inFormat, uint32_t inUsage);
+            PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage);
 
     status_t lock(uint32_t inUsage, void** vaddr);
     status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
@@ -160,7 +166,7 @@
     const GraphicBuffer& operator = (const GraphicBuffer& rhs) const;
 
     status_t initSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
-            uint32_t inUsage, std::string requestorName);
+            uint32_t inLayerCount, uint32_t inUsage, std::string requestorName);
 
     void free_handle();
 
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 28d0238..16967d4 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -32,7 +32,12 @@
 
 namespace android {
 
+namespace Gralloc2 {
+class Allocator;
+}
+
 class Gralloc1Loader;
+class GraphicBufferMapper;
 class String8;
 
 class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
@@ -60,8 +65,9 @@
     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, uint32_t usage, buffer_handle_t* handle,
+            uint32_t* stride, uint64_t graphicBufferId,
+            std::string requestorName);
 
     status_t free(buffer_handle_t handle);
 
@@ -74,6 +80,7 @@
         uint32_t height;
         uint32_t stride;
         PixelFormat format;
+        uint32_t layerCount;
         uint32_t usage;
         size_t size;
         std::string requestorName;
@@ -86,6 +93,9 @@
     GraphicBufferAllocator();
     ~GraphicBufferAllocator();
 
+    const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
+    GraphicBufferMapper& mMapper;
+
     std::unique_ptr<Gralloc1::Loader> mLoader;
     std::unique_ptr<Gralloc1::Device> mDevice;
 };
diff --git a/include/ui/GraphicBufferMapper.h b/include/ui/GraphicBufferMapper.h
index a25809c..b6de1b2 100644
--- a/include/ui/GraphicBufferMapper.h
+++ b/include/ui/GraphicBufferMapper.h
@@ -28,6 +28,10 @@
 
 // ---------------------------------------------------------------------------
 
+namespace Gralloc2 {
+class Mapper;
+}
+
 class Rect;
 
 class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -57,11 +61,18 @@
 
     status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
 
+    const Gralloc2::Mapper& getGrallocMapper() const
+    {
+        return *mMapper;
+    }
+
 private:
     friend class Singleton<GraphicBufferMapper>;
 
     GraphicBufferMapper();
 
+    const std::unique_ptr<const Gralloc2::Mapper> mMapper;
+
     std::unique_ptr<Gralloc1::Loader> mLoader;
     std::unique_ptr<Gralloc1::Device> mDevice;
 };
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index f26fecb..ab7a9a3 100644
--- a/include/ui/PixelFormat.h
+++ b/include/ui/PixelFormat.h
@@ -60,6 +60,7 @@
     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
 };
 
 typedef int32_t PixelFormat;
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index e9859fe..ce33d4e 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_UI_RECT
 #define ANDROID_UI_RECT
 
+#include <gfx/FloatRect.h>
 #include <utils/Flattenable.h>
 #include <utils/Log.h>
 #include <utils/TypeHelpers.h>
@@ -179,11 +180,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); }
+
+    gfx::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
index a6aadca..8edf5f8 100644
--- a/include/ui/TMatHelpers.h
+++ b/include/ui/TMatHelpers.h
@@ -14,25 +14,41 @@
  * 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_TMATHELPERS_H_
+#define UI_TMATHELPERS_H_
 
-
-#ifndef UI_TMAT_HELPERS_H
-#define UI_TMAT_HELPERS_H
-
+#include <math.h>
 #include <stdint.h>
 #include <sys/types.h>
-#include <math.h>
-#include <utils/Debug.h>
-#include <utils/String8.h>
+
+#include <cmath>
+#include <exception>
+#include <iomanip>
+#include <stdexcept>
+
+#include <ui/quat.h>
+#include <ui/TVecHelpers.h>
+
+#include  <utils/String8.h>
+
+#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
 
 #define PURE __attribute__((pure))
 
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
 namespace android {
+namespace details {
 // -------------------------------------------------------------------------------------
 
 /*
@@ -48,121 +64,285 @@
 
 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 constexpr int     transpose(int v)    { return v; }
+inline constexpr float   transpose(float v)  { return v; }
+inline constexpr double  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; }
+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 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 PURE gaussJordanInverse(const MATRIX& src) {
+    typedef typename MATRIX::value_type T;
+    static constexpr unsigned int N = MATRIX::NUM_ROWS;
     MATRIX tmp(src);
-    MATRIX inverse(1);
+    MATRIX inverted(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])) {
+    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 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;
-            }
+            // swap columns.
+            std::swap(tmp[i], tmp[swap]);
+            std::swap(inverted[i], inverted[swap]);
         }
 
-        t = 1 / tmp[i][i];
-        for (size_t k=0 ; k<N ; k++) {
-            tmp[i][k] *= t;
-            inverse[i][k] *= t;
+        const T denom(tmp[i][i]);
+        for (size_t k = 0; k < N; ++k) {
+            tmp[i][k] /= denom;
+            inverted[i][k] /= denom;
         }
-        for (size_t j=0 ; j<N ; j++) {
+
+        // Factor out the lower triangle
+        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;
+                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 inverse;
+
+    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>
-MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
+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
 
-    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 );
+    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 r=0 ; r<MATRIX_R::row_size() ; r++) {
-        res[r] = lhs * rhs[r];
+    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>
-MATRIX PURE transpose(const MATRIX& m) {
+CONSTEXPR 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 );
+    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices");
     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]);
+    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>
-typename MATRIX::value_type PURE trace(const MATRIX& m) {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
+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 r=0 ; r<MATRIX::row_size() ; r++)
-        result += trace(m[r][r]);
+    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+        result += trace(m[col][col]);
+    }
     return result;
 }
 
-// trace. this handles matrices of matrices
+// diag. 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 );
+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 r=0 ; r<MATRIX::row_size() ; r++)
-        result[r] = m[r][r];
+    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++) {
+    for (size_t c = 0; c < MATRIX::col_size(); c++) {
         s.append("|  ");
-        for (size_t r=0 ; r<MATRIX::row_size() ; r++) {
+        for (size_t r = 0; r < MATRIX::row_size(); r++) {
             s.appendFormat("%7.2f  ", m[r][c]);
         }
         s.append("|\n");
@@ -170,7 +350,7 @@
     return s;
 }
 
-}; // namespace matrix
+}  // namespace matrix
 
 // -------------------------------------------------------------------------------------
 
@@ -189,29 +369,36 @@
     // 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;
+        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 r=0 ; r<lhs.row_size() ; r++) {
-            lhs[r] /= v;
+        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 BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
+    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>.
  *
@@ -229,6 +416,7 @@
 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
@@ -236,22 +424,217 @@
      * 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); }
+    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) );
+        return matrix::asString(static_cast<const BASE<T>&>(*this));
     }
 };
 
 // -------------------------------------------------------------------------------------
-}; // namespace android
+}  // namespace details
+}  // namespace android
 
+#undef LIKELY
+#undef UNLIKELY
 #undef PURE
+#undef CONSTEXPR
 
-#endif /* UI_TMAT_HELPERS_H */
+#endif  // UI_TMATHELPERS_H_
diff --git a/include/ui/TQuatHelpers.h b/include/ui/TQuatHelpers.h
new file mode 100644
index 0000000..2f0f70f
--- /dev/null
+++ b/include/ui/TQuatHelpers.h
@@ -0,0 +1,304 @@
+/*
+ * 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_TQUATHELPERS_H_
+#define UI_TQUATHELPERS_H_
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <iostream>
+
+#include <ui/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
+
+
+#endif  // UI_TQUATHELPERS_H_
diff --git a/include/ui/TVecHelpers.h b/include/ui/TVecHelpers.h
index bb7dbfc..1884608 100644
--- a/include/ui/TVecHelpers.h
+++ b/include/ui/TVecHelpers.h
@@ -14,22 +14,28 @@
  * 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_TVECHELPERS_H_
+#define UI_TVECHELPERS_H_
 
-#ifndef UI_TVEC_HELPERS_H
-#define UI_TVEC_HELPERS_H
-
+#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 {
 // -------------------------------------------------------------------------------------
 
 /*
@@ -39,24 +45,6 @@
  */
 
 /*
- * 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>.
  *
@@ -65,27 +53,27 @@
  * get all the functionality here.
  */
 
-template <template<typename T> class BASE, typename T>
+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>
-    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];
+    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 rhs;
+        return lhs;
     }
-    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];
+    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 rhs;
+        return lhs;
     }
 
     /* compound assignment from a another vector of the same type.
@@ -93,19 +81,19 @@
      * 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];
+    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 rhs;
+        return lhs;
     }
-    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];
+    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 rhs;
+        return lhs;
     }
 
     /*
@@ -116,57 +104,57 @@
      * (the first one, BASE<T> being known).
      */
 
-    /* The operators below handle operation between vectors of the same side
+    /* The operators below handle operation between vectors of the same size
      * 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;
+    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
-    BASE<T> PURE operator -(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) -= rv;
+    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
+     * 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 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
-    BASE<T> PURE operator -(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(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 BASE, typename T>
+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>
-    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];
+    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 rhs;
+        return lhs;
     }
-    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];
+    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 rhs;
+        return lhs;
     }
 
     /* compound assignment from a another vector of the same type.
@@ -174,19 +162,19 @@
      * 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];
+    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 rhs;
+        return lhs;
     }
-    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];
+    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 rhs;
+        return lhs;
     }
 
     /*
@@ -197,18 +185,18 @@
      * (the first one, BASE<T> being known).
      */
 
-    /* The operators below handle operation between vectors of the same side
+    /* The operators below handle operation between vectors of the same size
      * 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;
+    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
-    BASE<T> PURE operator /(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) /= rv;
+    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,
@@ -217,13 +205,13 @@
      * 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 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
-    BASE<T> PURE operator /(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(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;
     }
 };
 
@@ -236,34 +224,35 @@
  *
  * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
  */
-template <template<typename T> class BASE, typename T>
+template<template<typename T> class VECTOR, 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++) {
+    VECTOR<T>& operator ++() {
+        VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < rhs.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++) {
+
+    VECTOR<T>& operator --() {
+        VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < rhs.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++) {
+
+    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>.
@@ -272,7 +261,7 @@
  * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
  * get all the functionality here.
  */
-template <template<typename T> class BASE, typename T>
+template<template<typename T> class VECTOR, typename T>
 class TVecComparisonOperators {
 public:
     /*
@@ -284,8 +273,8 @@
      */
     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++)
+    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;
@@ -293,41 +282,106 @@
 
     template<typename RT>
     friend inline
-    bool PURE operator !=(const BASE<T>& lv, const BASE<RT>& rv) {
+    bool PURE operator !=(const VECTOR<T>& lv, const VECTOR<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;
+    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
-    bool PURE operator <=(const BASE<T>& lv, const BASE<RT>& rv) {
+    constexpr bool PURE operator <=(const VECTOR<T>& lv, const VECTOR<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;
+    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
-    bool PURE operator >=(const BASE<T>& lv, const BASE<RT>& rv) {
+    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>.
@@ -336,7 +390,7 @@
  * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
  * get all the functionality here.
  */
-template <template<typename T> class BASE, typename T>
+template<template<typename T> class VECTOR, typename T>
 class TVecFunctions {
 public:
     /*
@@ -347,35 +401,212 @@
      * (the first one, BASE<T> being known).
      */
     template<typename RT>
-    friend inline
-    T PURE dot(const BASE<T>& lv, const BASE<RT>& rv) {
+    friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
         T r(0);
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            r += lv[i]*rv[i];
+        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
-    T PURE length(const BASE<T>& lv) {
-        return sqrt( dot(lv, lv) );
+    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
-    T PURE distance(const BASE<T>& lv, const BASE<RT>& rv) {
+    friend inline constexpr T PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
         return length(rv - lv);
     }
 
-    friend inline
-    BASE<T> PURE normalize(const BASE<T>& lv) {
-        return lv * (1 / length(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 android
+}  // namespace details
+}  // namespace android
 
 
-#endif /* UI_TVEC_HELPERS_H */
+#endif  // UI_TVECHELPERS_H_
diff --git a/include/ui/half.h b/include/ui/half.h
new file mode 100644
index 0000000..7a271dc
--- /dev/null
+++ b/include/ui/half.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 UI_HALF_H
+#define UI_HALF_H
+
+#include <stdint.h>
+#include <iosfwd>
+#include <limits>
+#include <type_traits>
+
+#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
+
+#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
+
+#undef LIKELY
+#undef UNLIKELY
+#undef CONSTEXPR
+
+#endif // UI_HALF_H
diff --git a/include/ui/mat2.h b/include/ui/mat2.h
new file mode 100644
index 0000000..37c7221
--- /dev/null
+++ b/include/ui/mat2.h
@@ -0,0 +1,380 @@
+/*
+ * 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_MAT2_H_
+#define UI_MAT2_H_
+
+#include <ui/TMatHelpers.h>
+#include <ui/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
+
+#endif  // UI_MAT2_H_
diff --git a/include/ui/mat3.h b/include/ui/mat3.h
new file mode 100644
index 0000000..4f5dba9
--- /dev/null
+++ b/include/ui/mat3.h
@@ -0,0 +1,443 @@
+/*
+ * 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_MAT3_H_
+#define UI_MAT3_H_
+
+#include <ui/quat.h>
+#include <ui/TMatHelpers.h>
+#include <ui/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
+
+#endif  // UI_MAT3_H_
diff --git a/include/ui/mat4.h b/include/ui/mat4.h
index 4fd1eff..f63d40a 100644
--- a/include/ui/mat4.h
+++ b/include/ui/mat4.h
@@ -14,173 +14,331 @@
  * limitations under the License.
  */
 
-#ifndef UI_MAT4_H
-#define UI_MAT4_H
+#ifndef UI_MAT4_H_
+#define UI_MAT4_H_
+
+#include <ui/mat3.h>
+#include <ui/quat.h>
+#include <ui/TMatHelpers.h>
+#include <ui/vec3.h>
+#include <ui/vec4.h>
 
 #include <stdint.h>
 #include <sys/types.h>
-
-#include <ui/vec4.h>
-#include <utils/String8.h>
-
-#define TMAT_IMPLEMENTATION
-#include <ui/TMatHelpers.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 TMatDebug<tmat44, 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;
+    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*<>
+    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  -->
      *
-     *  a00 a10 a20 ... aN0    ^
-     *  a01 a11 a21 ... aN1    |
-     *  a02 a12 a22 ... aN2  M rows
-     *  ...                    |
-     *  a0M a1M a2M ... aNM    v
+     *  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] = [a00 a01 a02 ... a01M]
+     *  m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
      */
 
-    col_type mValue[ROW_SIZE];
+    col_type m_value[NUM_COLS];
 
 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]; }
+    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];
+    }
 
-    T const* asArray() const { return &mValue[0][0]; }
+    inline col_type& operator[](size_t column) {
+        assert(column < NUM_COLS);
+        return m_value[column];
+    }
 
     // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
+    // 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 tmat44(no_init) { }
+    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
-    tmat44();
+    /** 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.
+    /** 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 tmat44(U v);
+    explicit CONSTEXPR TMat44(U v);
 
-    // sets the diagonal to the passed vector
+    /** 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 tmat44(const tvec4<U>& rhs);
+    explicit CONSTEXPR TMat44(const TVec4<U>& v);
 
     // construct from another matrix of the same size
     template <typename U>
-    explicit tmat44(const tmat44<U>& rhs);
+    explicit CONSTEXPR TMat44(const TMat44<U>& rhs);
 
-    // construct from 4 column vectors
+    /** 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>
-    tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3);
+    CONSTEXPR TMat44(const TVec4<A>& v0, const TVec4<B>& v1, const TVec4<C>& v2, const TVec4<D>& v3);
 
-    // construct from 16 scalars
+    /** 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>
-    tmat44( A m00, B m01, C m02, D m03,
+    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 C array
+    /**
+     * construct from a quaternion
+     */
     template <typename U>
-    explicit tmat44(U const* rawArray);
+    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 tmat44 ortho(T left, T right, T bottom, T top, T near, T far);
+    static CONSTEXPR 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);
+    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 tmat44 lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up);
+    static CONSTEXPR TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up);
 
     template <typename A>
-    static tmat44 translate(const tvec4<A>& t);
+    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 tmat44 scale(const tvec4<A>& s);
+    static CONSTEXPR TVec4<A> project(const TMat44& projectionMatrix, TVec4<A> vertice) {
+        vertice = projectionMatrix * vertice;
+        return { vertice.xyz / vertice.w, 1 };
+    }
 
-    template <typename A, typename B>
-    static tmat44 rotate(A radian, const tvec3<B>& about);
+    /**
+     * 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.
- */
+// 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);
+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>
-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);
+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>
-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);
+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
@@ -190,38 +348,96 @@
     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);
+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>
-tmat44<T>::tmat44(const tmat44<U>& rhs) {
-    for (size_t r=0 ; r<row_size() ; r++)
-        mValue[r] = rhs[r];
+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>
-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;
+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>
-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++;
+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
 }
 
 // ----------------------------------------------------------------------------------------
@@ -229,8 +445,8 @@
 // ----------------------------------------------------------------------------------------
 
 template <typename T>
-tmat44<T> tmat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
-    tmat44<T> m;
+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);
@@ -241,88 +457,55 @@
 }
 
 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;
+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>
-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));
-}
+CONSTEXPR TMat44<T> TMat44<T>::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) {
+    T h;
+    T w;
 
-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;
+    if (direction == TMat44::Fov::VERTICAL) {
+        h = std::tan(fov * M_PI / 360.0f) * near;
+        w = h * aspect;
     } 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;
+        w = std::tan(fov * M_PI / 360.0f) * near;
+        h = w / aspect;
     }
-    return rotation;
+    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));
 }
 
 // ----------------------------------------------------------------------------------------
@@ -337,40 +520,46 @@
  * it determines the output type (only relevant when T != U).
  */
 
-// matrix * vector, result is a vector of the same type than the input vector
+// matrix * column-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];
+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;
 }
 
-// vector * matrix, result is a vector of the same type than the input vector
+// mat44 * vec3, result is vec3( mat44 * {vec3, 1} )
 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]);
+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>
-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;
+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>
-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;
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(U lhs, const TMat44<T>& rhs) {
+    return rhs * lhs;
 }
 
 // ----------------------------------------------------------------------------------------
@@ -379,17 +568,22 @@
  * BASE<T>::col_type is not accessible from there (???)
  */
 template<typename T>
-typename tmat44<T>::col_type PURE diag(const tmat44<T>& m) {
+typename TMat44<T>::col_type PURE diag(const TMat44<T>& m) {
     return matrix::diag(m);
 }
 
-// ----------------------------------------------------------------------------------------
-
-typedef tmat44<float> mat4;
+} // namespace details
 
 // ----------------------------------------------------------------------------------------
-}; // namespace android
+
+typedef details::TMat44<double> mat4d;
+typedef details::TMat44<float> mat4;
+typedef details::TMat44<float> mat4f;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
 
 #undef PURE
+#undef CONSTEXPR
 
-#endif /* UI_MAT4_H */
+#endif  // UI_MAT4_H_
diff --git a/include/ui/quat.h b/include/ui/quat.h
new file mode 100644
index 0000000..5b8cd8b
--- /dev/null
+++ b/include/ui/quat.h
@@ -0,0 +1,195 @@
+/*
+ * 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_QUAT_H_
+#define UI_QUAT_H_
+
+#include <ui/half.h>
+#include <ui/TQuatHelpers.h>
+#include <ui/vec3.h>
+#include <ui/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
+
+#endif  // UI_QUAT_H_
diff --git a/include/ui/scalar.h b/include/ui/scalar.h
new file mode 100644
index 0000000..5f8329e
--- /dev/null
+++ b/include/ui/scalar.h
@@ -0,0 +1,47 @@
+/*
+ * 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 UI_SCALAR_H
+#define UI_SCALAR_H
+
+#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
+
+#endif // UI_SCALAR_H
diff --git a/include/ui/vec2.h b/include/ui/vec2.h
index c31d0e4..308d2b8 100644
--- a/include/ui/vec2.h
+++ b/include/ui/vec2.h
@@ -14,25 +14,32 @@
  * limitations under the License.
  */
 
-#ifndef UI_VEC2_H
-#define UI_VEC2_H
+#ifndef UI_VEC2_H_
+#define UI_VEC2_H_
 
+#include <ui/TVecHelpers.h>
+#include <ui/half.h>
+#include <assert.h>
 #include <stdint.h>
 #include <sys/types.h>
+#include <type_traits>
 
-#define TVEC_IMPLEMENTATION
-#include <ui/TVecHelpers.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 tvec2 :   public TVecProductOperators<tvec2, T>,
-                public TVecAddOperators<tvec2, T>,
-                public TVecUnaryOperators<tvec2, T>,
-                public TVecComparisonOperators<tvec2, T>,
-                public TVecFunctions<tvec2, 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;
@@ -46,46 +53,76 @@
         struct { T r, g; };
     };
 
-    enum { SIZE = 2 };
-    inline static size_type size() { return SIZE; }
+    static constexpr size_t SIZE = 2;
+    inline constexpr size_type size() const { return SIZE; }
 
     // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
+    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 don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
+    // 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 tvec2(no_init) { }
+    explicit
+    constexpr TVec2(no_init) { }
 
     // default constructor
-    tvec2() : x(0), y(0) { }
+    constexpr 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 = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+    constexpr TVec2(A v) : x(v), y(v) { }
 
     template<typename A, typename B>
-    tvec2(A x, B y) : x(x), y(y) { }
+    constexpr TVec2(A x, B y) : x(x), y(y) { }
 
     template<typename A>
-    explicit tvec2(const tvec2<A>& v) : x(v.x), y(v.y) { }
+    explicit
+    constexpr 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) { }
+    // 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);
+    }
 };
 
-// ----------------------------------------------------------------------------------------
-
-typedef tvec2<float> vec2;
+}  // namespace details
 
 // ----------------------------------------------------------------------------------------
-}; // namespace android
 
-#endif /* UI_VEC4_H */
+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
+
+#endif  // UI_VEC2_H_
diff --git a/include/ui/vec3.h b/include/ui/vec3.h
index dde59a9..e3a6d14 100644
--- a/include/ui/vec3.h
+++ b/include/ui/vec3.h
@@ -14,24 +14,30 @@
  * limitations under the License.
  */
 
-#ifndef UI_VEC3_H
-#define UI_VEC3_H
+#ifndef UI_VEC3_H_
+#define UI_VEC3_H_
 
+#include <ui/vec2.h>
+#include <ui/half.h>
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <ui/vec2.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>
-{
+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;
@@ -43,71 +49,86 @@
         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;
+        TVec2<T> xy;
+        TVec2<T> st;
+        TVec2<T> rg;
     };
 
-    enum { SIZE = 3 };
-    inline static size_type size() { return SIZE; }
+    static constexpr size_t SIZE = 3;
+    inline constexpr size_type size() const { return SIZE; }
 
     // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
+    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 don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
+    // 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 tvec3(no_init) { }
+    explicit
+    constexpr TVec3(no_init) { }
 
     // default constructor
-    tvec3() : x(0), y(0), z(0) { }
+    constexpr 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 = 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>
-    tvec3(A x, B y, C z) : x(x), y(y), z(z) { }
+    constexpr 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) { }
+    constexpr 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) { }
+    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
-    tvec3 __attribute__((pure)) cross(const tvec3& u, const tvec3<RT>& v) {
-        return tvec3(
+    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 tvec3<float> vec3;
+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
+}  // namespace android
 
-#endif /* UI_VEC4_H */
+#pragma clang diagnostic pop
+
+#endif  // UI_VEC3_H_
diff --git a/include/ui/vec4.h b/include/ui/vec4.h
index e03d331..9346fb3 100644
--- a/include/ui/vec4.h
+++ b/include/ui/vec4.h
@@ -14,24 +14,30 @@
  * limitations under the License.
  */
 
-#ifndef UI_VEC4_H
-#define UI_VEC4_H
+#ifndef UI_VEC4_H_
+#define UI_VEC4_H_
 
+#include <ui/vec3.h>
+#include <ui/half.h>
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <ui/vec3.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>
-{
+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;
@@ -43,76 +49,83 @@
         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;
+        TVec2<T> xy;
+        TVec2<T> st;
+        TVec2<T> rg;
+        TVec3<T> xyz;
+        TVec3<T> stp;
+        TVec3<T> rgb;
     };
 
-    enum { SIZE = 4 };
-    inline static size_type size() { return SIZE; }
+    static constexpr size_t SIZE = 4;
+    inline constexpr size_type size() const { return SIZE; }
 
     // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
+    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 don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
+    // 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 tvec4(no_init) { }
+    explicit
+    constexpr TVec4(no_init) { }
 
     // default constructor
-    tvec4() : x(0), y(0), z(0), w(0) { }
+    constexpr 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 = 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>
-    tvec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
+    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>
-    tvec4(const tvec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
+    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>
-    tvec4(const tvec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+    constexpr 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) { }
+    explicit
+    constexpr TVec4(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
 };
 
-// ----------------------------------------------------------------------------------------
-
-typedef tvec4<float> vec4;
+}  // namespace details
 
 // ----------------------------------------------------------------------------------------
-}; // namespace android
 
-#endif /* UI_VEC4_H */
+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
+
+#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..20e4f7c
--- /dev/null
+++ b/include/vr/vr_manager/vr_manager.h
@@ -0,0 +1,80 @@
+/*
+ * 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 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 bool getVrModeState() = 0;
+};
+
+enum VrManagerTransaction {
+    REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
+    UNREGISTER_LISTENER,
+    GET_VR_MODE_STATE,
+};
+
+enum class VrDisplayStateTransaction {
+  ON_DISPLAY_STATE_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class IVrDisplayStateService : public IInterface {
+public:
+    DECLARE_META_INTERFACE(VrDisplayStateService)
+
+    virtual void displayAvailable(bool available) = 0;
+};
+
+class BnVrDisplayStateService : public BnInterface<IVrDisplayStateService> {
+public:
+    status_t onTransact(uint32_t code, const Parcel &data, Parcel *reply,
+                        uint32_t flags = 0) override;
+};
+
+};  // namespace android
+
+#endif // ANDROID_VR_MANAGER_H
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 4780757..93b8684 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -21,6 +21,7 @@
         "BpBinder.cpp",
         "BufferedTextOutput.cpp",
         "Debug.cpp",
+        "IActivityManager.cpp",
         "IAppOpsCallback.cpp",
         "IAppOpsService.cpp",
         "IBatteryStats.cpp",
@@ -32,6 +33,7 @@
         "IProcessInfoService.cpp",
         "IResultReceiver.cpp",
         "IServiceManager.cpp",
+        "IShellCallback.cpp",
         "MemoryBase.cpp",
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
@@ -43,6 +45,8 @@
         "Static.cpp",
         "Status.cpp",
         "TextOutput.cpp",
+        "IpPrefix.cpp",
+        "Value.cpp",
     ],
 
     cflags: [
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..87b295a
--- /dev/null
+++ b/libs/binder/IActivityManager.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.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..f4e0a60 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -287,7 +287,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 +322,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/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..da94305 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) {
@@ -1106,6 +1107,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 +1147,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 +1158,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 +1335,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 +1552,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 +2069,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 +2095,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 +2113,6 @@
     return h;
 }
 
-
 int Parcel::readFileDescriptor() const
 {
     const flat_binder_object* flat = readObject(true);
@@ -1996,6 +2124,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 +2143,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 +2217,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/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/tests/Android.bp b/libs/binder/tests/Android.bp
index 2152206..0dc4469 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: [
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/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..1a05a52
--- /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_a.getInt(&int_x));
+    ASSERT_EQ(31337, int_x);
+}
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 7ac03f1..3815bdc 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -42,6 +42,9 @@
         "-Wno-nested-anon-types",
         "-Wno-gnu-anonymous-struct",
 
+        // We are aware of the risks inherent in comparing floats for equality
+        "-Wno-float-equal",
+
         "-DDEBUG_ONLY_CODE=0",
     ],
 
@@ -71,6 +74,7 @@
         "ConsumerBase.cpp",
         "CpuConsumer.cpp",
         "DisplayEventReceiver.cpp",
+        "FrameTimestamps.cpp",
         "GLConsumer.cpp",
         "GraphicBufferAlloc.cpp",
         "GraphicsEnv.cpp",
@@ -97,17 +101,17 @@
 
     shared_libs: [
         "libnativeloader",
+        "libsync",
         "libbinder",
         "libcutils",
         "libEGL",
         "libGLESv2",
-        "libsync",
         "libui",
         "libutils",
         "liblog",
     ],
 
-    export_shared_lib_headers: ["libbinder"],
+    export_shared_lib_headers: ["libbinder", "libui"],
 }
 
 subdirs = ["tests"]
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/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 6de98f5..f76a282 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -61,18 +61,19 @@
     }
 }
 
-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) {
+        const sp<IGraphicBufferAlloc>& allocator,
+        bool consumerIsSurfaceFlinger) {
     LOG_ALWAYS_FATAL_IF(outProducer == NULL,
             "BufferQueue: outProducer must not be NULL");
     LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
@@ -82,7 +83,7 @@
     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..d66aa1a 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>(
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 13b900c..3f69b1f 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -42,12 +42,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 +348,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 +417,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 +434,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 +454,8 @@
         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;
@@ -497,7 +506,7 @@
         status_t error;
         BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
         sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
-                width, height, format, usage,
+                width, height, format, BQ_LAYER_COUNT, usage,
                 {mConsumerName.string(), mConsumerName.size()}, &error));
         { // Autolock scope
             Mutex::Autolock lock(mCore->mMutex);
@@ -552,6 +561,8 @@
             mSlots[*outSlot].mFrameNumber,
             mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
 
+    addAndGetFrameTimestamps(nullptr, outTimestamps);
+
     return returnFlags;
 }
 
@@ -732,23 +743,27 @@
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(slot);
 
-    int64_t timestamp;
+    int64_t requestedPresentTimestamp;
     bool isAutoTimestamp;
     android_dataspace dataSpace;
     Rect crop(Rect::EMPTY_RECT);
     int scalingMode;
     uint32_t transform;
     uint32_t stickyTransform;
-    sp<Fence> fence;
-    input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode,
-            &transform, &fence, &stickyTransform);
+    sp<Fence> acquireFence;
+    bool getFrameTimestamps = false;
+    input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
+            &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
+            &getFrameTimestamps);
     Region surfaceDamage = input.getSurfaceDamage();
 
-    if (fence == NULL) {
+    if (acquireFence == NULL) {
         BQ_LOGE("queueBuffer: fence is NULL");
         return BAD_VALUE;
     }
 
+    auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);
+
     switch (scalingMode) {
         case NATIVE_WINDOW_SCALING_MODE_FREEZE:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
@@ -763,6 +778,7 @@
     sp<IConsumerListener> frameAvailableListener;
     sp<IConsumerListener> frameReplacedListener;
     int callbackTicket = 0;
+    uint64_t currentFrameNumber = 0;
     BufferItem item;
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
@@ -801,8 +817,9 @@
 
         BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
                 " crop=[%d,%d,%d,%d] transform=%#x scale=%s",
-                slot, mCore->mFrameCounter + 1, timestamp, dataSpace,
-                crop.left, crop.top, crop.right, crop.bottom, transform,
+                slot, mCore->mFrameCounter + 1, requestedPresentTimestamp,
+                dataSpace, crop.left, crop.top, crop.right, crop.bottom,
+                transform,
                 BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));
 
         const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
@@ -820,11 +837,14 @@
             dataSpace = mCore->mDefaultBufferDataSpace;
         }
 
-        mSlots[slot].mFence = fence;
+        mSlots[slot].mFence = acquireFence;
         mSlots[slot].mBufferState.queue();
 
+        // Increment the frame counter and store a local version of it
+        // for use outside the lock on mCore->mMutex.
         ++mCore->mFrameCounter;
-        mSlots[slot].mFrameNumber = mCore->mFrameCounter;
+        currentFrameNumber = mCore->mFrameCounter;
+        mSlots[slot].mFrameNumber = currentFrameNumber;
 
         item.mAcquireCalled = mSlots[slot].mAcquireCalled;
         item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
@@ -834,12 +854,13 @@
         item.mTransformToDisplayInverse =
                 (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
         item.mScalingMode = static_cast<uint32_t>(scalingMode);
-        item.mTimestamp = timestamp;
+        item.mTimestamp = requestedPresentTimestamp;
         item.mIsAutoTimestamp = isAutoTimestamp;
         item.mDataSpace = dataSpace;
-        item.mFrameNumber = mCore->mFrameCounter;
+        item.mFrameNumber = currentFrameNumber;
         item.mSlot = slot;
-        item.mFence = fence;
+        item.mFence = acquireFence;
+        item.mFenceTime = acquireFenceTime;
         item.mIsDroppable = mCore->mAsyncMode ||
                 mCore->mDequeueBufferCannotBlock ||
                 (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
@@ -858,6 +879,7 @@
             mCore->mSharedBufferCache.dataspace = dataSpace;
         }
 
+        output->bufferReplaced = false;
         if (mCore->mQueue.empty()) {
             // When the queue is empty, we can ignore mDequeueBufferCannotBlock
             // and simply queue this buffer
@@ -884,6 +906,7 @@
                     if (!mSlots[last.mSlot].mBufferState.isShared()) {
                         mCore->mActiveBuffers.erase(last.mSlot);
                         mCore->mFreeBuffers.push_back(last.mSlot);
+                        output->bufferReplaced = true;
                     }
                 }
 
@@ -900,10 +923,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()));
@@ -915,9 +939,14 @@
         VALIDATE_CONSISTENCY();
     } // Autolock scope
 
-    // Don't send the GraphicBuffer through the callback, and don't send
-    // the slot number, since the consumer shouldn't need it
-    item.mGraphicBuffer.clear();
+    // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because
+    // it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and
+    // there will be no Binder call
+    if (!mConsumerIsSurfaceFlinger) {
+        item.mGraphicBuffer.clear();
+    }
+
+    // Don't send the slot number through the callback since the consumer shouldn't need it
     item.mSlot = BufferItem::INVALID_BUFFER_SLOT;
 
     // Call back without the main BufferQueue lock held, but with the callback
@@ -945,10 +974,21 @@
         // small trade-off in favor of latency rather than throughput.
         mLastQueueBufferFence->waitForever("Throttling EGL Production");
     }
-    mLastQueueBufferFence = fence;
+    mLastQueueBufferFence = std::move(acquireFence);
     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;
 }
 
@@ -1031,6 +1071,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;
@@ -1109,10 +1153,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
@@ -1279,8 +1327,9 @@
         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));
+                    allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+                    allocUsage, {mConsumerName.string(), mConsumerName.size()},
+                    &result));
             if (result != NO_ERROR) {
                 BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
                         " %u, usage %u)", width, height, format, usage);
@@ -1431,20 +1480,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..be2b1af 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -56,7 +56,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());
 
@@ -366,6 +367,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..a9bafef 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -64,6 +64,7 @@
     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_RGB_888:
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_BGRA_8888:
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000..73537bf
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,690 @@
+/*
+* 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>
+
+#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 Fence::isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+    return Fence::isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+    return Fence::isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+    return Fence::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 or retire
+    // info is set. See ConsumerFrameEventHistory::addRetire/Release.
+    return addRetireCalled || addReleaseCalled;
+}
+
+bool FrameEvents::hasDequeueReadyInfo() const {
+    return Fence::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::hasDisplayRetireInfo() const {
+    // We may not get a displayRetire in addRetire for HWC2.
+    return addRetireCalled;
+}
+
+bool FrameEvents::hasReleaseInfo() const {
+    return addReleaseCalled;
+}
+
+void FrameEvents::checkFencesForCompletion() {
+    acquireFence->getSignalTime();
+    gpuCompositionDoneFence->getSignalTime();
+    displayPresentFence->getSignalTime();
+    displayRetireFence->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 (Fence::isValidTimestamp(latchTime)) {
+        outString.appendFormat("%" PRId64 "\n", latchTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    outString.appendFormat("--- Refresh (First)\t");
+    if (Fence::isValidTimestamp(firstRefreshStartTime)) {
+        outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    outString.appendFormat("--- Refresh (Last)\t");
+    if (Fence::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);
+    dumpFenceTime(outString, "Display Retire    \t",
+            !addRetireCalled, *displayRetireFence);
+
+    outString.appendFormat("--- DequeueReady  \t");
+    if (Fence::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;
+
+void ProducerFrameEventHistory::updateAcquireFence(
+        uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) {
+    FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
+    if (frame == nullptr) {
+        ALOGE("ProducerFrameEventHistory::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) {
+    for (auto& d : delta.mDeltas) {
+        // Avoid out-of-bounds access.
+        if (d.mIndex >= mFrames.size()) {
+            ALOGE("ProducerFrameEventHistory::applyDelta: Bad index.");
+            return;
+        }
+
+        FrameEvents& frame = mFrames[d.mIndex];
+
+        frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
+        frame.addRetireCalled = d.mAddRetireCalled != 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.displayRetireFence = 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(&mRetireTimeline,
+                &frame.displayRetireFence, d.mDisplayRetireFence);
+        applyFenceDelta(&mReleaseTimeline,
+                &frame.releaseFence, d.mReleaseFence);
+    }
+}
+
+void ProducerFrameEventHistory::updateSignalTimes() {
+    mAcquireTimeline.updateSignalTimes();
+    mGpuCompositionDoneTimeline.updateSignalTimes();
+    mPresentTimeline.updateSignalTimes();
+    mRetireTimeline.updateSignalTimes();
+    mReleaseTimeline.updateSignalTimes();
+}
+
+void ProducerFrameEventHistory::applyFenceDelta(FenceTimeline* timeline,
+        std::shared_ptr<FenceTime>* dst, const FenceTime::Snapshot& src) const {
+    if (CC_UNLIKELY(dst == nullptr)) {
+        ALOGE("applyFenceDelta: dst is null.");
+        return;
+    }
+
+    switch (src.state) {
+        case FenceTime::Snapshot::State::EMPTY:
+            return;
+        case FenceTime::Snapshot::State::FENCE:
+            if (CC_UNLIKELY((*dst)->isValid())) {
+                ALOGE("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::addQueue(const NewFrameEventsEntry& newEntry) {
+    // Overwrite all fields of the frame with default values unless set here.
+    FrameEvents newTimestamps;
+    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 (!Fence::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) {
+    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::GL_COMPOSITION_DONE>();
+        if (!frame->displayPresentFence->isValid()) {
+            frame->displayPresentFence = displayPresent;
+            mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
+        }
+    }
+}
+
+void ConsumerFrameEventHistory::addRetire(
+        uint64_t frameNumber, const std::shared_ptr<FenceTime>& displayRetire) {
+    FrameEvents* frame = getFrame(frameNumber, &mRetireOffset);
+    if (frame == nullptr) {
+        ALOGE_IF(mProducerWantsEvents, "addRetire: Did not find frame.");
+        return;
+    }
+    frame->addRetireCalled = true;
+    frame->displayRetireFence = displayRetire;
+    mFramesDirty[mRetireOffset].setDirty<FrameEvent::DISPLAY_RETIRE>();
+}
+
+void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber,
+        nsecs_t dequeueReadyTime, std::shared_ptr<FenceTime>&& release) {
+    FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
+    if (frame == nullptr) {
+        ALOGE("ConsumerFrameEventHistory::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()) {
+        delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]);
+        mFramesDirty[i].reset();
+    }
+}
+
+void ConsumerFrameEventHistory::getAndResetDelta(
+        FrameEventHistoryDelta* delta) {
+    // 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),
+      mAddRetireCalled(frameTimestamps.addRetireCalled),
+      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::GL_COMPOSITION_DONE>()) {
+        mGpuCompositionDoneFence =
+                frameTimestamps.gpuCompositionDoneFence->getSnapshot();
+    }
+    if (dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>()) {
+        mDisplayPresentFence =
+                frameTimestamps.displayPresentFence->getSnapshot();
+    }
+    if (dirtyFields.isDirty<FrameEvent::DISPLAY_RETIRE>()) {
+        mDisplayRetireFence = frameTimestamps.displayRetireFence->getSnapshot();
+    }
+    if (dirtyFields.isDirty<FrameEvent::RELEASE>()) {
+        mReleaseFence = frameTimestamps.releaseFence->getSnapshot();
+    }
+}
+
+size_t FrameEventsDelta::minFlattenedSize() {
+    constexpr size_t min =
+            sizeof(FrameEventsDelta::mFrameNumber) +
+            sizeof(uint8_t) + // mIndex
+            sizeof(uint8_t) + // mAddPostCompositeCalled
+            sizeof(uint8_t) + // mAddRetireCalled
+            sizeof(uint8_t) + // mAddReleaseCalled
+            sizeof(FrameEventsDelta::mPostedTime) +
+            sizeof(FrameEventsDelta::mRequestedPresentTime) +
+            sizeof(FrameEventsDelta::mLatchTime) +
+            sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
+            sizeof(FrameEventsDelta::mLastRefreshStartTime) +
+            sizeof(FrameEventsDelta::mDequeueReadyTime);
+    return min;
+}
+
+// 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<uint8_t>::max()) {
+        return BAD_VALUE;
+    }
+
+    FlattenableUtils::write(buffer, size, mFrameNumber);
+
+    // These are static_cast to uint8_t for alignment.
+    FlattenableUtils::write(buffer, size, static_cast<uint8_t>(mIndex));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(mAddRetireCalled));
+    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 uint8_t for alignment.
+    uint8_t temp = 0;
+    FlattenableUtils::read(buffer, size, temp);
+    mIndex = temp;
+    if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    FlattenableUtils::read(buffer, size, temp);
+    mAddPostCompositeCalled = static_cast<bool>(temp);
+    FlattenableUtils::read(buffer, size, temp);
+    mAddRetireCalled = static_cast<bool>(temp);
+    FlattenableUtils::read(buffer, size, temp);
+    mAddReleaseCalled = static_cast<bool>(temp);
+
+    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) {
+    if (CC_UNLIKELY(!mDeltas.empty())) {
+        ALOGE("FrameEventHistoryDelta: Clobbering history.");
+    }
+    mDeltas = std::move(src.mDeltas);
+    ALOGE_IF(src.mDeltas.empty(), "Source mDeltas not empty.");
+    return *this;
+}
+
+size_t FrameEventHistoryDelta::minFlattenedSize() {
+    return sizeof(uint32_t);
+}
+
+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, 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;
+    }
+
+    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..b11a3e5 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -157,6 +157,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 +186,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 +323,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 +491,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 +862,7 @@
             switch (buf->getPixelFormat()) {
                 case PIXEL_FORMAT_RGBA_8888:
                 case PIXEL_FORMAT_RGBX_8888:
+                case PIXEL_FORMAT_RGBA_FP16:
                 case PIXEL_FORMAT_RGB_888:
                 case PIXEL_FORMAT_RGB_565:
                 case PIXEL_FORMAT_BGRA_8888:
@@ -911,6 +918,12 @@
     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);
@@ -981,6 +994,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
index a8f40e0..30f5e53 100644
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ b/libs/gui/GraphicBufferAlloc.cpp
@@ -32,19 +32,21 @@
 }
 
 sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,
-        uint32_t height, PixelFormat format, uint32_t usage,
-        std::string requestorName, status_t* error) {
+        uint32_t height, PixelFormat format, uint32_t layerCount,
+        uint32_t usage, std::string requestorName, status_t* error) {
     sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
-            width, height, format, usage, std::move(requestorName)));
+            width, height, format, layerCount, 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) "
+        ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%u, h=%u, lc=%u) "
              "failed (%s), handle=%p",
-                width, height, strerror(-err), graphicBuffer->handle);
+                width, height, layerCount, strerror(-err),
+                graphicBuffer->handle);
         return 0;
     }
     return graphicBuffer;
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index ff7b83a..3d893b1 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -61,42 +61,6 @@
         data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
         remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
     }
-
-    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;
-    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -125,30 +89,6 @@
             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;
-        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
index 2fb380c..a3d3b74 100644
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ b/libs/gui/IGraphicBufferAlloc.cpp
@@ -45,13 +45,14 @@
     virtual ~BpGraphicBufferAlloc();
 
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t usage,
-            std::string requestorName, status_t* error) {
+            uint32_t height, PixelFormat format, uint32_t layerCount,
+            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(layerCount);
         data.writeUint32(usage);
         if (requestorName.empty()) {
             requestorName += "[PID ";
@@ -106,12 +107,13 @@
             uint32_t width = data.readUint32();
             uint32_t height = data.readUint32();
             PixelFormat format = static_cast<PixelFormat>(data.readInt32());
+            uint32_t layerCount = data.readUint32();
             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);
+                    format, layerCount, usage, requestorName, &error);
             reply->writeInt32(error);
             if (result != 0) {
                 reply->write(*result);
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 2401464..ef770e8 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -19,6 +19,7 @@
 
 #include <utils/Errors.h>
 #include <utils/NativeHandle.h>
+#include <utils/String8.h>
 
 #include <binder/Parcel.h>
 #include <binder/IInterface.h>
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 846c205..abdf649 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>
 
@@ -118,24 +119,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 +223,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 +284,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 +441,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 {
@@ -522,14 +525,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 +580,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 +618,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 +723,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 +757,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 +784,7 @@
     if (size < getFlattenedSize()) {
         return NO_MEMORY;
     }
+
     FlattenableUtils::write(buffer, size, timestamp);
     FlattenableUtils::write(buffer, size, isAutoTimestamp);
     FlattenableUtils::write(buffer, size, dataSpace);
@@ -793,6 +792,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 +804,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 +815,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 +825,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
index 59ecee7..8af51c5 100644
--- a/libs/gui/ISensorEventConnection.cpp
+++ b/libs/gui/ISensorEventConnection.cpp
@@ -34,7 +34,8 @@
     GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
     ENABLE_DISABLE,
     SET_EVENT_RATE,
-    FLUSH_SENSOR
+    FLUSH_SENSOR,
+    CONFIGURE_CHANNEL
 };
 
 class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
@@ -85,6 +86,15 @@
         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
@@ -131,6 +141,15 @@
             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);
 }
diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp
index 07c507a..aea7403 100644
--- a/libs/gui/ISensorServer.cpp
+++ b/libs/gui/ISensorServer.cpp
@@ -17,6 +17,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <cutils/native_handle.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
@@ -37,6 +38,7 @@
     CREATE_SENSOR_EVENT_CONNECTION,
     ENABLE_DATA_INJECTION,
     GET_DYNAMIC_SENSOR_LIST,
+    CREATE_SENSOR_DIRECT_CONNECTION,
 };
 
 class BpSensorServer : public BpInterface<ISensorServer>
@@ -101,6 +103,19 @@
         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());
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -153,6 +168,20 @@
             }
             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;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0a8e6a5..2a327da 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -64,6 +64,16 @@
         return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
     }
 
+    virtual sp<ISurfaceComposerClient> createScopedConnection(
+            const sp<IGraphicBufferProducer>& parent)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(IInterface::asBinder(parent));
+        remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply);
+        return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
+    }
+
     virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc()
     {
         Parcel data, reply;
@@ -104,7 +114,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 +125,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 +168,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;
@@ -383,6 +437,48 @@
         }
         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,6 +499,14 @@
             reply->writeStrongBinder(b);
             return NO_ERROR;
         }
+        case CREATE_SCOPED_CONNECTION: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IGraphicBufferProducer> bufferProducer =
+                interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+            sp<IBinder> b = IInterface::asBinder(createScopedConnection(bufferProducer));
+            reply->writeStrongBinder(b);
+            return NO_ERROR;
+        }
         case CREATE_GRAPHIC_BUFFER_ALLOC: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> b = IInterface::asBinder(createGraphicBufferAlloc());
@@ -458,8 +562,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 +582,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());
@@ -635,6 +758,26 @@
             }
             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..5a3fa04 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -56,8 +56,8 @@
 
     virtual status_t createSurface(const String8& name, uint32_t width,
             uint32_t height, PixelFormat format, uint32_t flags,
-            sp<IBinder>* handle,
-            sp<IGraphicBufferProducer>* gbp) {
+            const sp<IBinder>& parent, uint32_t windowType, uint32_t ownerUid,
+            sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
         data.writeString8(name);
@@ -65,6 +65,11 @@
         data.writeUint32(height);
         data.writeInt32(static_cast<int32_t>(format));
         data.writeUint32(flags);
+        data.writeUint32(windowType);
+        data.writeUint32(ownerUid);
+        if (parent != nullptr) {
+            data.writeStrongBinder(parent);
+        }
         remote()->transact(CREATE_SURFACE, data, &reply);
         *handle = reply.readStrongBinder();
         *gbp = interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
@@ -145,10 +150,16 @@
             uint32_t height = data.readUint32();
             PixelFormat format = static_cast<PixelFormat>(data.readInt32());
             uint32_t createFlags = data.readUint32();
+            uint32_t windowType = data.readUint32();
+            uint32_t ownerUid = data.readUint32();
+            sp<IBinder> parent = nullptr;
+            if (data.dataAvail() > 0) {
+                parent = data.readStrongBinder();
+            }
             sp<IBinder> handle;
             sp<IGraphicBufferProducer> gbp;
             status_t result = createSurface(name, width, height, format,
-                    createFlags, &handle, &gbp);
+                    createFlags, parent, windowType, ownerUid, &handle, &gbp);
             reply->writeStrongBinder(handle);
             reply->writeStrongBinder(IInterface::asBinder(gbp));
             reply->writeInt32(result);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index d1c576e..bb552aa 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);
@@ -40,6 +40,7 @@
     output.write(crop);
     output.write(finalCrop);
     output.writeStrongBinder(handle);
+    output.writeStrongBinder(reparentHandle);
     output.writeUint64(frameNumber);
     output.writeInt32(overrideScalingMode);
     output.write(transparentRegion);
@@ -52,7 +53,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();
@@ -68,6 +69,7 @@
     input.read(crop);
     input.read(finalCrop);
     handle = input.readStrongBinder();
+    reparentHandle = input.readStrongBinder();
     frameNumber = input.readUint64();
     overrideScalingMode = input.readInt32();
     input.read(transparentRegion);
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index 2c87562..2fd29d5 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -246,6 +246,14 @@
         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) {
@@ -292,7 +300,15 @@
     // 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));
+        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) {
@@ -402,6 +418,21 @@
     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);
 }
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
index 57c3073..46eaf28 100644
--- a/libs/gui/SensorManager.cpp
+++ b/libs/gui/SensorManager.cpp
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <cutils/native_handle.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
@@ -89,7 +90,7 @@
 }
 
 SensorManager::SensorManager(const String16& opPackageName)
-    : mSensorList(0), mOpPackageName(opPackageName) {
+    : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
     // okay we're not locked here, but it's not needed during construction
     assertStateLocked();
 }
@@ -237,5 +238,62 @@
     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;
+    }
+
+    switch (channelType) {
+        case SENSOR_DIRECT_MEM_TYPE_ASHMEM: {
+            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;
+        }
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            LOG_FATAL("%s: Finish implementation of ION and GRALLOC or remove", __FUNCTION__);
+            return BAD_VALUE;
+        default:
+            ALOGE("Bad channel shared memory type %d", channelType);
+            return BAD_VALUE;
+    }
+}
+
+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;
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d1a9cbb..c2ed91a 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -28,6 +28,7 @@
 
 #include <ui/Fence.h>
 #include <ui/Region.h>
+#include <ui/DisplayStatInfo.h>
 
 #include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
@@ -49,7 +50,11 @@
       mAutoRefresh(false),
       mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
       mSharedBufferHasBeenQueued(false),
-      mNextFrameNumber(1)
+      mQueriedSupportedTimestamps(false),
+      mFrameTimestampsSupportsPresent(false),
+      mFrameTimestampsSupportsRetire(false),
+      mEnableFrameTimestamps(false),
+      mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>())
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
@@ -93,6 +98,10 @@
     }
 }
 
+sp<ISurfaceComposer> Surface::composerService() const {
+    return ComposerService::getComposerService();
+}
+
 sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
     return mGraphicBufferProducer;
 }
@@ -135,37 +144,133 @@
             outTransformMatrix);
 }
 
-bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
-        nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
-        nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
-        nsecs_t* outReleaseTime) {
+void Surface::enableFrameTimestamps(bool enable) {
+    Mutex::Autolock lock(mMutex);
+    mEnableFrameTimestamps = enable;
+}
+
+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* outGlCompositionDoneTime,
+        const nsecs_t* outDisplayPresentTime,
+        const nsecs_t* outDisplayRetireTime,
+        const nsecs_t* outDequeueReadyTime,
+        const nsecs_t* outReleaseTime) {
+    bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo();
+    bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) &&
+            !e->hasFirstRefreshStartInfo();
+    bool checkForGlCompositionDone = (outGlCompositionDoneTime != nullptr) &&
+            !e->hasGpuCompositionDoneInfo();
+    bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) &&
+            !e->hasDisplayPresentInfo();
+
+    // LastRefreshStart, DisplayRetire, DequeueReady, and Release are never
+    // available for the last frame.
+    bool checkForLastRefreshStart = (outLastRefreshStartTime != nullptr) &&
+            !e->hasLastRefreshStartInfo() &&
+            (e->frameNumber != lastFrameNumber);
+    bool checkForDisplayRetire = (outDisplayRetireTime != nullptr) &&
+            !e->hasDisplayRetireInfo() && (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 || checkForGlCompositionDone ||
+            checkForDisplayPresent || checkForDisplayRetire ||
+            checkForDequeueReady || checkForRelease;
+}
+
+static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) {
+    if (dst != nullptr) {
+        *dst = Fence::isValidTimestamp(src) ? src : 0;
+    }
+}
+
+static void getFrameTimestampFence(nsecs_t *dst, const std::shared_ptr<FenceTime>& src) {
+    if (dst != nullptr) {
+        nsecs_t signalTime = src->getSignalTime();
+        *dst = Fence::isValidTimestamp(signalTime) ? signalTime : 0;
+    }
+}
+
+status_t Surface::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* outDisplayRetireTime,
+        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;
+    }
+    if (outDisplayRetireTime != nullptr && !mFrameTimestampsSupportsRetire) {
+        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,
+            outGlCompositionDoneTime, outDisplayPresentTime,
+            outDisplayRetireTime, 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);
+    getFrameTimestampFence(
+            outGlCompositionDoneTime, events->gpuCompositionDoneFence);
+    getFrameTimestampFence(
+            outDisplayPresentTime, events->displayPresentFence);
+    getFrameTimestampFence(outDisplayRetireTime, events->displayRetireFence);
+    getFrameTimestampFence(outReleaseTime, events->releaseFence);
+
+    return NO_ERROR;
+}
+status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) {
+    ATRACE_CALL();
+
+    DisplayStatInfo stats;
+    status_t err = composerService()->getDisplayStats(NULL, &stats);
+
+    *outRefreshDuration = stats.vsyncPeriod;
+
+    return NO_ERROR;
 }
 
 int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
@@ -271,6 +376,7 @@
     uint32_t reqHeight;
     PixelFormat reqFormat;
     uint32_t reqUsage;
+    bool enableFrameTimestamps;
 
     {
         Mutex::Autolock lock(mMutex);
@@ -281,6 +387,8 @@
         reqFormat = mReqFormat;
         reqUsage = mReqUsage;
 
+        enableFrameTimestamps = mEnableFrameTimestamps;
+
         if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                 BufferItem::INVALID_BUFFER_SLOT) {
             sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -295,8 +403,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,6 +428,10 @@
         freeAllBuffers();
     }
 
+    if (enableFrameTimestamps) {
+         mFrameEventHistory->applyDelta(frameTimestamps);
+    }
+
     if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
         result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
         if (result != NO_ERROR) {
@@ -435,7 +550,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 +622,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 +662,31 @@
     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;
+        } else if (sft == FrameEvent::DISPLAY_RETIRE) {
+            mFrameTimestampsSupportsRetire = true;
+        }
+    }
+}
+
 int Surface::query(int what, int* value) const {
     ATRACE_CALL();
     ALOGV("Surface::query");
@@ -546,9 +700,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 +748,16 @@
                         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_FRAME_TIMESTAMPS_SUPPORTS_RETIRE: {
+                querySupportedTimestampsLocked();
+                *value = mFrameTimestampsSupportsRetire ? 1 : 0;
+                return NO_ERROR;
+            }
         }
     }
     return mGraphicBufferProducer->query(what, value);
@@ -670,9 +833,15 @@
     case NATIVE_WINDOW_SET_AUTO_REFRESH:
         res = dispatchSetAutoRefresh(args);
         break;
+    case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS:
+        res = dispatchEnableFrameTimestamps(args);
+        break;
     case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
         res = dispatchGetFrameTimestamps(args);
         break;
+    case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION:
+        res = dispatchGetDisplayRefreshCycleDuration(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -793,18 +962,34 @@
     return setAutoRefresh(autoRefresh);
 }
 
+int Surface::dispatchEnableFrameTimestamps(va_list args) {
+    bool enable = va_arg(args, int);
+    enableFrameTimestamps(enable);
+    return NO_ERROR;
+}
+
 int Surface::dispatchGetFrameTimestamps(va_list args) {
     uint32_t framesAgo = va_arg(args, uint32_t);
-    nsecs_t* outPostedTime = va_arg(args, int64_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* outLatchTime = va_arg(args, int64_t*);
+    nsecs_t* outFirstRefreshStartTime = va_arg(args, int64_t*);
+    nsecs_t* outLastRefreshStartTime = va_arg(args, int64_t*);
     nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
+    nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
     nsecs_t* outDisplayRetireTime = 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(getNextFrameNumber() - 1 - framesAgo,
+            outRequestedPresentTime, outAcquireTime, outLatchTime,
+            outFirstRefreshStartTime, outLastRefreshStartTime,
+            outGlCompositionDoneTime, outDisplayPresentTime,
+            outDisplayRetireTime, outDequeueReadyTime, outReleaseTime);
+}
+
+int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) {
+    nsecs_t* outRefreshDuration = va_arg(args, int64_t*);
+    return getDisplayRefreshCycleDuration(outRefreshDuration);
 }
 
 int Surface::connect(int api) {
@@ -819,17 +1004,16 @@
     IGraphicBufferProducer::QueueBufferOutput output;
     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;
@@ -1396,14 +1580,18 @@
         int isSingleBuffered;
         res = parcel->readInt32(&isSingleBuffered);
         if (res != OK) {
+            ALOGE("Can't read isSingleBuffered");
             return res;
         }
     }
 
     sp<IBinder> binder;
 
-    res = parcel->readStrongBinder(&binder);
-    if (res != OK) return res;
+    res = parcel->readNullableStrongBinder(&binder);
+    if (res != OK) {
+        ALOGE("%s: Can't read strong binder", __FUNCTION__);
+        return res;
+    }
 
     graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 43506e9..ae81c8f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -33,6 +33,7 @@
 
 #include <ui/DisplayInfo.h>
 
+#include <gui/BufferItemConsumer.h>
 #include <gui/CpuConsumer.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ISurfaceComposer.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,7 @@
     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 setFlags(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
             uint32_t flags, uint32_t mask);
     status_t setTransparentRegionHint(
@@ -165,6 +168,9 @@
     status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, const sp<IBinder>& handle,
             uint64_t frameNumber);
+    status_t reparentChildren(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id,
+            const sp<IBinder>& newParentHandle);
     status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, int32_t overrideScalingMode);
     status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client,
@@ -190,6 +196,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 +267,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 +328,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)
@@ -420,6 +444,20 @@
     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::setOverrideScalingMode(
         const sp<SurfaceComposerClient>& client,
         const sp<IBinder>& id, int32_t overrideScalingMode) {
@@ -529,10 +567,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 +621,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);
@@ -652,6 +706,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,7 +733,7 @@
     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);
 }
 
@@ -715,6 +777,11 @@
     return getComposer().deferTransactionUntil(this, id, handle, frameNumber);
 }
 
+status_t SurfaceComposerClient::reparentChildren(const sp<IBinder>& id,
+        const sp<IBinder>& newParentHandle) {
+    return getComposer().reparentChildren(this, id, newParentHandle);
+}
+
 status_t SurfaceComposerClient::setOverrideScalingMode(
         const sp<IBinder>& id, int32_t overrideScalingMode) {
     return getComposer().setOverrideScalingMode(
@@ -824,13 +891,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 +946,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 +973,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 +982,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..0362216 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -102,7 +102,7 @@
     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);
@@ -163,13 +163,19 @@
     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::reparentChildren(const sp<IBinder>& newParentHandle) {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->reparentChildren(mHandle, newParentHandle);
+}
+
 status_t SurfaceControl::setOverrideScalingMode(int32_t overrideScalingMode) {
     status_t err = validate();
     if (err < 0) return err;
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 3c7958f..092d597 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -35,7 +35,6 @@
         "libbinder",
         "libcutils",
         "libgui",
-        "libsync",
         "libui",
         "libutils",
     ],
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 65df7dc..91ce531 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -139,7 +139,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 +183,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 +191,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 +234,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 +270,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 +280,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 +330,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 +379,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 +415,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 +438,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 +487,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 +501,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 +518,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 +561,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 +575,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 +613,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 +640,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 +680,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 +697,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 +733,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 +761,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 +773,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 +795,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 +818,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 +830,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 +882,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 +895,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 +911,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 +936,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 +1021,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 +1032,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));
@@ -1067,4 +1076,45 @@
     }
 }
 
+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);
+}
+
 } // namespace android
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 9f33047..0329a6d 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -196,7 +196,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 +210,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 +349,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 +366,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 +408,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 +477,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 +526,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 +563,7 @@
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                          DEFAULT_WIDTH, DEFAULT_HEIGHT,
                                          DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS)))
+                                         TEST_PRODUCER_USAGE_BITS, nullptr)))
                 << "slot: " << dequeuedSlot;
     }
 
@@ -606,7 +598,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 +615,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 +633,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 +653,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/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/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0de60c9..412c0f6 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -19,18 +19,28 @@
 #include <gtest/gtest.h>
 
 #include <binder/IMemory.h>
+#include <binder/ProcessState.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;
+
+class FakeSurfaceComposer;
+class FakeProducerFrameEventHistory;
+
+static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max();
+
 class SurfaceTest : public ::testing::Test {
 protected:
 
@@ -77,6 +87,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 +108,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 +153,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;
@@ -258,4 +279,951 @@
     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 setSupportedTimestamps(bool supportsPresent, bool supportsRetire) {
+        mSupportsPresent = supportsPresent;
+        mSupportsRetire = supportsRetire;
+    }
+
+    sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
+    sp<ISurfaceComposerClient> createScopedConnection(
+            const sp<IGraphicBufferProducer>& /* parent */) override {
+        return nullptr;
+    }
+    sp<IGraphicBufferAlloc> createGraphicBufferAlloc() 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::GL_COMPOSITION_DONE,
+                FrameEvent::DEQUEUE_READY,
+                FrameEvent::RELEASE
+        };
+        if (mSupportsPresent) {
+            outSupported->push_back(
+                        FrameEvent::DISPLAY_PRESENT);
+        }
+        if (mSupportsRetire) {
+            outSupported->push_back(
+                        FrameEvent::DISPLAY_RETIRE);
+        }
+        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};
+    bool mSupportsRetire{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;
+    }
+
+public:
+    sp<FakeSurfaceComposer> mFakeSurfaceComposer;
+
+    // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory,
+    // but this raw pointer gives access to test functionality.
+    FakeProducerFrameEventHistory* mFakeFrameEventHistory;
+};
+
+
+class GetFrameTimestampsTest : public SurfaceTest {
+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),
+              kStartTime(refreshStart + 1),
+              kGpuCompositionDoneTime(refreshStart + 2),
+              kPresentTime(refreshStart + 3) {}
+
+        void signalPostCompositeFences() {
+            mFenceMap.signalAllForTest(
+                        mGpuCompositionDone.mFence, kGpuCompositionDoneTime);
+            mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime);
+        }
+
+        FenceToFenceTimeMap& mFenceMap;
+
+        FenceAndFenceTime mGpuCompositionDone { mFenceMap };
+        FenceAndFenceTime mPresent { mFenceMap };
+
+        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),
+              kRetireTime(frameStartTime + 700),
+              kReleaseTime(frameStartTime + 800),
+              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(mRetire.mFence, kRetireTime);
+            mFenceMap.signalAllForTest(mRelease.mFence, kReleaseTime);
+        }
+
+        FenceToFenceTimeMap& mFenceMap;
+
+        FenceAndFenceTime mAcquireConsumer { mFenceMap };
+        FenceAndFenceTime mAcquireProducer { mFenceMap };
+        FenceAndFenceTime mRetire { 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 kRetireTime;
+        const nsecs_t kReleaseTime;
+
+        RefreshEvents mRefreshes[3];
+    };
+
+    GetFrameTimestampsTest() : SurfaceTest() {}
+
+    virtual void SetUp() {
+        SurfaceTest::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 enableFrameTimestamps() {
+        mFakeConsumer->mGetFrameTimestampsEnabled = true;
+        native_window_enable_frame_timestamps(mWindow.get(), 1);
+        mFrameTimestampsEnabled = true;
+    }
+
+    int getAllFrameTimestamps(uint32_t framesAgo) {
+        return native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+                &outRequestedPresentTime, &outAcquireTime, &outLatchTime,
+                &outFirstRefreshStartTime, &outLastRefreshStartTime,
+                &outGpuCompositionDoneTime, &outDisplayPresentTime,
+                &outDisplayRetireTime, &outDequeueReadyTime, &outReleaseTime);
+    }
+
+    void resetTimestamps() {
+        outRequestedPresentTime = -1;
+        outAcquireTime = -1;
+        outLatchTime = -1;
+        outFirstRefreshStartTime = -1;
+        outLastRefreshStartTime = -1;
+        outGpuCompositionDoneTime = -1;
+        outDisplayPresentTime = -1;
+        outDisplayRetireTime = -1;
+        outDequeueReadyTime = -1;
+        outReleaseTime = -1;
+    }
+
+    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, Retire, 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);
+        }
+
+        // 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);
+
+        // Retire the previous buffer just after compositing the new buffer.
+        if (oldFrame != nullptr) {
+            mCfeh->addRetire(nOldFrame, oldFrame->mRetire.mFenceTime);
+        }
+
+        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);
+    }
+
+    void QueryPresentRetireSupported(
+            bool displayPresentSupported, bool displayRetireSupported);
+    void PresentOrRetireUnsupportedNoSyncTest(
+            bool displayPresentSupported, bool displayRetireSupported);
+
+    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 outDisplayRetireTime = -1;
+    int64_t outDequeueReadyTime = -1;
+    int64_t outReleaseTime = -1;
+
+    FrameEvents mFrames[2] { { mFenceMap, 1000 }, { mFenceMap, 2000 } };
+};
+
+
+// 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);
+
+    // 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.
+    const uint32_t framesAgo = 0;
+    int result = getAllFrameTimestamps(framesAgo);
+    EXPECT_EQ(INVALID_OPERATION, result);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+// This test verifies that the frame timestamps are retrieved if explicitly
+// enabled via native_window_enable_frame_timestamps.
+TEST_F(GetFrameTimestampsTest, EnabledSimple) {
+    enableFrameTimestamps();
+
+    int fence;
+    ANativeWindowBuffer* buffer;
+
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify getFrameTimestamps is piggybacked on dequeue.
+    ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(1, 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(2, 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.
+    const uint32_t framesAgo = 0;
+    int result = getAllFrameTimestamps(framesAgo);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+void GetFrameTimestampsTest::QueryPresentRetireSupported(
+        bool displayPresentSupported, bool displayRetireSupported) {
+    mSurface->mFakeSurfaceComposer->setSupportedTimestamps(
+            displayPresentSupported, displayRetireSupported);
+
+    // Verify supported bits are forwarded.
+    int supportsPresent = -1;
+    mWindow.get()->query(mWindow.get(),
+            NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &supportsPresent);
+    EXPECT_EQ(displayPresentSupported, supportsPresent);
+
+    int supportsRetire = -1;
+    mWindow.get()->query(mWindow.get(),
+            NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE, &supportsRetire);
+    EXPECT_EQ(displayRetireSupported, supportsRetire);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryPresentSupported) {
+   QueryPresentRetireSupported(true, false);
+}
+
+TEST_F(GetFrameTimestampsTest, QueryRetireSupported) {
+   QueryPresentRetireSupported(false, true);
+}
+
+// This test verifies that:
+// 1) The timestamps recorded in the consumer's FrameTimestampsHistory are
+//    properly retrieved by the producer for the correct frames.
+// 2) When framesAgo is 0, it is querying for the most recently queued frame.
+TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) {
+    enableFrameTimestamps();
+
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    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.
+    uint32_t framesAgo = 1;
+    resetTimestamps();
+    int result = getAllFrameTimestamps(framesAgo);
+    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].kRetireTime, outDisplayRetireTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+
+    // Verify timestamps are correct for frame 2.
+    framesAgo = 0;
+    resetTimestamps();
+    result = getAllFrameTimestamps(framesAgo);
+    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(0, outDisplayRetireTime);
+    EXPECT_EQ(0, outDequeueReadyTime);
+    EXPECT_EQ(0, 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();
+    mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+    const uint32_t framesAgo = 0;
+
+    // Dequeue and queue frame 1.
+    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(), framesAgo,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, 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(0, 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(), framesAgo,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, 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.
+    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(), framesAgo,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, 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(0, 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(), framesAgo,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, 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();
+    mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+    // Dequeue and queue frame 1.
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    // Dequeue and queue frame 2.
+    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.
+    const uint32_t framesAgo = 0;
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+            nullptr, 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();
+    mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+    // Dequeue and queue frame 1.
+    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.
+    uint32_t framesAgo = 1;
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = getAllFrameTimestamps(framesAgo);
+    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(0, outGpuCompositionDoneTime);
+    EXPECT_EQ(0, outDisplayPresentTime);
+    EXPECT_EQ(0, outDisplayRetireTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(0, 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.
+    framesAgo = 1;
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(framesAgo);
+    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(0, outGpuCompositionDoneTime);
+    EXPECT_EQ(0, outDisplayPresentTime);
+    EXPECT_EQ(0, outDisplayRetireTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(0, outReleaseTime);
+
+    // Signal the fences for frame 1.
+    mFrames[0].signalRefreshFences();
+    mFrames[0].signalReleaseFences();
+
+    // Verify all timestamps are available without a sync call.
+    framesAgo = 1;
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(framesAgo);
+    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].kRetireTime, outDisplayRetireTime);
+    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();
+    mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+    const uint32_t framesAgo = 1;
+
+    // Dequeue and queue frame 1.
+    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(framesAgo);
+    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(0, outGpuCompositionDoneTime);
+    EXPECT_EQ(0, outDisplayPresentTime);
+    EXPECT_EQ(0, outDisplayRetireTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(0, 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(framesAgo);
+    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(0, outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime);
+    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, NoRetireOrReleaseNoSync) {
+    enableFrameTimestamps();
+    mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
+
+    // Dequeue and queue frame 1.
+    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.
+    uint32_t framesAgo = 1;
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = getAllFrameTimestamps(framesAgo);
+    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(0, outGpuCompositionDoneTime);
+    EXPECT_EQ(0, outDisplayPresentTime);
+    EXPECT_EQ(0, outDisplayRetireTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(0, 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, retire, 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.
+    framesAgo = 0;
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(framesAgo);
+    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(0, outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(0, outDisplayRetireTime);
+    EXPECT_EQ(0, outDequeueReadyTime);
+    EXPECT_EQ(0, outReleaseTime);
+}
+
+// This test verifies there are no sync calls for present or retire times
+// when they aren't supported and that an error is returned.
+void GetFrameTimestampsTest::PresentOrRetireUnsupportedNoSyncTest(
+        bool displayPresentSupported, bool displayRetireSupported) {
+
+    enableFrameTimestamps();
+    mSurface->mFakeSurfaceComposer->setSupportedTimestamps(
+        displayPresentSupported, displayRetireSupported);
+
+    // Dequeue and queue frame 1.
+    dequeueAndQueue(0);
+
+    // Verify a query for the Present and Retire times do not trigger
+    // a sync call if they are not supported.
+    const uint32_t framesAgo = 0;
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+            nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+            displayPresentSupported ? nullptr : &outDisplayPresentTime,
+            displayRetireSupported ? nullptr : &outDisplayRetireTime,
+            nullptr, nullptr);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(BAD_VALUE, result);
+    EXPECT_EQ(-1, outDisplayRetireTime);
+    EXPECT_EQ(-1, outDisplayPresentTime);
+}
+
+TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) {
+   PresentOrRetireUnsupportedNoSyncTest(false, true);
+}
+
+TEST_F(GetFrameTimestampsTest, RetireUnsupportedNoSync) {
+   PresentOrRetireUnsupportedNoSyncTest(true, false);
+}
+
 }
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d5ff753..4cd0d3a 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,10 +44,14 @@
     },
 
     srcs: [
+        "ColorSpace.cpp",
         "Fence.cpp",
+        "FenceTime.cpp",
         "FrameStats.cpp",
         "Gralloc1.cpp",
         "Gralloc1On0Adapter.cpp",
+        "GrallocAllocator.cpp",
+        "GrallocMapper.cpp",
         "GraphicBuffer.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicBufferMapper.cpp",
@@ -56,9 +63,13 @@
     ],
 
     shared_libs: [
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.mapper@2.0",
         "libbinder",
         "libcutils",
         "libhardware",
+        "libhidlbase",
+        "libhidltransport",
         "libsync",
         "libutils",
         "liblog",
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
new file mode 100644
index 0000000..6a86bb5
--- /dev/null
+++ b/libs/ui/ColorSpace.cpp
@@ -0,0 +1,315 @@
+/*
+ * 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 {
+
+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)) {
+
+    float3 r(rgbToXYZ * float3{1, 0, 0});
+    float3 g(rgbToXYZ * float3{0, 1, 0});
+    float3 b(rgbToXYZ * float3{0, 0, 1});
+
+    mPrimaries[0] = r.xy / dot(r, float3{1});
+    mPrimaries[1] = g.xy / dot(g, float3{1});
+    mPrimaries[2] = b.xy / dot(b, float3{1});
+
+    float3 w(rgbToXYZ * float3{1});
+    mWhitePoint = w.xy / dot(w, float3{1});
+}
+
+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) {
+}
+
+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)}
+    };
+}
+
+static constexpr float rcpResponse(float x, float g,float a, float b, float c, float d) {
+    return x >= d * c ? (std::pow(x, 1.0f / g) - b) / a : x / c;
+}
+
+static constexpr float response(float x, float g, float a, float b, float c, float d) {
+    return x >= d ? std::pow(a * x + b, g) : c * x;
+}
+
+static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
+    return std::copysign(rcpResponse(std::abs(x), g, a, b, c, d), x);
+}
+
+static float absResponse(float x, float g, float a, float b, float c, float d) {
+    return std::copysign(response(std::abs(x), g, a, b, c, d), x);
+}
+
+static float safePow(float x, float e) {
+    return powf(x < 0.0f ? 0.0f : x, e);
+}
+
+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},
+        std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+        std::bind(response,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f)
+    };
+}
+
+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},
+        linearReponse,
+        linearReponse,
+        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},
+        std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
+        std::bind(response,    _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+    };
+}
+
+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},
+        std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
+        std::bind(response,    _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+    };
+}
+
+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},
+        std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
+        std::bind(response,    _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+    };
+}
+
+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},
+        std::bind(safePow, _1, 1.0f / 2.2f),
+        std::bind(safePow, _1, 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},
+        std::bind(rcpResponse, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f),
+        std::bind(response,    _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f)
+    };
+}
+
+const ColorSpace ColorSpace::DisplayP3() {
+    return {
+        "Display P3",
+        {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
+        {0.3127f, 0.3290f},
+        std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
+        std::bind(response,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f)
+    };
+}
+
+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},
+        std::bind(safePow, _1, 1.0f / 2.6f),
+        std::bind(safePow, _1, 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},
+        linearReponse,
+        linearReponse,
+        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},
+        linearReponse,
+        linearReponse,
+        std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
+    };
+}
+
+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;
+}
+
+ColorSpace::Connector::Connector(
+        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 = XYZ(float3{src.getWhitePoint(), 1});
+        float3 dstXYZ = 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;
+    }
+}
+
+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();
+
+    Connector 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;
+}
+
+
+}; // namespace android
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..8106b16
--- /dev/null
+++ b/libs/ui/FenceTime.cpp
@@ -0,0 +1,365 @@
+/*
+* 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>
+
+#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 == Fence::SIGNAL_TIME_PENDING ?
+            Fence::SIGNAL_TIME_INVALID : signalTime) {
+}
+
+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("FenceTime::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(),
+                    "FenceToFenceTimeMap::signalAllForTest: "
+                     "Signaling invalid fence.");
+            fenceTime->signalForTest(signalTime);
+            signaled = true;
+        }
+    }
+
+    if (!signaled) {
+        ALOGE("FenceToFenceTimeMap::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
index 4c73ce4..367d1ce 100644
--- a/libs/ui/Gralloc1.cpp
+++ b/libs/ui/Gralloc1.cpp
@@ -77,6 +77,17 @@
             mShimDevice.mDevice, mDeviceId, format, &mFormat);
 }
 
+gralloc1_error_t Descriptor::setLayerCount(uint32_t layerCount)
+{
+    if (mShimDevice.hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+        return setHelper<uint32_t>(mShimDevice.mFunctions.setLayerCount.pfn,
+                mShimDevice.mDevice, mDeviceId, layerCount, &mLayerCount);
+    } else {
+        // Layered buffers are not supported on this device.
+        return GRALLOC1_ERROR_UNSUPPORTED;
+    }
+}
+
 gralloc1_error_t Descriptor::setProducerUsage(gralloc1_producer_usage_t usage)
 {
     return setHelper<uint64_t>(mShimDevice.mFunctions.setProducerUsage.pfn,
@@ -366,6 +377,15 @@
         mFunctions.allocate.load(mDevice, false);
     }
 
+    if (hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+        if (!mFunctions.setLayerCount.load(mDevice, true)) {
+            return false;
+        }
+        if (!mFunctions.getLayerCount.load(mDevice, true)) {
+            return false;
+        }
+    }
+
     return true;
 }
 
diff --git a/libs/ui/Gralloc1On0Adapter.cpp b/libs/ui/Gralloc1On0Adapter.cpp
index ec7df31..a0bbe63 100644
--- a/libs/ui/Gralloc1On0Adapter.cpp
+++ b/libs/ui/Gralloc1On0Adapter.cpp
@@ -97,6 +97,8 @@
             return asFP<GRALLOC1_PFN_SET_DIMENSIONS>(setDimensionsHook);
         case GRALLOC1_FUNCTION_SET_FORMAT:
             return asFP<GRALLOC1_PFN_SET_FORMAT>(setFormatHook);
+        case GRALLOC1_FUNCTION_SET_LAYER_COUNT:
+            return asFP<GRALLOC1_PFN_SET_LAYER_COUNT>(setLayerCountHook);
         case GRALLOC1_FUNCTION_SET_PRODUCER_USAGE:
             return asFP<GRALLOC1_PFN_SET_PRODUCER_USAGE>(setProducerUsageHook);
         case GRALLOC1_FUNCTION_GET_BACKING_STORE:
@@ -113,6 +115,10 @@
             return asFP<GRALLOC1_PFN_GET_FORMAT>(
                     bufferHook<decltype(&Buffer::getFormat),
                     &Buffer::getFormat, int32_t*>);
+        case GRALLOC1_FUNCTION_GET_LAYER_COUNT:
+            return asFP<GRALLOC1_PFN_GET_LAYER_COUNT>(
+                    bufferHook<decltype(&Buffer::getLayerCount),
+                    &Buffer::getLayerCount, uint32_t*>);
         case GRALLOC1_FUNCTION_GET_PRODUCER_USAGE:
             return asFP<GRALLOC1_PFN_GET_PRODUCER_USAGE>(getProducerUsageHook);
         case GRALLOC1_FUNCTION_GET_STRIDE:
diff --git a/libs/ui/GrallocAllocator.cpp b/libs/ui/GrallocAllocator.cpp
new file mode 100644
index 0000000..ca67990
--- /dev/null
+++ b/libs/ui/GrallocAllocator.cpp
@@ -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.
+ */
+
+#define LOG_TAG "GrallocAllocator"
+
+#include <log/log.h>
+#include <ui/GrallocAllocator.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+Allocator::Allocator()
+{
+    mAllocator = IAllocator::getService("gralloc");
+    if (mAllocator != nullptr) {
+        mAllocator->createClient(
+                [&](const auto& tmpError, const auto& tmpClient) {
+                    if (tmpError == Error::NONE) {
+                        mClient = tmpClient;
+                    }
+                });
+        if (mClient == nullptr) {
+            mAllocator.clear();
+        }
+    }
+}
+
+std::string Allocator::dumpDebugInfo() const
+{
+    std::string info;
+
+    mAllocator->dumpDebugInfo([&](const auto& tmpInfo) {
+        info = tmpInfo.c_str();
+    });
+
+    return info;
+}
+
+Error Allocator::createBufferDescriptor(
+        const IAllocatorClient::BufferDescriptorInfo& descriptorInfo,
+        BufferDescriptor* outDescriptor) const
+{
+    Error error = kDefaultError;
+    mClient->createDescriptor(descriptorInfo,
+            [&](const auto& tmpError, const auto& tmpDescriptor) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outDescriptor = tmpDescriptor;
+            });
+
+    return error;
+}
+
+void Allocator::destroyBufferDescriptor(BufferDescriptor descriptor) const
+{
+    mClient->destroyDescriptor(descriptor);
+}
+
+Error Allocator::allocate(BufferDescriptor descriptor,
+        Buffer* outBuffer) const
+{
+    hardware::hidl_vec<BufferDescriptor> descriptors;
+    descriptors.setToExternal(&descriptor, 1);
+
+    Error error = kDefaultError;
+    auto status = mClient->allocate(descriptors,
+            [&](const auto& tmpError, const auto& tmpBuffers) {
+                error = tmpError;
+                if (tmpError != Error::NONE) {
+                    return;
+                }
+
+                *outBuffer = tmpBuffers[0];
+            });
+
+    return error;
+}
+
+void Allocator::free(Buffer buffer) const
+{
+    mClient->free(buffer);
+}
+
+Error Allocator::exportHandle(BufferDescriptor descriptor, Buffer buffer,
+        native_handle_t** outBufferHandle) const
+{
+    Error error = kDefaultError;
+    auto status = mClient->exportHandle(descriptor, buffer,
+            [&](const auto& tmpError, const auto& tmpBufferHandle) {
+                error = tmpError;
+                if (tmpError != Error::NONE) {
+                    return;
+                }
+
+                *outBufferHandle = native_handle_clone(tmpBufferHandle);
+                if (!*outBufferHandle) {
+                    error = Error::NO_RESOURCES;
+                }
+            });
+
+    return error;
+}
+
+} // namespace Gralloc2
+
+} // namespace android
diff --git a/libs/ui/GrallocMapper.cpp b/libs/ui/GrallocMapper.cpp
new file mode 100644
index 0000000..7ee01ad
--- /dev/null
+++ b/libs/ui/GrallocMapper.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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 "GrallocMapper"
+
+#include <array>
+#include <string>
+
+#include <log/log.h>
+#include <ui/GrallocMapper.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+static constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+Mapper::Mapper()
+{
+    mMapper = IMapper::getService("gralloc-mapper");
+    if (mMapper != nullptr && mMapper->isRemote()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+    }
+}
+
+Error Mapper::retain(buffer_handle_t handle) const
+{
+    auto ret = mMapper->retain(handle);
+    return (ret.isOk()) ? static_cast<Error>(ret) : kDefaultError;
+}
+
+void Mapper::release(buffer_handle_t handle) const
+{
+    auto ret = mMapper->release(handle);
+
+    auto error = (ret.isOk()) ? static_cast<Error>(ret) : kDefaultError;
+    ALOGE_IF(error != Error::NONE,
+            "release(%p) failed with %d", handle, error);
+}
+
+Error Mapper::getStride(buffer_handle_t handle, uint32_t* outStride) const
+{
+    Error error = kDefaultError;
+    mMapper->getStride(handle,
+            [&](const auto& tmpError, const auto& tmpStride)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outStride = tmpStride;
+            });
+
+    return error;
+}
+
+Error Mapper::lock(buffer_handle_t handle,
+        uint64_t producerUsageMask,
+        uint64_t consumerUsageMask,
+        const IMapper::Rect& accessRegion,
+        int acquireFence, void** outData) const
+{
+    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 = kDefaultError;
+    mMapper->lock(handle, producerUsageMask, consumerUsageMask,
+            accessRegion, acquireFenceHandle,
+            [&](const auto& tmpError, const auto& tmpData)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outData = tmpData;
+            });
+
+    if (error == Error::NONE && acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    return error;
+}
+
+Error Mapper::lock(buffer_handle_t handle,
+        uint64_t producerUsageMask,
+        uint64_t consumerUsageMask,
+        const IMapper::Rect& accessRegion,
+        int acquireFence, FlexLayout* outLayout) const
+{
+    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 = kDefaultError;
+    mMapper->lockFlex(handle, producerUsageMask, consumerUsageMask,
+            accessRegion, acquireFenceHandle,
+            [&](const auto& tmpError, const auto& tmpLayout)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outLayout = tmpLayout;
+            });
+
+    if (error == Error::NONE && acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    return error;
+}
+
+int Mapper::unlock(buffer_handle_t handle) const
+{
+    int releaseFence = -1;
+
+    Error error = kDefaultError;
+    mMapper->unlock(handle,
+            [&](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 {
+                        error = Error::NO_RESOURCES;
+                    }
+                } else {
+                    releaseFence = -1;
+                }
+            });
+
+    if (error != Error::NONE) {
+        ALOGE("unlock(%p) failed with %d", handle, error);
+        releaseFence = -1;
+    }
+
+    return releaseFence;
+}
+
+} // namespace Gralloc2
+
+} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 97b948d..07164a4 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -23,6 +23,7 @@
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
+#include <ui/GrallocMapper.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
@@ -49,6 +50,7 @@
     height =
     stride =
     format =
+    layerCount =
     usage  = 0;
     handle = NULL;
 }
@@ -62,15 +64,33 @@
     height =
     stride =
     format =
+    layerCount =
     usage  = 0;
     handle = NULL;
-    mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage,
+    mInitCheck = initSize(inWidth, inHeight, inFormat, 1, 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)
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+        std::string requestorName)
+    : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
+      mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
+{
+    width  =
+    height =
+    stride =
+    format =
+    layerCount =
+    usage  = 0;
+    handle = NULL;
+    mInitCheck = initSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+            std::move(requestorName));
+}
+
+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)
     : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
       mBufferMapper(GraphicBufferMapper::get()),
       mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
@@ -79,6 +99,7 @@
     height = static_cast<int>(inHeight);
     stride = static_cast<int>(inStride);
     format = inFormat;
+    layerCount = inLayerCount;
     usage  = static_cast<int>(inUsage);
     handle = inHandle;
 }
@@ -93,6 +114,7 @@
     height = buffer->height;
     stride = buffer->stride;
     format = buffer->format;
+    layerCount = buffer->layerCount;
     usage  = buffer->usage;
     handle = buffer->handle;
 }
@@ -108,8 +130,10 @@
 {
     if (mOwner == ownHandle) {
         mBufferMapper.unregisterBuffer(handle);
-        native_handle_close(handle);
-        native_handle_delete(const_cast<native_handle*>(handle));
+        if (!mBufferMapper.getGrallocMapper().valid()) {
+            native_handle_close(handle);
+            native_handle_delete(const_cast<native_handle*>(handle));
+        }
     } else if (mOwner == ownData) {
         GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
         allocator.free(handle);
@@ -135,7 +159,7 @@
 }
 
 status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage)
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage)
 {
     if (mOwner != ownData)
         return INVALID_OPERATION;
@@ -144,6 +168,7 @@
             static_cast<int>(inWidth) == width &&
             static_cast<int>(inHeight) == height &&
             inFormat == format &&
+            inLayerCount == layerCount &&
             static_cast<int>(inUsage) == usage)
         return NO_ERROR;
 
@@ -152,30 +177,34 @@
         allocator.free(handle);
         handle = 0;
     }
-    return initSize(inWidth, inHeight, inFormat, inUsage, "[Reallocation]");
+    return initSize(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, uint32_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)
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_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);
     }
@@ -281,7 +310,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 +330,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 +359,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,34 +396,35 @@
         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);
         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);
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 75dd8df..07ad4c1 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -25,6 +25,9 @@
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/Gralloc1On0Adapter.h>
+#include <ui/GrallocAllocator.h>
+#include <ui/GrallocMapper.h>
+#include <ui/GraphicBufferMapper.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -36,8 +39,14 @@
     GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
 
 GraphicBufferAllocator::GraphicBufferAllocator()
-  : mLoader(std::make_unique<Gralloc1::Loader>()),
-    mDevice(mLoader->getDevice()) {}
+  : mAllocator(std::make_unique<Gralloc2::Allocator>()),
+    mMapper(GraphicBufferMapper::getInstance())
+{
+    if (!mAllocator->valid()) {
+        mLoader = std::make_unique<Gralloc1::Loader>();
+        mDevice = mLoader->getDevice();
+    }
+}
 
 GraphicBufferAllocator::~GraphicBufferAllocator() {}
 
@@ -54,22 +63,29 @@
     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%08x | %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%08x | %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;
+    if (mAllocator->valid()) {
+        deviceDump = mAllocator->dumpDebugInfo();
+    } else {
+        deviceDump = mDevice->dump();
+    }
+
     result.append(deviceDump.c_str(), deviceDump.size());
 }
 
@@ -80,9 +96,108 @@
     ALOGD("%s", s.string());
 }
 
+namespace {
+
+class HalBuffer {
+public:
+    HalBuffer(const Gralloc2::Allocator* allocator,
+            uint32_t width, uint32_t height,
+            PixelFormat format, uint32_t layerCount, uint32_t usage)
+        : mAllocator(allocator), mBufferValid(false)
+    {
+        Gralloc2::IAllocatorClient::BufferDescriptorInfo info = {};
+        info.width = width;
+        info.height = height;
+        info.format = static_cast<Gralloc2::PixelFormat>(format);
+        info.layerCount = layerCount;
+        info.producerUsageMask = usage;
+        info.consumerUsageMask = usage;
+
+        Gralloc2::BufferDescriptor descriptor;
+        auto error = mAllocator->createBufferDescriptor(info, &descriptor);
+        if (error != Gralloc2::Error::NONE) {
+            ALOGE("Failed to create desc (%u x %u) layerCount %u format %d usage %u: %d",
+                    width, height, layerCount, format, usage, error);
+            return;
+        }
+
+        error = mAllocator->allocate(descriptor, &mBuffer);
+        if (error == Gralloc2::Error::NOT_SHARED) {
+            error = Gralloc2::Error::NONE;
+        }
+
+        if (error != Gralloc2::Error::NONE) {
+            ALOGE("Failed to allocate (%u x %u) layerCount %u format %d usage %u: %d",
+                    width, height, layerCount, format, usage, error);
+            mAllocator->destroyBufferDescriptor(descriptor);
+            return;
+        }
+
+        error = mAllocator->exportHandle(descriptor, mBuffer, &mHandle);
+        if (error != Gralloc2::Error::NONE) {
+            ALOGE("Failed to export handle");
+            mAllocator->free(mBuffer);
+            mAllocator->destroyBufferDescriptor(descriptor);
+            return;
+        }
+
+        mAllocator->destroyBufferDescriptor(descriptor);
+
+        mBufferValid = true;
+    }
+
+    ~HalBuffer()
+    {
+        if (mBufferValid) {
+            if (mHandle) {
+                native_handle_close(mHandle);
+                native_handle_delete(mHandle);
+            }
+
+            mAllocator->free(mBuffer);
+        }
+    }
+
+    bool exportHandle(GraphicBufferMapper& mapper,
+            buffer_handle_t* handle, uint32_t* stride)
+    {
+        if (!mBufferValid) {
+            return false;
+        }
+
+        if (mapper.registerBuffer(mHandle)) {
+            return false;
+        }
+
+        *handle = mHandle;
+
+        auto error = mapper.getGrallocMapper().getStride(mHandle, stride);
+        if (error != Gralloc2::Error::NONE) {
+            ALOGW("Failed to get stride from buffer: %d", error);
+            *stride = 0;
+        }
+
+        mHandle = nullptr;
+        mAllocator->free(mBuffer);
+        mBufferValid = false;
+
+        return true;
+    }
+
+private:
+    const Gralloc2::Allocator* mAllocator;
+
+    bool mBufferValid;
+    Gralloc2::Buffer mBuffer;
+    native_handle_t* mHandle;
+};
+
+} // namespace
+
 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, uint32_t usage,
+        buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
+        std::string requestorName)
 {
     ATRACE_CALL();
 
@@ -91,43 +206,70 @@
     if (!width || !height)
         width = height = 1;
 
+    // Ensure that layerCount is valid.
+    if (layerCount < 1)
+        layerCount = 1;
+
     // Filter out any usage bits that should not be passed to the gralloc module
     usage &= GRALLOC_USAGE_ALLOC_MASK;
 
-    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;
-    }
+    gralloc1_error_t error;
+    if (mAllocator->valid()) {
+        HalBuffer buffer(mAllocator.get(), width, height, format, layerCount,
+                usage);
+        if (!buffer.exportHandle(mMapper, handle, stride)) {
+            return NO_MEMORY;
+        }
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        auto descriptor = mDevice->createDescriptor();
+        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;
+        }
+        if (mDevice->hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+            error = descriptor->setLayerCount(layerCount);
+            if (error != GRALLOC1_ERROR_NONE) {
+                ALOGE("Failed to set layer count to %u: %d", layerCount, error);
+                return BAD_VALUE;
+            }
+        } else if (layerCount > 1) {
+            ALOGE("Failed to set layer count to %u: capability unsupported",
+                    layerCount);
+            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;
+        }
 
-    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->allocate(descriptor, graphicBufferId, handle);
+        if (error != GRALLOC1_ERROR_NONE) {
+            ALOGE("Failed to allocate (%u x %u) layerCount %u format %d usage %u: %d",
+                    width, height, layerCount, 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);
+        error = mDevice->getStride(*handle, stride);
+        if (error != GRALLOC1_ERROR_NONE) {
+            ALOGW("Failed to get stride from buffer: %d", error);
+        }
     }
 
     if (error == NO_ERROR) {
@@ -139,6 +281,7 @@
         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);
@@ -152,7 +295,14 @@
 {
     ATRACE_CALL();
 
-    auto error = mDevice->release(handle);
+    gralloc1_error_t error;
+    if (mAllocator->valid()) {
+        error = static_cast<gralloc1_error_t>(
+                mMapper.unregisterBuffer(handle));
+    } else {
+        error = mDevice->release(handle);
+    }
+
     if (error != GRALLOC1_ERROR_NONE) {
         ALOGE("Failed to free buffer: %d", error);
     }
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 481d43c..1ff934b 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -33,6 +33,7 @@
 #include <utils/Trace.h>
 
 #include <ui/Gralloc1On0Adapter.h>
+#include <ui/GrallocMapper.h>
 #include <ui/GraphicBufferMapper.h>
 #include <ui/Rect.h>
 
@@ -44,8 +45,13 @@
 ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
 
 GraphicBufferMapper::GraphicBufferMapper()
-  : mLoader(std::make_unique<Gralloc1::Loader>()),
-    mDevice(mLoader->getDevice()) {}
+  : mMapper(std::make_unique<const Gralloc2::Mapper>())
+{
+    if (!mMapper->valid()) {
+        mLoader = std::make_unique<Gralloc1::Loader>();
+        mDevice = mLoader->getDevice();
+    }
+}
 
 
 
@@ -53,7 +59,13 @@
 {
     ATRACE_CALL();
 
-    gralloc1_error_t error = mDevice->retain(handle);
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        error = static_cast<gralloc1_error_t>(mMapper->retain(handle));
+    } else {
+        error = mDevice->retain(handle);
+    }
+
     ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
             handle, error);
 
@@ -64,7 +76,14 @@
 {
     ATRACE_CALL();
 
-    gralloc1_error_t error = mDevice->retain(buffer);
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        error = static_cast<gralloc1_error_t>(
+                mMapper->retain(buffer->getNativeBuffer()->handle));
+    } else {
+        error = mDevice->retain(buffer);
+    }
+
     ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
             buffer->getNativeBuffer()->handle, error);
 
@@ -75,7 +94,14 @@
 {
     ATRACE_CALL();
 
-    gralloc1_error_t error = mDevice->release(handle);
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        mMapper->release(handle);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        error = mDevice->release(handle);
+    }
+
     ALOGW_IF(error != GRALLOC1_ERROR_NONE, "unregisterBuffer(%p): failed %d",
             handle, error);
 
@@ -120,11 +146,20 @@
     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);
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        const Gralloc2::IMapper::Rect& accessRect =
+            *reinterpret_cast<Gralloc2::IMapper::Rect*>(&accessRegion);
+        error = static_cast<gralloc1_error_t>(mMapper->lock(
+                    handle, usage, usage, accessRect, fenceFd, vaddr));
+    } else {
+        sp<Fence> fence = new Fence(fenceFd);
+        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);
 
@@ -160,38 +195,63 @@
     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,
+    std::vector<android_flex_plane_t> planes;
+    android_flex_layout_t flexLayout{};
+    gralloc1_error_t error;
+
+    if (mMapper->valid()) {
+        const Gralloc2::IMapper::Rect& accessRect =
+            *reinterpret_cast<Gralloc2::IMapper::Rect*>(&accessRegion);
+        Gralloc2::FlexLayout layout{};
+        error = static_cast<gralloc1_error_t>(mMapper->lock(
+                    handle, usage, usage, accessRect, fenceFd, &layout));
+
+        if (error == GRALLOC1_ERROR_NONE) {
+            planes.resize(layout.planes.size());
+            memcpy(planes.data(), layout.planes.data(),
+                    sizeof(planes[0]) * planes.size());
+
+            flexLayout.format = static_cast<android_flex_format_t>(
+                    layout.format);
+            flexLayout.num_planes = static_cast<uint32_t>(planes.size());
+            flexLayout.planes = planes.data();
+        }
+    } else {
+        sp<Fence> fence = new Fence(fenceFd);
+
+        if (mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
+            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;
+        }
+
+        uint32_t numPlanes = 0;
+        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;
+        }
+
+        planes.resize(numPlanes);
+        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, ycbcr, fence);
-        ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lockYCbCr(%p, ...) failed: %d",
-                handle, error);
-        return error;
+                &accessRegion, &flexLayout, fence);
     }
 
-    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;
@@ -276,14 +336,20 @@
 {
     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;
-    }
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        *fenceFd = mMapper->unlock(handle);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        sp<Fence> fence = Fence::NO_FENCE;
+        error = mDevice->unlock(handle, &fence);
+        if (error != GRALLOC1_ERROR_NONE) {
+            ALOGE("unlock(%p) failed: %d", handle, error);
+            return error;
+        }
 
-    *fenceFd = fence->dup();
+        *fenceFd = fence->dup();
+    }
     return error;
 }
 
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index cab1dde..734472d 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -22,6 +22,8 @@
 
 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:
@@ -38,6 +40,8 @@
 
 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:
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 8cdab8c..c4f34d5 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,3 +29,19 @@
     name: "mat_test",
     srcs: ["mat_test.cpp"],
 }
+
+cc_test {
+    name: "half_test",
+    srcs: ["half_test.cpp"],
+}
+
+cc_test {
+    name: "quat_test",
+    srcs: ["quat_test.cpp"],
+}
+
+cc_test {
+    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..1e359d3
--- /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 = ColorSpace::connect(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 = ColorSpace::connect(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/half_test.cpp b/libs/ui/tests/half_test.cpp
new file mode 100644
index 0000000..b2a5e5c
--- /dev/null
+++ b/libs/ui/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 <ui/half.h>
+#include <ui/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/ui/tests/mat_test.cpp b/libs/ui/tests/mat_test.cpp
index a2c63ac..0f8e631 100644
--- a/libs/ui/tests/mat_test.cpp
+++ b/libs/ui/tests/mat_test.cpp
@@ -14,13 +14,17 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "RegionTest"
+#define LOG_TAG "MatTest"
 
 #include <stdlib.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
+
+#include <limits>
+#include <random>
+#include <functional>
+
 #include <gtest/gtest.h>
 
+#include <ui/mat2.h>
 #include <ui/mat4.h>
 
 namespace android {
@@ -99,11 +103,13 @@
     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 = -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 = -m0;
     EXPECT_EQ(identity, m0);
 }
 
@@ -112,19 +118,19 @@
     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));
+    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(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 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(3,  m3i[0][1]);
+    EXPECT_FLOAT_EQ(3,  m3i[1][0]);
     EXPECT_FLOAT_EQ(-4, m3i[1][1]);
 
     mat4 m3ii(inverse(m3i));
@@ -134,6 +140,553 @@
     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/ui/tests/quat_test.cpp b/libs/ui/tests/quat_test.cpp
new file mode 100644
index 0000000..f5cb659
--- /dev/null
+++ b/libs/ui/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 <ui/quat.h>
+#include <ui/mat4.h>
+#include <ui/vec3.h>
+#include <ui/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/ui/tests/vec_test.cpp b/libs/ui/tests/vec_test.cpp
index 454c999..7c749a7 100644
--- a/libs/ui/tests/vec_test.cpp
+++ b/libs/ui/tests/vec_test.cpp
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "RegionTest"
+#define LOG_TAG "VecTest"
 
 #include <math.h>
 #include <stdlib.h>
 
-#include <ui/Region.h>
-#include <ui/Rect.h>
 #include <ui/vec4.h>
 
 #include <gtest/gtest.h>
@@ -37,7 +35,7 @@
     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);
+    EXPECT_EQ(reinterpret_cast<void*>(&v3), reinterpret_cast<void*>(&v4));
 }
 
 TEST_F(VecTest, Constructors) {
@@ -53,7 +51,7 @@
     EXPECT_EQ(v1.z, 1);
     EXPECT_EQ(v1.w, 1);
 
-    vec4 v2(1,2,3,4);
+    vec4 v2(1, 2, 3, 4);
     EXPECT_EQ(v2.x, 1);
     EXPECT_EQ(v2.y, 2);
     EXPECT_EQ(v2.z, 3);
@@ -77,15 +75,16 @@
     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);
+    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);
+    vec4 v0(1, 2, 3, 4);
+
     v0.x = 10;
     v0.y = 20;
     v0.z = 30;
@@ -104,7 +103,7 @@
     EXPECT_EQ(v0.z, 300);
     EXPECT_EQ(v0.w, 400);
 
-    v0.xyz = vec3(1,2,3);
+    v0.xyz = vec3(1, 2, 3);
     EXPECT_EQ(v0.x, 1);
     EXPECT_EQ(v0.y, 2);
     EXPECT_EQ(v0.z, 3);
@@ -112,7 +111,7 @@
 }
 
 TEST_F(VecTest, UnaryOps) {
-    vec4 v0(1,2,3,4);
+    vec4 v0(1, 2, 3, 4);
 
     v0 += 1;
     EXPECT_EQ(v0.x, 2);
@@ -164,41 +163,23 @@
     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);
+    EXPECT_EQ(v1.x, -10);
+    EXPECT_EQ(v1.y, -20);
+    EXPECT_EQ(v1.z, -30);
+    EXPECT_EQ(v1.w, -40);
 
-    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);
+    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);
+    vec4 v0(1, 2, 3, 4);
+    vec4 v1(10, 20, 30, 40);
 
     EXPECT_TRUE(v0 == v0);
     EXPECT_TRUE(v0 != v1);
@@ -206,9 +187,26 @@
     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 v0(1, 2, 3, 4);
+    vec4 v1(10, 20, 30, 40);
 
     vec4 v2(v0 + v1);
     EXPECT_EQ(v2.x, 11);
@@ -228,8 +226,8 @@
     EXPECT_EQ(v0.z, 60);
     EXPECT_EQ(v0.w, 80);
 
-    tvec4<double> vd(2);
-    v0 = v1 * vd;
+    float4 vf(2);
+    v0 = v1 * vf;
     EXPECT_EQ(v0.x, 20);
     EXPECT_EQ(v0.y, 40);
     EXPECT_EQ(v0.z, 60);
@@ -239,19 +237,34 @@
 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));
+    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 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);
+    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/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/libbufferhub/Android.mk b/libs/vr/libbufferhub/Android.mk
new file mode 100644
index 0000000..467f69f
--- /dev/null
+++ b/libs/vr/libbufferhub/Android.mk
@@ -0,0 +1,57 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	buffer_hub_client.cpp \
+	buffer_hub_rpc.cpp \
+	ion_buffer.cpp
+
+includeFiles := \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	libchrome \
+	libdvrcommon \
+	libpdx_default_transport \
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	libhardware \
+	liblog \
+	libui \
+	libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libbufferhub\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libbufferhub
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := bufferhub_tests.cpp
+LOCAL_STATIC_LIBRARIES := libbufferhub $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := bufferhub_tests
+include $(BUILD_NATIVE_TEST)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
new file mode 100644
index 0000000..146780e
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -0,0 +1,417 @@
+#include <private/dvr/buffer_hub_client.h>
+
+#include <cutils/log.h>
+#include <poll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/platform_defines.h>
+
+#include "include/private/dvr/bufferhub_rpc.h"
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::WrapBuffer;
+using android::pdx::Status;
+
+namespace {
+
+constexpr int kUncachedBlobUsageFlags = GRALLOC_USAGE_SW_READ_RARELY |
+                                        GRALLOC_USAGE_SW_WRITE_RARELY |
+                                        GRALLOC_USAGE_PRIVATE_UNCACHED;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      id_(-1) {}
+BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+          endpoint_path)},
+      id_(-1) {}
+
+BufferHubBuffer::~BufferHubBuffer() {}
+
+Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() {
+  Status<LocalChannelHandle> status =
+      InvokeRemoteMethod<BufferHubRPC::NewConsumer>();
+  ALOGE_IF(!status,
+           "BufferHub::CreateConsumer: Failed to create consumer channel: %s",
+           status.GetErrorMessage().c_str());
+  return status;
+}
+
+int BufferHubBuffer::ImportBuffer() {
+  ATRACE_NAME("BufferHubBuffer::ImportBuffer");
+  if (!IonBuffer::GetGrallocModule())
+    return -EIO;
+
+  Status<std::vector<NativeBufferHandle<LocalHandle>>> status =
+      InvokeRemoteMethod<BufferHubRPC::GetBuffers>();
+  if (!status) {
+    ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffers: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  } else if (status.get().empty()) {
+    ALOGE(
+        "BufferHubBuffer::ImportBuffer: Expected to receive at least one "
+        "buffer handle but got zero!");
+    return -EIO;
+  }
+
+  auto buffer_handles = status.take();
+
+  // Stash the buffer id to replace the value in id_. All sub-buffers of a
+  // buffer hub buffer have the same id.
+  const int new_id = buffer_handles[0].id();
+
+  // Import all of the buffers.
+  std::vector<IonBuffer> ion_buffers;
+  for (auto& handle : buffer_handles) {
+    const size_t i = &handle - buffer_handles.data();
+    ALOGD_IF(
+        TRACE,
+        "BufferHubBuffer::ImportBuffer: i=%zu id=%d FdCount=%zu IntCount=%zu",
+        i, handle.id(), handle.FdCount(), handle.IntCount());
+
+    IonBuffer buffer;
+    const int ret = handle.Import(&buffer);
+    if (ret < 0)
+      return ret;
+
+    ion_buffers.emplace_back(std::move(buffer));
+  }
+
+  // If all imports succeed, replace the previous buffers and id.
+  slices_ = std::move(ion_buffers);
+  id_ = new_id;
+  return 0;
+}
+
+int BufferHubBuffer::Poll(int timeout_ms) {
+  ATRACE_NAME("BufferHubBuffer::Poll");
+  pollfd p = {event_fd(), POLLIN, 0};
+  return poll(&p, 1, timeout_ms);
+}
+
+int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height,
+                          void** address, size_t index) {
+  return slices_[index].Lock(usage, x, y, width, height, address);
+}
+
+int BufferHubBuffer::Unlock(size_t index) { return slices_[index].Unlock(); }
+
+int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) {
+  int width = static_cast<int>(size);
+  int height = 1;
+  constexpr int usage = GRALLOC_USAGE_SW_READ_RARELY |
+                        GRALLOC_USAGE_SW_WRITE_RARELY |
+                        GRALLOC_USAGE_PRIVATE_UNCACHED;
+  int ret = Lock(usage, 0, 0, width, height, addr);
+  if (ret == 0)
+    Unlock();
+  return ret;
+}
+
+int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) {
+  int width = static_cast<int>(size);
+  int height = 1;
+  constexpr int usage =
+      GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_PRIVATE_UNCACHED;
+  int ret = Lock(usage, 0, 0, width, height, addr);
+  if (ret == 0)
+    Unlock();
+  return ret;
+}
+
+BufferConsumer::BufferConsumer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s",
+          strerror(-ret));
+    Close(ret);
+  }
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+    LocalChannelHandle channel) {
+  ATRACE_NAME("BufferConsumer::Import");
+  ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value());
+  return BufferConsumer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferConsumer> BufferConsumer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence) {
+  return Acquire(ready_fence, nullptr, 0);
+}
+
+int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta,
+                            size_t meta_size_bytes) {
+  ATRACE_NAME("BufferConsumer::Acquire");
+  LocalFence fence;
+  auto return_value =
+      std::make_pair(std::ref(fence), WrapBuffer(meta, meta_size_bytes));
+  auto status = InvokeRemoteMethodInPlace<BufferHubRPC::ConsumerAcquire>(
+      &return_value, meta_size_bytes);
+  if (status && ready_fence)
+    *ready_fence = fence.take();
+  return status ? 0 : -status.error();
+}
+
+int BufferConsumer::Release(const LocalHandle& release_fence) {
+  ATRACE_NAME("BufferConsumer::Release");
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>(
+      BorrowedFence(release_fence.Borrow())));
+}
+
+int BufferConsumer::ReleaseAsync() {
+  ATRACE_NAME("BufferConsumer::ReleaseAsync");
+  return ReturnStatusOrError(
+      SendImpulse(BufferHubRPC::ConsumerRelease::Opcode));
+}
+
+int BufferConsumer::Discard() { return Release(LocalHandle()); }
+
+int BufferConsumer::SetIgnore(bool ignore) {
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore));
+}
+
+BufferProducer::BufferProducer(int width, int height, int format, int usage,
+                               size_t metadata_size, size_t slice_count)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: fd=%d width=%d height=%d format=%d "
+           "usage=%d, metadata_size=%zu, slice_count=%zu",
+           event_fd(), width, height, format, usage, metadata_size,
+           slice_count);
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, usage, metadata_size, slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create producer buffer: %s",
+        status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, int width, int height, int format,
+                               int usage, size_t meta_size_bytes,
+                               size_t slice_count)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: fd=%d name=%s user_id=%d "
+           "group_id=%d width=%d height=%d format=%d usage=%d, "
+           "meta_size_bytes=%zu, slice_count=%zu",
+           event_fd(), name.c_str(), user_id, group_id, width, height, format,
+           usage, meta_size_bytes, slice_count);
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+      name, user_id, group_id, width, height, format, usage, meta_size_bytes,
+      slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create/get persistent "
+        "buffer \"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(int usage, size_t size)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%d size=%zu", usage,
+           size);
+  const int width = static_cast<int>(size);
+  const int height = 1;
+  const int format = HAL_PIXEL_FORMAT_BLOB;
+  const size_t meta_size_bytes = 0;
+  const size_t slice_count = 1;
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>(
+      width, height, format, usage, meta_size_bytes, slice_count);
+  if (!status) {
+    ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s",
+          status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name, int user_id,
+                               int group_id, int usage, size_t size)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE,
+           "BufferProducer::BufferProducer: name=%s user_id=%d group=%d "
+           "usage=%d size=%zu",
+           name.c_str(), user_id, group_id, usage, size);
+  const int width = static_cast<int>(size);
+  const int height = 1;
+  const int format = HAL_PIXEL_FORMAT_BLOB;
+  const size_t meta_size_bytes = 0;
+  const size_t slice_count = 1;
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+      name, user_id, group_id, width, height, format, usage, meta_size_bytes,
+      slice_count);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to create persistent "
+        "buffer \"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(const std::string& name)
+    : BASE(BufferHubRPC::kClientPath) {
+  ATRACE_NAME("BufferProducer::BufferProducer");
+  ALOGD_IF(TRACE, "BufferProducer::BufferProducer: name=%s", name.c_str());
+
+  auto status = InvokeRemoteMethod<BufferHubRPC::GetPersistentBuffer>(name);
+  if (!status) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to get producer buffer by name "
+        "\"%s\": %s",
+        name.c_str(), status.GetErrorMessage().c_str());
+    Close(-status.error());
+    return;
+  }
+
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer "
+        "\"%s\": %s",
+        name.c_str(), strerror(-ret));
+    Close(ret);
+  }
+}
+
+BufferProducer::BufferProducer(LocalChannelHandle channel)
+    : BASE(std::move(channel)) {
+  const int ret = ImportBuffer();
+  if (ret < 0) {
+    ALOGE(
+        "BufferProducer::BufferProducer: Failed to import producer buffer: %s",
+        strerror(-ret));
+    Close(ret);
+  }
+}
+
+int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta,
+                         size_t meta_size_bytes) {
+  ATRACE_NAME("BufferProducer::Post");
+  return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>(
+      BorrowedFence(ready_fence.Borrow()), WrapBuffer(meta, meta_size_bytes)));
+}
+
+int BufferProducer::Gain(LocalHandle* release_fence) {
+  ATRACE_NAME("BufferProducer::Gain");
+  auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>();
+  if (!status)
+    return -status.error();
+  if (release_fence)
+    *release_fence = status.take().take();
+  return 0;
+}
+
+int BufferProducer::GainAsync() {
+  ATRACE_NAME("BufferProducer::GainAsync");
+  return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+    LocalChannelHandle channel) {
+  ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value());
+  return BufferProducer::Create(std::move(channel));
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::Import(
+    Status<LocalChannelHandle> status) {
+  return Import(status ? status.take()
+                       : LocalChannelHandle{nullptr, -status.error()});
+}
+
+int BufferProducer::MakePersistent(const std::string& name, int user_id,
+                                   int group_id) {
+  ATRACE_NAME("BufferProducer::MakePersistent");
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ProducerMakePersistent>(name, user_id,
+                                                               group_id));
+}
+
+int BufferProducer::RemovePersistence() {
+  ATRACE_NAME("BufferProducer::RemovePersistence");
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<BufferHubRPC::ProducerRemovePersistence>());
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreateUncachedBlob(
+    size_t size) {
+  return BufferProducer::Create(kUncachedBlobUsageFlags, size);
+}
+
+std::unique_ptr<BufferProducer> BufferProducer::CreatePersistentUncachedBlob(
+    const std::string& name, int user_id, int group_id, size_t size) {
+  return BufferProducer::Create(name, user_id, group_id,
+                                kUncachedBlobUsageFlags, size);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/buffer_hub_rpc.cpp b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
new file mode 100644
index 0000000..9a67faa
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_rpc.cpp
@@ -0,0 +1,9 @@
+#include "include/private/dvr/bufferhub_rpc.h"
+
+namespace android {
+namespace dvr {
+
+constexpr char BufferHubRPC::kClientPath[];
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/bufferhub_tests.cpp
new file mode 100644
index 0000000..cb45dbe
--- /dev/null
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -0,0 +1,219 @@
+#include <android/native_window.h>
+#include <base/posix/eintr_wrapper.h>
+#include <gtest/gtest.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <mutex>
+#include <thread>
+
+using android::dvr::BufferProducer;
+using android::dvr::BufferConsumer;
+using android::pdx::LocalHandle;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const uint64_t kContext = 42;
+
+using LibBufferHubTest = ::testing::Test;
+
+TEST_F(LibBufferHubTest, TestBasicUsage) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  // Check that consumers can spawn other consumers.
+  std::unique_ptr<BufferConsumer> c2 =
+      BufferConsumer::Import(c->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+
+  EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+  // Both consumers should be triggered.
+  EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_LT(0, HANDLE_EINTR(c->Poll(10)));
+  EXPECT_LT(0, HANDLE_EINTR(c2->Poll(10)));
+
+  uint64_t context;
+  LocalHandle fence;
+  EXPECT_LE(0, c->Acquire(&fence, &context));
+  EXPECT_EQ(kContext, context);
+  EXPECT_GE(0, HANDLE_EINTR(c->Poll(0)));
+
+  EXPECT_LE(0, c2->Acquire(&fence, &context));
+  EXPECT_EQ(kContext, context);
+  EXPECT_GE(0, HANDLE_EINTR(c2->Poll(0)));
+
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, c2->Discard());
+
+  EXPECT_LE(0, HANDLE_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, p->Gain(&fence));
+  EXPECT_GE(0, HANDLE_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  Metadata m = {1, 3};
+  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+  EXPECT_LE(0, HANDLE_EINTR(c->Poll(10)));
+
+  LocalHandle fence;
+  Metadata m2 = {};
+  EXPECT_EQ(0, c->Acquire(&fence, &m2));
+  EXPECT_EQ(m.field1, m2.field1);
+  EXPECT_EQ(m.field2, m2.field2);
+
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_LT(0, HANDLE_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+  EXPECT_GE(0, HANDLE_EINTR(c->Poll(10)));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  Metadata m = {1, 3};
+  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+
+  LocalHandle fence;
+  int64_t sequence;
+  EXPECT_NE(0, c->Acquire(&fence, &sequence));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+
+  LocalHandle fence;
+  EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestWithNoMeta) {
+  std::unique_ptr<BufferProducer> p =
+      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  LocalHandle fence;
+
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+  EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
+  std::unique_ptr<BufferProducer> p =
+      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  // Record the original buffer id for later comparison.
+  const int buffer_id = p->id();
+
+  auto c = BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_NE(nullptr, c);
+
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+
+  // Close the connection to the producer. This should not affect the consumer.
+  p = nullptr;
+
+  LocalHandle fence;
+  EXPECT_EQ(0, c->Acquire(&fence));
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+
+  // Attempt to reconnect to the persistent buffer.
+  p = BufferProducer::Create("TestPersistentBuffer");
+  ASSERT_NE(nullptr, p);
+  EXPECT_EQ(buffer_id, p->id());
+  EXPECT_EQ(0, p->Gain(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  // Close the connection to the producer.
+  p = nullptr;
+
+  // Mismatch the params.
+  p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2,
+                             kHeight, kFormat, kUsage);
+  ASSERT_EQ(nullptr, p);
+}
+
+TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  LocalHandle fence;
+  auto c = BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_NE(nullptr, c);
+  EXPECT_NE(-EPIPE, c->Acquire(&fence));
+
+  // Test that removing persistence and closing the producer orphans the
+  // consumer.
+  EXPECT_EQ(0, p->RemovePersistence());
+  p = nullptr;
+
+  EXPECT_EQ(-EPIPE, c->Release(LocalHandle()));
+}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..b6ff5b6
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -0,0 +1,313 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_
+
+#include <hardware/gralloc.h>
+#include <pdx/channel_handle.h>
+#include <pdx/client.h>
+#include <pdx/file_handle.h>
+#include <pdx/status.h>
+
+#include <vector>
+
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class BufferHubBuffer : public pdx::Client {
+ public:
+  using LocalHandle = pdx::LocalHandle;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  template <typename T>
+  using Status = pdx::Status<T>;
+
+  // Create a new consumer channel that is attached to the producer. Returns
+  // a file descriptor for the new channel or a negative error code.
+  Status<LocalChannelHandle> CreateConsumer();
+
+  // Polls the fd for |timeout_ms| milliseconds (-1 for infinity).
+  int Poll(int timeout_ms);
+
+  // Locks the area specified by (x, y, width, height) for a specific usage. If
+  // the usage is software then |addr| will be updated to point to the address
+  // of the buffer in virtual memory. The caller should only access/modify the
+  // pixels in the specified area. anything else is undefined behavior.
+  int Lock(int usage, int x, int y, int width, int height, void** addr,
+           size_t index);
+
+  // Must be called after Lock() when the caller has finished changing the
+  // buffer.
+  int Unlock(size_t index);
+
+  // Helper for when index is 0.
+  int Lock(int usage, int x, int y, int width, int height, void** addr) {
+    return Lock(usage, x, y, width, height, addr, 0);
+  }
+
+  // Helper for when index is 0.
+  int Unlock() { return Unlock(0); }
+
+  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadWritePointer(size_t size, void** addr);
+
+  // Gets a blob buffer that was created with BufferProducer::CreateBlob.
+  // Locking and Unlocking is handled internally. There's no need to Unlock
+  // after calling this method.
+  int GetBlobReadOnlyPointer(size_t size, void** addr);
+
+  // Returns a dup'd file descriptor for accessing the blob shared memory. The
+  // caller takes ownership of the file descriptor and must close it or pass on
+  // ownership. Some GPU API extensions can take file descriptors to bind shared
+  // memory gralloc buffers to GPU buffer objects.
+  LocalHandle GetBlobFd() const {
+    // Current GPU vendor puts the buffer allocation in one FD. If we change GPU
+    // vendors and this is the wrong fd, late-latching and EDS will very clearly
+    // stop working and we will need to correct this. The alternative is to use
+    // a GL context in the pose service to allocate this buffer or to use the
+    // ION API directly instead of gralloc.
+    return LocalHandle(dup(native_handle()->data[0]));
+  }
+
+  using Client::event_fd;
+  native_handle_t* native_handle() const {
+    return const_cast<native_handle_t*>(slices_[0].handle());
+  }
+  // If index is greater than or equal to slice_count(), the result is
+  // undefined.
+  native_handle_t* native_handle(size_t index) const {
+    return const_cast<native_handle_t*>(slices_[index].handle());
+  }
+
+  IonBuffer* buffer() { return &slices_[0]; }
+  // If index is greater than or equal to slice_count(), the result is
+  // undefined.
+  IonBuffer* slice(size_t index) { return &slices_[index]; }
+
+  int slice_count() const { return static_cast<int>(slices_.size()); }
+  int id() const { return id_; }
+
+  // The following methods return settings of the first buffer. Currently,
+  // it is only possible to create multi-buffer BufferHubBuffers with the same
+  // settings.
+  int width() const { return slices_[0].width(); }
+  int height() const { return slices_[0].height(); }
+  int stride() const { return slices_[0].stride(); }
+  int format() const { return slices_[0].format(); }
+  int usage() const { return slices_[0].usage(); }
+
+ protected:
+  explicit BufferHubBuffer(LocalChannelHandle channel);
+  explicit BufferHubBuffer(const std::string& endpoint_path);
+  virtual ~BufferHubBuffer();
+
+  // Initialization helper.
+  int ImportBuffer();
+
+ private:
+  BufferHubBuffer(const BufferHubBuffer&) = delete;
+  void operator=(const BufferHubBuffer&) = delete;
+
+  // Global id for the buffer that is consistent across processes. It is meant
+  // for logging and debugging purposes only and should not be used for lookup
+  // or any other functional purpose as a security precaution.
+  int id_;
+
+  // A BufferHubBuffer may contain multiple slices of IonBuffers with same
+  // configurations.
+  std::vector<IonBuffer> slices_;
+};
+
+// This represents a writable buffer. Calling Post notifies all clients and
+// makes the buffer read-only. Call Gain to acquire write access. A buffer
+// may have many consumers.
+//
+// The user of BufferProducer is responsible with making sure that the Post() is
+// done with the correct metadata type and size. The user is also responsible
+// for making sure that remote ends (BufferConsumers) are also using the correct
+// metadata when acquiring the buffer. The API guarantees that a Post() with a
+// metadata of wrong size will fail. However, it currently does not do any
+// type checking.
+// The API also assumes that metadata is a serializable type (plain old data).
+class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> {
+ public:
+  // Create a buffer designed to hold arbitrary bytes that can be read and
+  // written from CPU, GPU and DSP. The buffer is mapped uncached so that CPU
+  // reads and writes are predictable.
+  static std::unique_ptr<BufferProducer> CreateUncachedBlob(size_t size);
+
+  // Creates a persistent uncached buffer with the given name and access.
+  static std::unique_ptr<BufferProducer> CreatePersistentUncachedBlob(
+      const std::string& name, int user_id, int group_id, size_t size);
+
+  // Imports a bufferhub producer channel, assuming ownership of its handle.
+  static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<BufferProducer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Post this buffer, passing |ready_fence| to the consumers. The bytes in
+  // |meta| are passed unaltered to the consumers. The producer must not modify
+  // the buffer until it is re-gained.
+  // This returns zero or a negative unix error code.
+  int Post(const LocalHandle& ready_fence, const void* meta,
+           size_t meta_size_bytes);
+
+  template <typename Meta,
+            typename = typename std::enable_if<std::is_void<Meta>::value>::type>
+  int Post(const LocalHandle& ready_fence) {
+    return Post(ready_fence, nullptr, 0);
+  }
+  template <typename Meta, typename = typename std::enable_if<
+                               !std::is_void<Meta>::value>::type>
+  int Post(const LocalHandle& ready_fence, const Meta& meta) {
+    return Post(ready_fence, &meta, sizeof(meta));
+  }
+
+  // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it
+  // must be waited on before using the buffer. If it is not valid then the
+  // buffer is free for immediate use. This call will only succeed if the buffer
+  // is in the released state.
+  // This returns zero or a negative unix error code.
+  int Gain(LocalHandle* release_fence);
+
+  // Asynchronously marks a released buffer as gained. This method is similar to
+  // the synchronous version above, except that it does not wait for BufferHub
+  // to acknowledge success or failure, nor does it transfer a release fence to
+  // the client. This version may be used in situations where a release fence is
+  // not needed. Because of the asynchronous nature of the underlying message,
+  // no error is returned if this method is called when the buffer is in an
+  // incorrect state. Returns zero if sending the message succeeded, or a
+  // negative errno code otherwise.
+  int GainAsync();
+
+  // Attaches the producer to |name| so that it becomes a persistent buffer that
+  // may be retrieved by name at a later time. This may be used in cases where a
+  // shared memory buffer should persist across the life of the producer process
+  // (i.e. the buffer may be held by clients across a service restart). The
+  // buffer may be associated with a user and/or group id to restrict access to
+  // the buffer. If user_id or group_id is -1 then checks for the respective id
+  // are disabled. If user_id or group_id is 0 then the respective id of the
+  // calling process is used instead.
+  int MakePersistent(const std::string& name, int user_id, int group_id);
+
+  // Removes the persistence of the producer.
+  int RemovePersistence();
+
+ private:
+  friend BASE;
+
+  // Constructors are automatically exposed through BufferProducer::Create(...)
+  // static template methods inherited from ClientBase, which take the same
+  // arguments as the constructors.
+
+  // Constructs a buffer with the given geometry and parameters.
+  BufferProducer(int width, int height, int format, int usage,
+                 size_t metadata_size = 0, size_t slice_count = 1);
+
+  // Constructs a persistent buffer with the given geometry and parameters and
+  // binds it to |name| in one shot. If a persistent buffer with the same name
+  // and settings already exists and matches the given geometry and parameters,
+  // that buffer is connected to this client instead of creating a new buffer.
+  // If the name matches but the geometry or settings do not match then
+  // construction fails and BufferProducer::Create() returns nullptr.
+  //
+  // Access to the persistent buffer may be restricted by |user_id| and/or
+  // |group_id|; these settings are established only when the buffer is first
+  // created and cannot be changed. A user or group id of -1 disables checks for
+  // that respective id. A user or group id of 0 is substituted with the
+  // effective user or group id of the calling process.
+  BufferProducer(const std::string& name, int user_id, int group_id, int width,
+                 int height, int format, int usage, size_t metadata_size = 0,
+                 size_t slice_count = 1);
+
+  // Constructs a blob (flat) buffer with the given usage flags.
+  BufferProducer(int usage, size_t size);
+
+  // Constructs a persistent blob (flat) buffer and binds it to |name|.
+  BufferProducer(const std::string& name, int user_id, int group_id, int usage,
+                 size_t size);
+
+  // Constructs a channel to persistent buffer by name only. The buffer must
+  // have been previously created or made persistent.
+  explicit BufferProducer(const std::string& name);
+
+  // Imports the given file handle to a producer channel, taking ownership.
+  explicit BufferProducer(LocalChannelHandle channel);
+};
+
+// This is a connection to a producer buffer, which can be located in another
+// application. When that buffer is Post()ed, this fd will be signaled and
+// Acquire allows read access. The user is responsible for making sure that
+// Acquire is called with the correct metadata structure. The only guarantee the
+// API currently provides is that an Acquire() with metadata of the wrong size
+// will fail.
+class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> {
+ public:
+  // This call assumes ownership of |fd|.
+  static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel);
+  static std::unique_ptr<BufferConsumer> Import(
+      Status<LocalChannelHandle> status);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| will be set to a fence to wait on until the buffer is ready.
+  // This call will only succeed after the fd is signalled. This call may be
+  // performed as an alternative to the Acquire() with metadata. In such cases
+  // the metadata is not read.
+  //
+  // This returns zero or negative unix error code.
+  int Acquire(LocalHandle* ready_fence);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| is set to a fence signaling that the contents of the buffer
+  // are available. This call will only succeed if the buffer is in the posted
+  // state.
+  // Returns zero on success, or a negative errno code otherwise.
+  int Acquire(LocalHandle* ready_fence, void* meta, size_t meta_size_bytes);
+
+  // Attempt to retrieve a post event from buffer hub. If successful,
+  // |ready_fence| is set to a fence to wait on until the buffer is ready. This
+  // call will only succeed after the fd is signaled. This returns zero or a
+  // negative unix error code.
+  template <typename Meta>
+  int Acquire(LocalHandle* ready_fence, Meta* meta) {
+    return Acquire(ready_fence, meta, sizeof(*meta));
+  }
+
+  // This should be called after a successful Acquire call. If the fence is
+  // valid the fence determines the buffer usage, otherwise the buffer is
+  // released immediately.
+  // This returns zero or a negative unix error code.
+  int Release(const LocalHandle& release_fence);
+
+  // Asynchronously releases a buffer. Similar to the synchronous version above,
+  // except that it does not wait for BufferHub to reply with success or error,
+  // nor does it transfer a release fence. This version may be used in
+  // situations where a release fence is not needed. Because of the asynchronous
+  // nature of the underlying message, no error is returned if this method is
+  // called when the buffer is in an incorrect state. Returns zero if sending
+  // the message succeeded, or a negative errno code otherwise.
+  int ReleaseAsync();
+
+  // May be called after or instead of Acquire to indicate that the consumer
+  // does not need to access the buffer this cycle. This returns zero or a
+  // negative unix error code.
+  int Discard();
+
+  // When set, this consumer is no longer notified when this buffer is
+  // available. The system behaves as if Discard() is immediately called
+  // whenever the buffer is posted. If ignore is set to true while a buffer is
+  // pending, it will act as if Discard() was also called.
+  // This returns zero or a negative unix error code.
+  int SetIgnore(bool ignore);
+
+ private:
+  friend BASE;
+
+  explicit BufferConsumer(LocalChannelHandle channel);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_CLIENT_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
new file mode 100644
index 0000000..ed11551
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -0,0 +1,214 @@
+#ifndef ANDROID_DVR_BUFFERHUB_RPC_H_
+#define ANDROID_DVR_BUFFERHUB_RPC_H_
+
+#include <cutils/native_handle.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/types.h>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/remote_method.h>
+#include <pdx/rpc/serializable.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+template <typename FileHandleType>
+class NativeBufferHandle {
+ public:
+  NativeBufferHandle() { Clear(); }
+  NativeBufferHandle(const IonBuffer& buffer, int id)
+      : id_(id),
+        stride_(buffer.stride()),
+        width_(buffer.width()),
+        height_(buffer.height()),
+        format_(buffer.format()),
+        usage_(buffer.usage()) {
+    // Populate the fd and int vectors: native_handle->data[] is an array of fds
+    // followed by an array of opaque ints.
+    const int fd_count = buffer.handle()->numFds;
+    const int int_count = buffer.handle()->numInts;
+    for (int i = 0; i < fd_count; i++) {
+      fds_.emplace_back(FileHandleType::AsDuplicate(buffer.handle()->data[i]));
+    }
+    for (int i = 0; i < int_count; i++) {
+      opaque_ints_.push_back(buffer.handle()->data[fd_count + i]);
+    }
+  }
+  NativeBufferHandle(NativeBufferHandle&& other) = default;
+
+  // Imports the native handle into the given IonBuffer instance.
+  int Import(IonBuffer* buffer) {
+    // This is annoying, but we need to convert the vector of FileHandles into a
+    // vector of ints for the Import API.
+    std::vector<int> fd_ints;
+    for (const auto& fd : fds_)
+      fd_ints.push_back(fd.Get());
+
+    const int ret = buffer->Import(fd_ints.data(), fd_ints.size(),
+                                   opaque_ints_.data(), opaque_ints_.size(),
+                                   width_, height_, stride_, format_, usage_);
+    if (ret < 0)
+      return ret;
+
+    // Import succeeded, release the file handles which are now owned by the
+    // IonBuffer and clear members.
+    for (auto& fd : fds_)
+      fd.Release();
+    opaque_ints_.clear();
+    Clear();
+
+    return 0;
+  }
+
+  int id() const { return id_; }
+  size_t IntCount() const { return opaque_ints_.size(); }
+  size_t FdCount() const { return fds_.size(); }
+
+ private:
+  int id_;
+  int stride_;
+  int width_;
+  int height_;
+  int format_;
+  int usage_;
+  std::vector<int> opaque_ints_;
+  std::vector<FileHandleType> fds_;
+
+  void Clear() { id_ = stride_ = width_ = height_ = format_ = usage_ = -1; }
+
+  PDX_SERIALIZABLE_MEMBERS(NativeBufferHandle<FileHandleType>, id_, stride_,
+                           width_, height_, format_, usage_, opaque_ints_,
+                           fds_);
+
+  NativeBufferHandle(const NativeBufferHandle&) = delete;
+  void operator=(const NativeBufferHandle&) = delete;
+};
+
+template <typename FileHandleType>
+class FenceHandle {
+ public:
+  FenceHandle() = default;
+  explicit FenceHandle(int fence) : fence_{fence} {}
+  explicit FenceHandle(FileHandleType&& fence) : fence_{std::move(fence)} {}
+  FenceHandle(FenceHandle&&) = default;
+  FenceHandle& operator=(FenceHandle&&) = default;
+
+  explicit operator bool() const { return fence_.IsValid(); }
+
+  const FileHandleType& get() const { fence_; }
+  FileHandleType&& take() { return std::move(fence_); }
+
+  int get_fd() const { return fence_.Get(); }
+  void close() { fence_.Close(); }
+
+  FenceHandle<pdx::BorrowedHandle> borrow() const {
+    return FenceHandle<pdx::BorrowedHandle>(fence_.Borrow());
+  }
+
+ private:
+  FileHandleType fence_;
+
+  PDX_SERIALIZABLE_MEMBERS(FenceHandle<FileHandleType>, fence_);
+
+  FenceHandle(const FenceHandle&) = delete;
+  void operator=(const FenceHandle&) = delete;
+};
+
+using LocalFence = FenceHandle<pdx::LocalHandle>;
+using BorrowedFence = FenceHandle<pdx::BorrowedHandle>;
+
+// BufferHub Service RPC interface. Defines the endpoints, op codes, and method
+// type signatures supported by bufferhubd.
+struct BufferHubRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/buffer_hub/client";
+
+  // |BufferHubQueue| will keep track of at most this value of buffers.
+  // Attempts at runtime to increase the number of buffers past this
+  // will fail. Note that the value is in sync with |android::BufferQueue|, so
+  // that slot id can be shared between |android::dvr::BufferHubQueueProducer|
+  // and |android::BufferQueueProducer| which both implements the same
+  // interface: |android::IGraphicBufferProducer|.
+  static constexpr size_t kMaxQueueCapacity =
+      android::BufferQueueDefs::NUM_BUFFER_SLOTS;
+
+  // Op codes.
+  enum {
+    kOpCreateBuffer = 0,
+    kOpCreatePersistentBuffer,
+    kOpGetPersistentBuffer,
+    kOpGetBuffer,
+    kOpGetBuffers,
+    kOpNewConsumer,
+    kOpProducerMakePersistent,
+    kOpProducerRemovePersistence,
+    kOpProducerPost,
+    kOpProducerGain,
+    kOpConsumerAcquire,
+    kOpConsumerRelease,
+    kOpConsumerSetIgnore,
+    kOpCreateProducerQueue,
+    kOpCreateConsumerQueue,
+    kOpProducerQueueAllocateBuffers,
+    kOpProducerQueueDetachBuffer,
+    kOpConsumerQueueImportBuffers,
+  };
+
+  // Aliases.
+  using MetaData = pdx::rpc::BufferWrapper<std::uint8_t*>;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  using LocalHandle = pdx::LocalHandle;
+  using Void = pdx::rpc::Void;
+
+  // Methods.
+  PDX_REMOTE_METHOD(CreateBuffer, kOpCreateBuffer,
+                    int(int width, int height, int format, int usage,
+                        size_t meta_size_bytes, size_t slice_count));
+  PDX_REMOTE_METHOD(CreatePersistentBuffer, kOpCreatePersistentBuffer,
+                    int(const std::string& name, int user_id, int group_id,
+                        int width, int height, int format, int usage,
+                        size_t meta_size_bytes, size_t slice_count));
+  PDX_REMOTE_METHOD(GetPersistentBuffer, kOpGetPersistentBuffer,
+                    int(const std::string& name));
+  PDX_REMOTE_METHOD(GetBuffer, kOpGetBuffer,
+                    NativeBufferHandle<LocalHandle>(unsigned index));
+  PDX_REMOTE_METHOD(GetBuffers, kOpGetBuffers,
+                    std::vector<NativeBufferHandle<LocalHandle>>(Void));
+  PDX_REMOTE_METHOD(NewConsumer, kOpNewConsumer, LocalChannelHandle(Void));
+  PDX_REMOTE_METHOD(ProducerMakePersistent, kOpProducerMakePersistent,
+                    int(const std::string& name, int user_id, int group_id));
+  PDX_REMOTE_METHOD(ProducerRemovePersistence, kOpProducerRemovePersistence,
+                    int(Void));
+  PDX_REMOTE_METHOD(ProducerPost, kOpProducerPost,
+                    int(LocalFence acquire_fence, MetaData));
+  PDX_REMOTE_METHOD(ProducerGain, kOpProducerGain, LocalFence(Void));
+  PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire,
+                    std::pair<LocalFence, MetaData>(std::size_t metadata_size));
+  PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
+                    int(LocalFence release_fence));
+  PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, int(bool ignore));
+
+  // Buffer Queue Methods.
+  PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue,
+                    int(size_t meta_size_bytes, int usage_set_mask,
+                        int usage_clear_mask, int usage_deny_set_mask,
+                        int usage_deny_clear_mask));
+  PDX_REMOTE_METHOD(CreateConsumerQueue, kOpCreateConsumerQueue,
+                    std::pair<LocalChannelHandle, size_t>(Void));
+  PDX_REMOTE_METHOD(ProducerQueueAllocateBuffers,
+                    kOpProducerQueueAllocateBuffers,
+                    std::vector<std::pair<LocalChannelHandle, size_t>>(
+                        int width, int height, int format, int usage,
+                        size_t slice_count, size_t buffer_count));
+  PDX_REMOTE_METHOD(ProducerQueueDetachBuffer, kOpProducerQueueDetachBuffer,
+                    int(size_t slot));
+  PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers,
+                    std::vector<std::pair<LocalChannelHandle, size_t>>(Void));
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUB_RPC_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..8125c54
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -0,0 +1,108 @@
+#ifndef ANDROID_DVR_ION_BUFFER_H_
+#define ANDROID_DVR_ION_BUFFER_H_
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+  IonBuffer();
+  IonBuffer(int width, int height, int format, int usage);
+  IonBuffer(buffer_handle_t handle, int width, int height, int stride,
+            int format, int usage);
+  IonBuffer(buffer_handle_t handle, int width, int height, int layer_count,
+            int stride, int layer_stride, int format, int usage);
+  ~IonBuffer();
+
+  IonBuffer(IonBuffer&& other);
+  IonBuffer& operator=(IonBuffer&& other);
+
+  // Frees the underlying native handle and leaves the instance initialized to
+  // empty.
+  void FreeHandle();
+
+  // Allocates a new native handle with the given parameters, freeing the
+  // previous native handle if necessary. Returns 0 on success or a negative
+  // errno code otherwise. If allocation fails the previous native handle is
+  // left intact.
+  int Alloc(int width, int height, int format, int usage);
+
+  // Resets the underlying native handle and parameters, freeing the previous
+  // native handle if necessary.
+  void Reset(buffer_handle_t handle, int width, int height, int stride,
+             int format, int usage);
+
+  // Like Reset but also registers the native handle, which is necessary for
+  // native handles received over IPC. Returns 0 on success or a negative errno
+  // code otherwise. If import fails the previous native handle is left intact.
+  int Import(buffer_handle_t handle, int width, int height, int stride,
+             int format, int usage);
+
+  // Like Reset but imports a native handle from raw fd and int arrays. Returns
+  // 0 on success or a negative errno code otherwise. If import fails the
+  // previous native handle is left intact.
+  int Import(const int* fd_array, int fd_count, const int* int_array,
+             int int_count, int width, int height, int stride, int format,
+             int usage);
+
+  // Duplicates the native handle underlying |other| and then imports it. This
+  // is useful for creating multiple, independent views of the same Ion/Gralloc
+  // buffer. Returns 0 on success or a negative errno code otherwise. If
+  // duplication or import fail the previous native handle is left intact.
+  int Duplicate(const IonBuffer* other);
+
+  int Lock(int usage, int x, int y, int width, int height, void** address);
+  int LockYUV(int usage, int x, int y, int width, int height,
+              struct android_ycbcr* yuv);
+  int Unlock();
+
+  buffer_handle_t handle() const { return handle_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+  int layer_count() const { return layer_count_; }
+  int stride() const { return stride_; }
+  int layer_stride() const { return layer_stride_; }
+  int format() const { return format_; }
+  int usage() const { return usage_; }
+
+  static gralloc_module_t const* GetGrallocModule() {
+    GrallocInit();
+    return gralloc_module_;
+  }
+
+  static alloc_device_t* GetGrallocDevice() {
+    GrallocInit();
+    return gralloc_device_;
+  }
+
+ private:
+  buffer_handle_t handle_;
+  int width_;
+  int height_;
+  int layer_count_;
+  int stride_;
+  int layer_stride_;
+  int format_;
+  int usage_;
+  bool locked_;
+  bool needs_unregister_;
+
+  void Replace(buffer_handle_t handle, int width, int height, int layer_count,
+               int stride, int layer_stride, int format, int usage,
+               bool needs_unregister);
+
+  static void GrallocInit();
+  static gralloc_module_t const* gralloc_module_;
+  static alloc_device_t* gralloc_device_;
+
+  IonBuffer(const IonBuffer&) = delete;
+  void operator=(const IonBuffer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_ION_BUFFER_H_
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_buffer.h b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
new file mode 100644
index 0000000..f6c24d9
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -0,0 +1,226 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_H_
+#define ANDROID_DVR_NATIVE_BUFFER_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android/native_window.h>
+#include <base/logging.h>
+#include <cutils/log.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/RefBase.h>
+
+#include <private/dvr/buffer_hub_client.h>
+
+namespace android {
+namespace dvr {
+
+// ANativeWindowBuffer is the abstraction Android HALs and frameworks use to
+// pass around hardware graphics buffers. The following classes implement this
+// abstraction with different DVR backing buffers, all of which provide
+// different semantics on top of ion/gralloc buffers.
+
+// An implementation of ANativeWindowBuffer backed by an IonBuffer.
+class NativeBuffer
+    : public android::ANativeObjectBase<ANativeWindowBuffer, NativeBuffer,
+                                        android::LightRefBase<NativeBuffer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBuffer(const std::shared_ptr<IonBuffer>& buffer)
+      : BASE(), buffer_(buffer), fence_(kEmptyFence) {
+    ANativeWindowBuffer::width = buffer->width();
+    ANativeWindowBuffer::height = buffer->height();
+    ANativeWindowBuffer::stride = buffer->stride();
+    ANativeWindowBuffer::format = buffer->format();
+    ANativeWindowBuffer::usage = buffer->usage();
+    handle = buffer_->handle();
+  }
+
+  virtual ~NativeBuffer() {}
+
+  std::shared_ptr<IonBuffer> buffer() { return buffer_; }
+  int fence() const { return fence_.Get(); }
+
+  void SetFence(int fence) { fence_.Reset(fence); }
+
+ private:
+  friend class android::LightRefBase<NativeBuffer>;
+
+  std::shared_ptr<IonBuffer> buffer_;
+  pdx::LocalHandle fence_;
+
+  NativeBuffer(const NativeBuffer&) = delete;
+  void operator=(NativeBuffer&) = delete;
+};
+
+// NativeBufferProducerSlice is an implementation of ANativeWindowBuffer backed
+// by a buffer slice of a BufferProducer.
+class NativeBufferProducerSlice
+    : public android::ANativeObjectBase<
+          ANativeWindowBuffer, NativeBufferProducerSlice,
+          android::LightRefBase<NativeBufferProducerSlice>> {
+ public:
+  NativeBufferProducerSlice(const std::shared_ptr<BufferProducer>& buffer,
+                            int buffer_index)
+      : BASE(), buffer_(buffer) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle(buffer_index);
+  }
+
+  virtual ~NativeBufferProducerSlice() {}
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducerSlice>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+
+  NativeBufferProducerSlice(const NativeBufferProducerSlice&) = delete;
+  void operator=(NativeBufferProducerSlice&) = delete;
+};
+
+// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a
+// BufferProducer.
+class NativeBufferProducer : public android::ANativeObjectBase<
+  ANativeWindowBuffer, NativeBufferProducer,
+  android::LightRefBase<NativeBufferProducer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer,
+                       EGLDisplay display, uint32_t surface_buffer_index)
+      : BASE(),
+        buffer_(buffer),
+        surface_buffer_index_(surface_buffer_index),
+        display_(display) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle();
+    for (int i = 0; i < buffer->slice_count(); ++i) {
+      // display == null means don't create an EGL image. This is used by our
+      // Vulkan code.
+      slices_.push_back(new NativeBufferProducerSlice(buffer, i));
+      if (display_ != nullptr) {
+        egl_images_.push_back(eglCreateImageKHR(
+            display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+            static_cast<ANativeWindowBuffer*>(slices_.back().get()), nullptr));
+        if (egl_images_.back() == EGL_NO_IMAGE_KHR) {
+          ALOGE("NativeBufferProducer: eglCreateImageKHR failed");
+        }
+      }
+    }
+  }
+
+  explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer)
+      : NativeBufferProducer(buffer, nullptr, 0) {}
+
+  virtual ~NativeBufferProducer() {
+    for (EGLImageKHR egl_image : egl_images_) {
+      if (egl_image != EGL_NO_IMAGE_KHR)
+        eglDestroyImageKHR(display_, egl_image);
+    }
+  }
+
+  EGLImageKHR image_khr(int index) const { return egl_images_[index]; }
+  std::shared_ptr<BufferProducer> buffer() const { return buffer_; }
+  int release_fence() const { return release_fence_.Get(); }
+  uint32_t surface_buffer_index() const { return surface_buffer_index_; }
+
+  // Return the release fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); }
+
+  // Post the buffer consumer, closing the acquire and release fences.
+  int Post(int acquire_fence, uint64_t sequence) {
+    release_fence_.Close();
+    return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence);
+  }
+
+  // Gain the buffer producer, closing the previous release fence if valid.
+  int Gain() { return buffer_->Gain(&release_fence_); }
+
+  // Asynchronously gain the buffer, closing the previous release fence.
+  int GainAsync() {
+    release_fence_.Close();
+    return buffer_->GainAsync();
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducer>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+  pdx::LocalHandle release_fence_;
+  std::vector<android::sp<NativeBufferProducerSlice>> slices_;
+  std::vector<EGLImageKHR> egl_images_;
+  uint32_t surface_buffer_index_;
+  EGLDisplay display_;
+
+  NativeBufferProducer(const NativeBufferProducer&) = delete;
+  void operator=(NativeBufferProducer&) = delete;
+};
+
+// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a
+// BufferConsumer.
+class NativeBufferConsumer : public android::ANativeObjectBase<
+                                 ANativeWindowBuffer, NativeBufferConsumer,
+                                 android::LightRefBase<NativeBufferConsumer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer,
+                                int index)
+      : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    CHECK(buffer_->slice_count() > index);
+    handle = buffer_->slice(index)->handle();
+  }
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer)
+      : NativeBufferConsumer(buffer, 0) {}
+
+  virtual ~NativeBufferConsumer() {}
+
+  std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+  int acquire_fence() const { return acquire_fence_.Get(); }
+  uint64_t sequence() const { return sequence_; }
+
+  // Return the acquire fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); }
+
+  // Acquire the underlying buffer consumer, closing the previous acquire fence
+  // if valid.
+  int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); }
+
+  // Release the buffer consumer, closing the acquire and release fences if
+  // valid.
+  int Release(int release_fence) {
+    acquire_fence_.Close();
+    sequence_ = 0;
+    return buffer_->Release(pdx::LocalHandle(release_fence));
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferConsumer>;
+
+  std::shared_ptr<BufferConsumer> buffer_;
+  pdx::LocalHandle acquire_fence_;
+  uint64_t sequence_;
+
+  NativeBufferConsumer(const NativeBufferConsumer&) = delete;
+  void operator=(NativeBufferConsumer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NATIVE_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
new file mode 100644
index 0000000..7d20049
--- /dev/null
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -0,0 +1,322 @@
+#include <private/dvr/ion_buffer.h>
+
+#include <cutils/log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+namespace android {
+namespace dvr {
+
+gralloc_module_t const* IonBuffer::gralloc_module_ = nullptr;
+alloc_device_t* IonBuffer::gralloc_device_ = nullptr;
+
+IonBuffer::IonBuffer() : IonBuffer(nullptr, 0, 0, 0, 0, 0, 0, 0) {}
+
+IonBuffer::IonBuffer(int width, int height, int format, int usage)
+    : IonBuffer() {
+  Alloc(width, height, format, usage);
+}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, int width, int height, int stride,
+                     int format, int usage)
+    : IonBuffer(handle, width, height, 1, stride, 0, format, usage) {}
+
+IonBuffer::IonBuffer(buffer_handle_t handle, int width, int height,
+                     int layer_count, int stride, int layer_stride, int format,
+                     int usage)
+    : handle_(handle),
+      width_(width),
+      height_(height),
+      layer_count_(layer_count),
+      stride_(stride),
+      layer_stride_(layer_stride),
+      format_(format),
+      usage_(usage),
+      locked_(false),
+      needs_unregister_(false) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::IonBuffer: handle=%p width=%d height=%d layer_count=%d "
+           "stride=%d layer stride=%d format=%d usage=%d",
+           handle_, width_, height_, layer_count_, stride_, layer_stride_,
+           format_, usage_);
+  GrallocInit();
+}
+
+IonBuffer::~IonBuffer() {
+  ALOGD_IF(TRACE,
+           "IonBuffer::~IonBuffer: handle=%p width=%d height=%d stride=%d "
+           "format=%d usage=%d",
+           handle_, width_, height_, stride_, format_, usage_);
+
+  FreeHandle();
+}
+
+IonBuffer::IonBuffer(IonBuffer&& other) : IonBuffer() {
+  *this = std::move(other);
+}
+
+IonBuffer& IonBuffer::operator=(IonBuffer&& other) {
+  ALOGD_IF(TRACE, "IonBuffer::operator=: handle_=%p other.handle_=%p", handle_,
+           other.handle_);
+
+  if (this != &other) {
+    Replace(other.handle_, other.width_, other.height_, other.layer_count_,
+            other.stride_, other.layer_stride_, other.format_, other.usage_,
+            other.needs_unregister_);
+    locked_ = other.locked_;
+    other.handle_ = nullptr;
+    other.FreeHandle();
+  }
+
+  return *this;
+}
+
+void IonBuffer::FreeHandle() {
+  if (handle_) {
+    // Lock/Unlock don't need to be balanced, but one Unlock is needed to
+    // clean/unmap the buffer. Warn if this didn't happen before freeing the
+    // native handle.
+    ALOGW_IF(locked_,
+             "IonBuffer::FreeHandle: freeing a locked handle!!! handle=%p",
+             handle_);
+
+    if (needs_unregister_) {
+      int ret = gralloc_module_->unregisterBuffer(gralloc_module_, handle_);
+      ALOGE_IF(ret < 0,
+               "IonBuffer::FreeHandle: Failed to unregister handle: %s",
+               strerror(-ret));
+
+      native_handle_close(const_cast<native_handle_t*>(handle_));
+      native_handle_delete(const_cast<native_handle_t*>(handle_));
+    } else {
+      int ret = gralloc_device_->free(gralloc_device_, handle_);
+      if (ret < 0) {
+        ALOGE("IonBuffer::FreeHandle: failed to free buffer: %s",
+              strerror(-ret));
+
+        // Not sure if this is the right thing to do. Attempting to prevent a
+        // memory leak of the native handle.
+        native_handle_close(const_cast<native_handle_t*>(handle_));
+        native_handle_delete(const_cast<native_handle_t*>(handle_));
+      }
+    }
+  }
+
+  // Always re-initialize these members, even if handle_ was nullptr, in case
+  // someone was dumb enough to pass a nullptr handle to the constructor or
+  // Reset.
+  handle_ = nullptr;
+  width_ = 0;
+  height_ = 0;
+  layer_count_ = 0;
+  stride_ = 0;
+  layer_stride_ = 0;
+  format_ = 0;
+  usage_ = 0;
+  locked_ = false;
+  needs_unregister_ = false;
+}
+
+int IonBuffer::Alloc(int width, int height, int format, int usage) {
+  ATRACE_NAME("IonBuffer::Alloc");
+  ALOGD_IF(TRACE, "IonBuffer::Alloc: width=%d height=%d format=%d usage=%d",
+           width, height, format, usage);
+
+  int stride;
+  buffer_handle_t handle;
+
+  int ret = gralloc_device_->alloc(gralloc_device_, width, height, format,
+                                   usage, &handle, &stride);
+  if (ret < 0) {
+    ALOGE("IonBuffer::Alloc: failed to allocate gralloc buffer: %s",
+          strerror(-ret));
+    return ret;
+  }
+
+  Replace(handle, width, height, 1, stride, 0, format, usage, false);
+  return 0;
+}
+
+void IonBuffer::Replace(buffer_handle_t handle, int width, int height,
+                        int layer_count, int stride, int layer_stride,
+                        int format, int usage, bool needs_unregister) {
+  FreeHandle();
+
+  handle_ = handle;
+  width_ = width;
+  height_ = height;
+  layer_count_ = layer_count;
+  stride_ = stride;
+  layer_stride_ = layer_stride;
+  format_ = format;
+  usage_ = usage;
+  needs_unregister_ = needs_unregister;
+}
+
+void IonBuffer::Reset(buffer_handle_t handle, int width, int height, int stride,
+                      int format, int usage) {
+  ALOGD_IF(TRACE,
+           "IonBuffer::Reset: handle=%p width=%d height=%d stride=%d format=%d "
+           "usage=%d",
+           handle, width, height, stride, format, usage);
+
+  Replace(handle, width, height, 1, stride, 0, format, usage, false);
+}
+
+int IonBuffer::Import(buffer_handle_t handle, int width, int height, int stride,
+                      int format, int usage) {
+  ATRACE_NAME("IonBuffer::Import1");
+  ALOGD_IF(
+      TRACE,
+      "IonBuffer::Import: handle=%p width=%d height=%d stride=%d format=%d "
+      "usage=%d",
+      handle, width, height, stride, format, usage);
+
+  int ret = gralloc_module_->registerBuffer(gralloc_module_, handle);
+  if (ret < 0) {
+    ALOGE("IonBuffer::Import: failed to import handle: %s", strerror(-ret));
+    return ret;
+  }
+
+  Replace(handle, width, height, 1, stride, 0, format, usage, true);
+  return 0;
+}
+
+int IonBuffer::Import(const int* fd_array, int fd_count, const int* int_array,
+                      int int_count, int width, int height, int stride,
+                      int format, int usage) {
+  ATRACE_NAME("IonBuffer::Import2");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Import: fd_count=%d int_count=%d width=%d height=%d "
+           "stride=%d format=%d usage=%d",
+           fd_count, int_count, width, height, stride, format, usage);
+
+  if (fd_count < 0 || int_count < 0) {
+    ALOGE("IonBuffer::Import: invalid arguments.");
+    return -EINVAL;
+  }
+
+  native_handle_t* handle = native_handle_create(fd_count, int_count);
+  if (!handle) {
+    ALOGE("IonBuffer::Import: failed to create new native handle.");
+    return -ENOMEM;
+  }
+
+  // Copy fd_array into the first part of handle->data and int_array right
+  // after it.
+  memcpy(handle->data, fd_array, sizeof(int) * fd_count);
+  memcpy(handle->data + fd_count, int_array, sizeof(int) * int_count);
+
+  int ret = Import(handle, width, height, stride, format, usage);
+  if (ret < 0) {
+    ALOGE("IonBuffer::Import: failed to import raw native handle: %s",
+          strerror(-ret));
+    native_handle_close(handle);
+    native_handle_delete(handle);
+  }
+
+  return ret;
+}
+
+int IonBuffer::Duplicate(const IonBuffer* other) {
+  if (!other->handle())
+    return -EINVAL;
+
+  const int fd_count = other->handle()->numFds;
+  const int int_count = other->handle()->numInts;
+
+  if (fd_count < 0 || int_count < 0)
+    return -EINVAL;
+
+  native_handle_t* handle = native_handle_create(fd_count, int_count);
+  if (!handle) {
+    ALOGE("IonBuffer::Duplicate: Failed to create new native handle.");
+    return -ENOMEM;
+  }
+
+  // Duplicate the file descriptors from the other native handle.
+  for (int i = 0; i < fd_count; i++)
+    handle->data[i] = dup(other->handle()->data[i]);
+
+  // Copy the ints after the file descriptors.
+  memcpy(handle->data + fd_count, other->handle()->data + fd_count,
+         sizeof(int) * int_count);
+
+  const int ret = Import(handle, other->width(), other->height(),
+                         other->stride(), other->format(), other->usage());
+  if (ret < 0) {
+    ALOGE("IonBuffer::Duplicate: Failed to import duplicate native handle: %s",
+          strerror(-ret));
+    native_handle_close(handle);
+    native_handle_delete(handle);
+  }
+
+  return ret;
+}
+
+int IonBuffer::Lock(int usage, int x, int y, int width, int height,
+                    void** address) {
+  ATRACE_NAME("IonBuffer::Lock");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d "
+           "address=%p",
+           handle_, usage, x, y, width, height, address);
+
+  // Lock may be called multiple times; but only one Unlock is required.
+  const int err = gralloc_module_->lock(gralloc_module_, handle_, usage, x, y,
+                                        width, height, address);
+  if (!err)
+    locked_ = true;
+
+  return err;
+}
+
+int IonBuffer::LockYUV(int usage, int x, int y, int width, int height,
+                       struct android_ycbcr* yuv) {
+  ATRACE_NAME("IonBuffer::LockYUV");
+  ALOGD_IF(TRACE,
+           "IonBuffer::Lock: handle=%p usage=%d x=%d y=%d width=%d height=%d",
+           handle_, usage, x, y, width, height);
+  const int err = gralloc_module_->lock_ycbcr(gralloc_module_, handle_, usage,
+                                              x, y, width, height, yuv);
+  if (!err)
+    locked_ = true;
+
+  return err;
+}
+
+int IonBuffer::Unlock() {
+  ATRACE_NAME("IonBuffer::Unlock");
+  ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle_);
+
+  // Lock may be called multiple times; but only one Unlock is required.
+  const int err = gralloc_module_->unlock(gralloc_module_, handle_);
+  if (!err)
+    locked_ = false;
+
+  return err;
+}
+
+void IonBuffer::GrallocInit() {
+  static std::once_flag gralloc_flag;
+  std::call_once(gralloc_flag, []() {
+    hw_module_t const* module = nullptr;
+    alloc_device_t* device = nullptr;
+
+    int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+    ALOGE_IF(err, "IonBuffer::GrallocInit: failed to find the %s module: %s",
+             GRALLOC_HARDWARE_MODULE_ID, strerror(-err));
+
+    err = gralloc_open(module, &device);
+    ALOGE_IF(err, "IonBuffer::GrallocInit: failed to open gralloc device: %s",
+             strerror(-err));
+
+    gralloc_module_ = reinterpret_cast<gralloc_module_t const*>(module);
+    gralloc_device_ = device;
+  });
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..33816fa
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/client/private/dvr/buffer_hub_client.h
@@ -0,0 +1,57 @@
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_  // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+// TODO(jwcai) mock not need for now
+class native_handle_t;
+
+namespace android {
+namespace dvr {
+
+// TODO(jwcai) mock not need for now
+class IonBuffer;
+
+class BufferHubBuffer {
+ public:
+  MOCK_METHOD1(Poll, int(int timeout_ms));
+  MOCK_METHOD6(Lock, bool(int usage, int x, int y, int width, int height,
+                          void** addr));
+  MOCK_METHOD0(Unlock, int());
+
+  MOCK_METHOD0(native_handle, native_handle_t*());
+  MOCK_METHOD0(buffer, IonBuffer*());
+  MOCK_METHOD0(event_fd, int());
+
+  MOCK_METHOD0(id, int());
+  MOCK_METHOD0(width, int());
+  MOCK_METHOD0(height, int());
+  MOCK_METHOD0(stride, int());
+  MOCK_METHOD0(format, int());
+  MOCK_METHOD0(usage, int());
+};
+
+class BufferProducer : public BufferHubBuffer {
+ public:
+  // Note that static method |CreateBuffer| and |Import| are not mocked
+  // here, they are just implementation details and thus not needed.
+  MOCK_METHOD2(Post, int(int ready_fence, uint64_t sequence));
+  MOCK_METHOD1(Gain, int(int* release_fence));
+
+  static BufferProducer* staticObject;
+};
+
+class BufferConsumer : public BufferHubBuffer {
+ public:
+  MOCK_METHOD2(Acquire, int(int* ready_fence, uint64_t* sequence));
+  MOCK_METHOD1(Release, int(int release_fence));
+  MOCK_METHOD0(Discard, int());
+  MOCK_METHOD3(DoAcquire,
+               int(int* ready_fence, void* meta, size_t meta_size_bytes));
+
+  static BufferConsumer* staticObject;
+};
+
+}  // namespace dvr
+}  // namespace android
+#endif  // LIB_LIBBUFFERHUB_PRIVATE_DVR_BUFFER_HUB_CLIENT_H_  //NOLINT
diff --git a/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
new file mode 100644
index 0000000..9674c7c
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/BUILD.gn
@@ -0,0 +1,23 @@
+config("gralloc_config") {
+  include_dirs = [ "." ]
+}
+
+static_library("gralloc") {
+  testonly = true
+
+  sources = [
+    "gralloc.cpp",
+    "gralloc.h",
+  ]
+
+  include_dirs = [
+    "//system/core/include",
+    "//hardware/libhardware/include",
+  ]
+
+  public_deps = [
+    "//dreamos/external/gmock",
+  ]
+
+  public_configs = [ ":gralloc_config" ]
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
new file mode 100644
index 0000000..4a923ec
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc.cpp
@@ -0,0 +1,68 @@
+#include <gralloc_mock.h>
+#include <hardware/gralloc.h>
+
+static alloc_device_t sdevice;
+
+static int local_registerBuffer(struct gralloc_module_t const*,
+                                buffer_handle_t handle) {
+  return GrallocMock::staticObject->registerBuffer(handle);
+}
+
+static int local_unregisterBuffer(struct gralloc_module_t const*,
+                                  buffer_handle_t handle) {
+  return GrallocMock::staticObject->unregisterBuffer(handle);
+}
+
+static int local_unlock(struct gralloc_module_t const*,
+                        buffer_handle_t handle) {
+  return GrallocMock::staticObject->unlock(handle);
+}
+
+static int local_lock(struct gralloc_module_t const*, buffer_handle_t handle,
+                      int usage, int l, int t, int w, int h, void** vaddr) {
+  return GrallocMock::staticObject->lock(handle, usage, l, t, w, h, vaddr);
+}
+
+static int local_alloc(struct alloc_device_t*, int w, int h, int format,
+                       int usage, buffer_handle_t* handle, int* stride) {
+  return GrallocMock::staticObject->alloc(w, h, format, usage, handle, stride);
+}
+
+static int local_free(struct alloc_device_t*, buffer_handle_t handle) {
+  return GrallocMock::staticObject->free(handle);
+}
+
+static int local_open(const struct hw_module_t*, const char*,
+                      struct hw_device_t** device) {
+  sdevice.alloc = local_alloc;
+  sdevice.free = local_free;
+  *device = reinterpret_cast<hw_device_t*>(&sdevice);
+  return 0;
+}
+
+static hw_module_methods_t smethods;
+
+static gralloc_module_t smodule;
+
+int hw_get_module(const char*, const struct hw_module_t** module) {
+  smodule.registerBuffer = local_registerBuffer;
+  smodule.unregisterBuffer = local_unregisterBuffer;
+  smodule.lock = local_lock;
+  smodule.unlock = local_unlock;
+  smethods.open = local_open;
+  smodule.common.methods = &smethods;
+  *module = reinterpret_cast<hw_module_t*>(&smodule);
+  return 0;
+}
+
+int native_handle_close(const native_handle_t* handle) {
+  return GrallocMock::staticObject->native_handle_close(handle);
+}
+
+int native_handle_delete(native_handle_t* handle) {
+  return GrallocMock::staticObject->native_handle_delete(handle);
+}
+
+native_handle_t* native_handle_create(int numFds, int numInts) {
+  return GrallocMock::staticObject->native_handle_create(numFds, numInts);
+}
diff --git a/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
new file mode 100644
index 0000000..f62f579
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/gralloc/gralloc_mock.h
@@ -0,0 +1,23 @@
+#ifndef LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#define LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hardware/gralloc.h>
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class GrallocMock {
+ public:
+  // Add methods here.
+  MOCK_METHOD1(native_handle_close, int(const native_handle_t*));
+  MOCK_METHOD1(native_handle_delete, int(native_handle_t*));
+  MOCK_METHOD2(native_handle_create, native_handle_t*(int, int));
+  MOCK_METHOD1(registerBuffer, int(buffer_handle_t));
+  MOCK_METHOD1(unregisterBuffer, int(buffer_handle_t));
+  MOCK_METHOD7(lock, int(buffer_handle_t, int, int, int, int, int, void**));
+  MOCK_METHOD1(unlock, int(buffer_handle_t));
+  MOCK_METHOD6(alloc, int(int, int, int, int, buffer_handle_t*, int*));
+  MOCK_METHOD1(free, int(buffer_handle_t));
+  static GrallocMock* staticObject;
+};
+
+#endif  // LIB_LIBBUFFERHUB_MOCKS_GRALLOC_GRALLOC_MOCK_H_
diff --git a/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
new file mode 100644
index 0000000..fac6db0
--- /dev/null
+++ b/libs/vr/libbufferhub/mocks/ion_buffer/private/dvr/ion_buffer.h
@@ -0,0 +1,78 @@
+// This file has a big hack, it "mocks" the actual IonBuffer by redefining
+// it with mock methods and using the same header guard to prevent the original
+// definition from being included in the same context.
+#ifndef LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_  // NOLINT
+#define LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <hardware/gralloc.h>
+
+namespace android {
+namespace dvr {
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBufferMock {
+ public:
+  IonBufferMock() {}
+  MOCK_METHOD0(GetGrallocModuleImpl, gralloc_module_t const*());
+  MOCK_METHOD6(Import, int(buffer_handle_t handle, int width, int height,
+                           int stride, int format, int usage));
+  MOCK_METHOD9(Import, int(const int* fd_array, int fd_count,
+                           const int* int_array, int int_count, int width,
+                           int height, int stride, int format, int usage));
+  MOCK_METHOD6(Lock, int(int usage, int x, int y, int width, int height,
+                         void** address));
+  MOCK_METHOD0(Unlock, int());
+  MOCK_CONST_METHOD0(handle, buffer_handle_t());
+  MOCK_CONST_METHOD0(width, int());
+  MOCK_CONST_METHOD0(height, int());
+  MOCK_CONST_METHOD0(layer_count, int());
+  MOCK_CONST_METHOD0(stride, int());
+  MOCK_CONST_METHOD0(layer_stride, int());
+  MOCK_CONST_METHOD0(format, int());
+  MOCK_CONST_METHOD0(usage, int());
+};
+
+// IonBuffer is an abstraction of Ion/Gralloc buffers.
+class IonBuffer {
+ public:
+  IonBuffer() : mock_(new IonBufferMock) {
+    if (initializer) {
+      initializer(mock_.get());
+    }
+  }
+  IonBuffer(IonBuffer&& other) = default;
+  static gralloc_module_t const* GetGrallocModule() {
+    return staticObject->GetGrallocModuleImpl();
+  }
+  int Import(buffer_handle_t handle, int width, int height, int stride,
+             int format, int usage) {
+    return mock_->Import(handle, width, height, stride, format, usage);
+  }
+  int Import(const int* fd_array, int fd_count, const int* int_array,
+             int int_count, int width, int height, int stride, int format,
+             int usage) {
+    return mock_->Import(fd_array, fd_count, int_array, int_count, width,
+                         height, stride, format, usage);
+  }
+  int Lock(int usage, int x, int y, int width, int height, void** address) {
+    return mock_->Lock(usage, x, y, width, height, address);
+  }
+  int Unlock() { return mock_->Unlock(); }
+  buffer_handle_t handle() const { return mock_->handle(); }
+  int width() const { return mock_->width(); }
+  int height() const { return mock_->height(); }
+  int layer_count() const { return mock_->layer_count(); }
+  int stride() const { return mock_->stride(); }
+  int layer_stride() const { return mock_->layer_stride(); }
+  int format() const { return mock_->format(); }
+  int usage() const { return mock_->usage(); }
+  std::unique_ptr<IonBufferMock> mock_;
+  static IonBufferMock* staticObject;
+  static void (*initializer)(IonBufferMock* target);
+};
+
+}  // namespace dvr
+}  // namespace android
+#endif  // LIB_LIBBUFFERHUB_PRIVATE_DVR_ION_BUFFER_H_ - NOLINT
diff --git a/libs/vr/libbufferhub/tests/Android.mk b/libs/vr/libbufferhub/tests/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/libs/vr/libbufferhub/tests/ion_buffer/Android.mk b/libs/vr/libbufferhub/tests/ion_buffer/Android.mk
new file mode 100644
index 0000000..3bfdb7b
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/ion_buffer/Android.mk
@@ -0,0 +1,74 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+COMPONENT_TOP := ${LOCAL_PATH}/../..
+
+LOCAL_SRC_FILES := \
+        ion_buffer-test.cpp \
+        ../../ion_buffer.cpp \
+        ../../mocks/gralloc/gralloc.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+        libc \
+        libcutils \
+        libutils \
+        liblog
+
+LOCAL_STATIC_LIBRARIES := \
+        libgmock
+
+LOCAL_C_INCLUDES := \
+        ${COMPONENT_TOP}/mocks/gralloc \
+        ${COMPONENT_TOP}/include \
+        $(TOP)/system/core/base/include
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+
+LOCAL_NATIVE_COVERAGE := true
+
+LOCAL_CFLAGS := -DTRACE=0 -g
+
+LOCAL_MODULE := ion_buffer-test
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        ion_buffer-test.cpp \
+        ../../ion_buffer.cpp \
+        ../../mocks/gralloc/gralloc.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+        liblog
+
+LOCAL_STATIC_LIBRARIES := \
+        libgmock_host
+
+LOCAL_C_INCLUDES := \
+        ${COMPONENT_TOP}/mocks/gralloc \
+        ${COMPONENT_TOP}/include \
+        $(TOP)/system/core/base/include
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+
+LOCAL_NATIVE_COVERAGE := true
+
+LOCAL_CFLAGS := -DTRACE=0
+
+LOCAL_MODULE := ion_buffer-host_test
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_HOST_NATIVE_TEST)
+
+.PHONY: dvr_host_native_unit_tests
+dvr_host_native_unit_tests: ion_buffer-host_test
+ifeq (true,$(NATIVE_COVERAGE))
+  ion_buffer-host_test: llvm-cov
+  ion_buffer-test: llvm-cov
+  # This shouldn't be necessary, but the default build with
+  # NATIVE_COVERAGE=true manages to ion_buffer-test without
+  # building llvm-cov (droid is the default target).
+  droid: llvm-cov
+endif
diff --git a/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp b/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp
new file mode 100644
index 0000000..68f82d7
--- /dev/null
+++ b/libs/vr/libbufferhub/tests/ion_buffer/ion_buffer-test.cpp
@@ -0,0 +1,375 @@
+#include <gmock/gmock.h>
+#include <gralloc_mock.h>
+#include <gtest/gtest.h>
+#include <private/dvr/ion_buffer.h>
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using android::dvr::IonBuffer;
+
+GrallocMock* GrallocMock::staticObject = nullptr;
+
+namespace {
+
+const int w1 = 100;
+const int h1 = 200;
+const int d1 = 2;
+const int f1 = 1;
+const int u1 = 3;
+const int stride1 = 8;
+const int layer_stride1 = 8;
+native_handle_t handle1;
+const int w2 = 150;
+const int h2 = 300;
+const int d2 = 4;
+const int f2 = 2;
+const int u2 = 5;
+const int stride2 = 4;
+const int layer_stride2 = 4;
+native_handle_t handle2;
+const int kMaxFd = 10;
+const int kMaxInt = 10;
+char handleData[sizeof(native_handle_t) + (kMaxFd + kMaxInt) * sizeof(int)];
+native_handle_t* const dataHandle =
+    reinterpret_cast<native_handle_t*>(handleData);
+char refData[sizeof(native_handle_t) + (kMaxFd + kMaxInt) * sizeof(int)];
+native_handle_t* const refHandle = reinterpret_cast<native_handle_t*>(refData);
+
+class IonBufferUnitTest : public ::testing::Test {
+ protected:
+  // You can remove any or all of the following functions if its body
+  // is empty.
+
+  IonBufferUnitTest() {
+    GrallocMock::staticObject = new GrallocMock;
+    // You can do set-up work for each test here.
+    // most ServicefsClients will use this initializer. Use as the
+    // default.
+  }
+
+  virtual ~IonBufferUnitTest() {
+    delete GrallocMock::staticObject;
+    GrallocMock::staticObject = nullptr;
+    // You can do clean-up work that doesn't throw exceptions here.
+  }
+
+  // If the constructor and destructor are not enough for setting up
+  // and cleaning up each test, you can define the following methods:
+
+  void SetUp() override {
+    // Code here will be called immediately after the constructor (right
+    // before each test).
+  }
+
+  void TearDown() override {
+    // Code here will be called immediately after each test (right
+    // before the destructor).
+  }
+};
+
+void TestIonBufferState(const IonBuffer& buffer, int w, int h, int d, int f,
+                        int u, native_handle_t* handle, int stride,
+                        int layer_stride) {
+  EXPECT_EQ(buffer.width(), w);
+  EXPECT_EQ(buffer.height(), h);
+  EXPECT_EQ(buffer.layer_count(), d);
+  EXPECT_EQ(buffer.format(), f);
+  EXPECT_EQ(buffer.usage(), u);
+  EXPECT_EQ(buffer.handle(), handle);
+  EXPECT_EQ(buffer.stride(), stride);
+  EXPECT_EQ(buffer.layer_stride(), layer_stride);
+}
+
+TEST_F(IonBufferUnitTest, TestFreeOnDestruction) {
+  // Set up |alloc|(|w1...|) to succeed once and fail on others calls.
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(1)
+      .WillOnce(DoAll(SetArgPointee<4>(&handle1), SetArgPointee<5>(stride1),
+                      Return(0)));
+  // Set up |free| to be called once.
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  IonBuffer buffer;
+  // First call to |alloc| with |w1...| set up to succeed.
+  int ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+
+  // Scoped destructor will be called, calling |free| on |handle1|.
+}
+
+TEST_F(IonBufferUnitTest, TestAlloc) {
+  IonBuffer buffer;
+  // Set up |alloc|(|w1...|) to succeed once and fail on others calls.
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(2)
+      .WillOnce(DoAll(SetArgPointee<4>(&handle1), SetArgPointee<5>(stride1),
+                      Return(0)))
+      .WillRepeatedly(Return(-1));
+
+  // Set up |alloc|(|w2...|)  to succeed once and fail on others calls.
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w2, h2, f2, u2, _, _))
+      .Times(2)
+      .WillOnce(DoAll(SetArgPointee<4>(&handle2), SetArgPointee<5>(stride2),
+                      Return(0)))
+      .WillRepeatedly(Return(-1));
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle2))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  // First call to |alloc| with |w1...| set up to succeed.
+  int ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+
+  // First call to |alloc| with |w2...| set up to succeed, |free| should be
+  // called once on |handle1|.
+  ret = buffer.Alloc(w2, h2, f2, u2);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+
+  // Second call to |alloc| with |w1| is set up to fail.
+  ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+
+  // |free| on |handle2| should be called here.
+  buffer.FreeHandle();
+  TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+
+  // |alloc| is set up to fail.
+  ret = buffer.Alloc(w2, h2, f2, u2);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+}
+
+TEST_F(IonBufferUnitTest, TestReset) {
+  IonBuffer buffer;
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle2))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+  buffer.Reset(&handle1, w1, h1, stride1, f1, u1);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+  buffer.Reset(&handle2, w2, h2, stride2, f2, u2);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+  buffer.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestImport1) {
+  IonBuffer buffer;
+  EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(&handle1))
+      .Times(3)
+      .WillOnce(Return(0))
+      .WillRepeatedly(Return(-1));
+  EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(&handle2))
+      .Times(3)
+      .WillOnce(Return(0))
+      .WillOnce(Return(-1))
+      .WillOnce(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(&handle1))
+      .Times(1)
+      .WillOnce(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(&handle1))
+      .Times(1);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(&handle1))
+      .Times(1);
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(1)
+      .WillRepeatedly(DoAll(SetArgPointee<4>(&handle1),
+                            SetArgPointee<5>(stride1), Return(0)));
+  EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(&handle2))
+      .Times(2)
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(&handle2))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(&handle2))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  int ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+  ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+  ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, &handle1, stride1, 0);
+  ret = buffer.Import(&handle2, w2, h2, stride2, f2, u2);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+  ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, w2, h2, 1, f2, u2, &handle2, stride2, 0);
+  buffer.FreeHandle();
+  ret = buffer.Import(&handle1, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  TestIonBufferState(buffer, 0, 0, 0, 0, 0, nullptr, 0, 0);
+}
+
+native_handle_t* native_handle_create_impl(int nFds, int nInts) {
+  if ((nFds + nInts) > (kMaxFd + kMaxInt))
+    return nullptr;
+  dataHandle->version = sizeof(native_handle_t);
+  dataHandle->numFds = nFds;
+  dataHandle->numInts = nInts;
+  for (int i = 0; i < nFds + nInts; i++)
+    dataHandle->data[i] = 0;
+  return dataHandle;
+}
+
+TEST_F(IonBufferUnitTest, TestImport2) {
+  IonBuffer buffer;
+  int ints[] = {211, 313, 444};
+  int fds[] = {-1, -1};
+  int ni = sizeof(ints) / sizeof(ints[0]);
+  int nfd = sizeof(fds) / sizeof(fds[0]);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_create(nfd, ni))
+      .Times(3)
+      .WillOnce(Return(nullptr))
+      .WillRepeatedly(Invoke(native_handle_create_impl));
+  EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(dataHandle))
+      .Times(2)
+      .WillOnce(Return(-1))
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(dataHandle))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(dataHandle))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(dataHandle))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  int ret = buffer.Import(fds, -1, ints, ni, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Import(fds, nfd, ints, -1, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Import(fds, nfd, ints, ni, w1, h1, stride1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, dataHandle, stride1, 0);
+  EXPECT_EQ(dataHandle->numFds, nfd);
+  EXPECT_EQ(dataHandle->numInts, ni);
+  for (int i = 0; i < nfd; i++)
+    EXPECT_EQ(dataHandle->data[i], fds[i]);
+  for (int i = 0; i < ni; i++)
+    EXPECT_EQ(dataHandle->data[nfd + i], ints[i]);
+  buffer.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestDuplicate) {
+  IonBuffer buffer;
+  IonBuffer ref;
+  int ints[] = {211, 313, 444};
+  int fds[] = {-1, -1};
+  int ni = sizeof(ints) / sizeof(ints[0]);
+  int nfd = sizeof(fds) / sizeof(fds[0]);
+
+  for (int i = 0; i < nfd; i++) {
+    refHandle->data[i] = fds[i];
+  }
+  for (int i = 0; i < ni; i++) {
+    refHandle->data[i + nfd] = ints[i];
+  }
+
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(1)
+      .WillRepeatedly(DoAll(SetArgPointee<4>(refHandle),
+                            SetArgPointee<5>(stride1), Return(0)));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_create(nfd, ni))
+      .Times(3)
+      .WillOnce(Return(nullptr))
+      .WillRepeatedly(Invoke(native_handle_create_impl));
+  EXPECT_CALL(*GrallocMock::staticObject, registerBuffer(dataHandle))
+      .Times(2)
+      .WillOnce(Return(-1))
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_close(dataHandle))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, native_handle_delete(dataHandle))
+      .Times(2);
+  EXPECT_CALL(*GrallocMock::staticObject, unregisterBuffer(dataHandle))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+  EXPECT_CALL(*GrallocMock::staticObject, free(refHandle))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  int ret = buffer.Duplicate(&ref);
+  EXPECT_LT(ret, 0);
+  ret = ref.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  refHandle->numFds = -1;
+  refHandle->numInts = 0;
+  ret = buffer.Duplicate(&ref);
+  EXPECT_LT(ret, 0);
+  refHandle->numFds = nfd;
+  refHandle->numInts = ni;
+  ret = buffer.Duplicate(&ref);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Duplicate(&ref);
+  EXPECT_LT(ret, 0);
+  ret = buffer.Duplicate(&ref);
+  EXPECT_EQ(ret, 0);
+  TestIonBufferState(buffer, w1, h1, 1, f1, u1, dataHandle, stride1, 0);
+  EXPECT_EQ(dataHandle->numFds, nfd);
+  EXPECT_EQ(dataHandle->numInts, ni);
+  for (int i = 0; i < nfd; i++)
+    EXPECT_LT(dataHandle->data[i], 0);
+  for (int i = 0; i < ni; i++)
+    EXPECT_EQ(dataHandle->data[nfd + i], ints[i]);
+  buffer.FreeHandle();
+  ref.FreeHandle();
+}
+
+TEST_F(IonBufferUnitTest, TestLockUnlock) {
+  IonBuffer buffer;
+  const int x = 12;
+  const int y = 24;
+  const int value1 = 17;
+  const int value2 = 25;
+  void* addr1;
+  void** addr = &addr1;
+
+  EXPECT_CALL(*GrallocMock::staticObject, alloc(w1, h1, f1, u1, _, _))
+      .Times(1)
+      .WillRepeatedly(DoAll(SetArgPointee<4>(&handle1),
+                            SetArgPointee<5>(stride1), Return(0)));
+  EXPECT_CALL(*GrallocMock::staticObject,
+              lock(&handle1, u2, x, y, w2, h2, addr))
+      .Times(1)
+      .WillRepeatedly(Return(value1));
+  EXPECT_CALL(*GrallocMock::staticObject, unlock(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(value2));
+  EXPECT_CALL(*GrallocMock::staticObject, free(&handle1))
+      .Times(1)
+      .WillRepeatedly(Return(0));
+
+  int ret = buffer.Alloc(w1, h1, f1, u1);
+  EXPECT_EQ(ret, 0);
+  ret = buffer.Lock(u2, x, y, w2, h2, addr);
+  EXPECT_EQ(ret, value1);
+  ret = buffer.Unlock();
+  EXPECT_EQ(ret, value2);
+  buffer.FreeHandle();
+}
+
+}  // namespace
diff --git a/libs/vr/libbufferhubqueue/Android.mk b/libs/vr/libbufferhubqueue/Android.mk
new file mode 100644
index 0000000..46b83e7
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/Android.mk
@@ -0,0 +1,50 @@
+# 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_queue_client.cpp \
+	buffer_hub_queue_core.cpp \
+	buffer_hub_queue_consumer.cpp \
+	buffer_hub_queue_producer.cpp \
+
+includeFiles := \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	libbufferhub \
+	libchrome \
+	libdvrcommon \
+	libpdx_default_transport \
+
+sharedLibraries := \
+	libbase \
+	libbinder \
+	libcutils \
+	libhardware \
+	liblog \
+	libui \
+	libutils \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libbufferhubqueue
+include $(BUILD_STATIC_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
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..4fbfcf6
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -0,0 +1,414 @@
+#include "include/private/dvr/buffer_hub_queue_client.h"
+
+#include <base/logging.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::LocalHandle;
+using android::pdx::LocalChannelHandle;
+
+namespace android {
+namespace dvr {
+
+BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle,
+                               size_t meta_size)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      meta_size_(meta_size),
+      meta_buffer_tmp_(meta_size ? new uint8_t[meta_size] : nullptr),
+      buffers_(BufferHubQueue::kMaxQueueCapacity),
+      available_buffers_(BufferHubQueue::kMaxQueueCapacity),
+      capacity_(0) {
+  Initialize();
+}
+
+BufferHubQueue::BufferHubQueue(const std::string& endpoint_path,
+                               size_t meta_size)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+          endpoint_path)},
+      meta_size_(meta_size),
+      meta_buffer_tmp_(meta_size ? new uint8_t[meta_size] : nullptr),
+      buffers_(BufferHubQueue::kMaxQueueCapacity),
+      available_buffers_(BufferHubQueue::kMaxQueueCapacity),
+      capacity_(0) {
+  Initialize();
+}
+
+void BufferHubQueue::Initialize() {
+  int ret = epoll_fd_.Create();
+  if (ret < 0) {
+    LOG(ERROR) << "BufferHubQueue::BufferHubQueue: Failed to create epoll fd:"
+               << 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) {
+    LOG(ERROR) << "Failed to register ConsumerQueue into epoll event: "
+               << strerror(-ret);
+  }
+}
+
+std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
+  Status<std::pair<LocalChannelHandle, size_t>> status =
+      InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>();
+
+  if (!status) {
+    LOG(ERROR) << "Cannot create ConsumerQueue: " << status.GetErrorMessage();
+    return nullptr;
+  }
+
+  auto return_value = status.take();
+
+  VLOG(1) << "CreateConsumerQueue: meta_size_bytes=" << return_value.second;
+  return ConsumerQueue::Create(std::move(return_value.first),
+                               return_value.second);
+}
+
+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) {
+      VLOG(1) << "Wait on epoll returns nothing before timeout.";
+      return false;
+    }
+
+    if (ret < 0 && ret != -EINTR) {
+      LOG(ERROR) << "Failed to wait for buffers:" << 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);
+
+      VLOG(1) << "New BufferHubQueue event " << i << ": index=" << index;
+
+      if (is_buffer_event_index(index) && (events[i].events & EPOLLIN)) {
+        auto buffer = buffers_[index];
+        ret = OnBufferReady(buffer);
+        if (ret < 0) {
+          LOG(ERROR) << "Failed to set buffer ready:" << strerror(-ret);
+          continue;
+        }
+        Enqueue(buffer, index);
+      } else if (is_buffer_event_index(index) &&
+                 (events[i].events & EPOLLHUP)) {
+        // This maybe caused by producer replacing an exising buffer slot.
+        // Currently the epoll FD is cleaned up when the replacement consumer
+        // client is imported.
+        LOG(WARNING) << "Receives EPOLLHUP at slot: " << index;
+      } else if (is_queue_event_index(index) && (events[i].events & EPOLLIN)) {
+        // Note that after buffer imports, if |count()| still returns 0, epoll
+        // wait will be tried again to acquire the newly imported buffer.
+        ret = OnBufferAllocated();
+        if (ret < 0) {
+          LOG(ERROR) << "Failed to import buffer:" << strerror(-ret);
+          continue;
+        }
+      } else {
+        LOG(WARNING) << "Unknown event " << i << ": u64=" << index
+                     << ": events=" << events[i].events;
+      }
+    }
+  }
+
+  return true;
+}
+
+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.
+    LOG(ERROR) << "BufferHubQueue::AddBuffer queue is at maximum capacity: "
+               << 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 and set up with the new one.
+    DetachBuffer(slot);
+  }
+
+  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) {
+    LOG(ERROR)
+        << "BufferHubQueue::AddBuffer: Failed to add buffer to epoll set:"
+        << strerror(-ret);
+    return ret;
+  }
+
+  buffers_[slot] = buf;
+  capacity_++;
+  return 0;
+}
+
+int BufferHubQueue::DetachBuffer(size_t slot) {
+  auto& buf = buffers_[slot];
+  if (buf == nullptr) {
+    LOG(ERROR) << "BufferHubQueue::DetachBuffer: Invalid slot: " << slot;
+    return -EINVAL;
+  }
+
+  const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, buf->event_fd(), nullptr);
+  if (ret < 0) {
+    LOG(ERROR) << "BufferHubQueue::DetachBuffer: Failed to detach buffer from  "
+                  "epoll set:"
+               << strerror(-ret);
+    return ret;
+  }
+
+  buffers_[slot] = nullptr;
+  capacity_--;
+  return 0;
+}
+
+void BufferHubQueue::Enqueue(std::shared_ptr<BufferHubBuffer> buf,
+                             size_t slot) {
+  if (count() == capacity_) {
+    LOG(ERROR) << "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) {
+  VLOG(1) << "Dequeue: count=" << count() << ", timeout=" << timeout;
+
+  if (count() == 0 && !WaitForBuffers(timeout))
+    return nullptr;
+
+  std::shared_ptr<BufferHubBuffer> buf;
+  BufferInfo& buffer_info = available_buffers_.Front();
+
+  // 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) {
+    LOG(ERROR) << "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) {}
+
+ProducerQueue::ProducerQueue(LocalChannelHandle handle, size_t meta_size)
+    : BASE(std::move(handle), meta_size) {}
+
+ProducerQueue::ProducerQueue(size_t meta_size, int usage_set_mask,
+                             int usage_clear_mask, int usage_deny_set_mask,
+                             int usage_deny_clear_mask)
+    : BASE(BufferHubRPC::kClientPath, meta_size) {
+  auto status = InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+      meta_size_, usage_set_mask, usage_clear_mask, usage_deny_set_mask,
+      usage_deny_clear_mask);
+  if (!status) {
+    LOG(ERROR)
+        << "ProducerQueue::ProducerQueue: Failed to create producer queue: %s"
+        << status.GetErrorMessage();
+    Close(-status.error());
+    return;
+  }
+}
+
+int ProducerQueue::AllocateBuffer(int width, int height, int format, int usage,
+                                  size_t slice_count, size_t* out_slot) {
+  if (out_slot == nullptr) {
+    LOG(ERROR) << "Parameter out_slot cannot be null.";
+    return -EINVAL;
+  }
+
+  if (is_full()) {
+    LOG(ERROR) << "ProducerQueue::AllocateBuffer queue is at maximum capacity: "
+               << capacity();
+    return -E2BIG;
+  }
+
+  const size_t kBufferCount = 1U;
+
+  Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
+          width, height, format, usage, slice_count, kBufferCount);
+  if (!status) {
+    LOG(ERROR) << "ProducerQueue::AllocateBuffer failed to create producer "
+                  "buffer through BufferHub.";
+    return -status.error();
+  }
+
+  auto buffer_handle_slots = status.take();
+  CHECK_EQ(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;
+  VLOG(1) << "ProducerQueue::AllocateBuffer, new buffer, channel_handle: "
+          << 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) {
+  Status<int> status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(slot);
+  if (!status) {
+    LOG(ERROR) << "ProducerQueue::DetachBuffer failed to detach producer "
+                  "buffer through BufferHub, error: "
+               << status.GetErrorMessage();
+    return -status.error();
+  }
+
+  return BufferHubQueue::DetachBuffer(slot);
+}
+
+std::shared_ptr<BufferProducer> ProducerQueue::Dequeue(int timeout,
+                                                       size_t* slot) {
+  auto buf = BufferHubQueue::Dequeue(timeout, slot, nullptr);
+  return std::static_pointer_cast<BufferProducer>(buf);
+}
+
+int ProducerQueue::OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) {
+  auto buffer = std::static_pointer_cast<BufferProducer>(buf);
+  return buffer->GainAsync();
+}
+
+ConsumerQueue::ConsumerQueue(LocalChannelHandle handle, size_t meta_size)
+    : BASE(std::move(handle), meta_size) {
+  // TODO(b/34387835) Import consumer queue in case the ProducerQueue we are
+  // based on was not empty.
+}
+
+int ConsumerQueue::ImportBuffers() {
+  Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
+      InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
+  if (!status) {
+    LOG(ERROR) << "ConsumerQueue::ImportBuffers failed to import consumer "
+                  "buffer through BufferBub, error: "
+               << status.GetErrorMessage();
+    return -status.error();
+  }
+
+  int last_error = 0;
+  int imported_buffers = 0;
+
+  auto buffer_handle_slots = status.take();
+  for (auto& buffer_handle_slot : buffer_handle_slots) {
+    VLOG(1) << "ConsumerQueue::ImportBuffers, new buffer, buffer_handle: "
+            << 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) {
+      LOG(ERROR) << "ConsumerQueue::ImportBuffers failed to add buffer, ret: "
+                 << strerror(-ret);
+      last_error = ret;
+      continue;
+    } else {
+      imported_buffers++;
+    }
+  }
+
+  return imported_buffers > 0 ? imported_buffers : 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) {
+  if (meta_size != meta_size_) {
+    LOG(ERROR) << "metadata size (" << meta_size
+               << ") for the dequeuing buffer does not match metadata size ("
+               << meta_size_ << ") for the queue.";
+    return nullptr;
+  }
+  auto buf = BufferHubQueue::Dequeue(timeout, slot, meta);
+  return std::static_pointer_cast<BufferConsumer>(buf);
+}
+
+int ConsumerQueue::OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) {
+  auto buffer = std::static_pointer_cast<BufferConsumer>(buf);
+  LocalHandle fence;
+  return buffer->Acquire(&fence, meta_buffer_tmp_.get(), meta_size_);
+}
+
+int ConsumerQueue::OnBufferAllocated() {
+  const int ret = ImportBuffers();
+  if (ret == 0) {
+    LOG(WARNING) << "No new buffer can be imported on buffer allocated event.";
+  } else if (ret < 0) {
+    LOG(ERROR) << "Failed to import buffers on buffer allocated event.";
+  }
+  VLOG(1) << "Imported " << ret << " consumer buffers.";
+  return ret;
+}
+
+}  // 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..3fc0600
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
@@ -0,0 +1,93 @@
+#include "include/private/dvr/buffer_hub_queue_core.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)) {
+    LOG(ERROR)
+        << "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) {
+    LOG(ERROR) << "Failed to allocate new buffer in BufferHub.";
+    return NO_MEMORY;
+  }
+
+  auto buffer_producer = producer_->GetBuffer(slot);
+
+  CHECK(buffer_producer != nullptr) << "Failed to get buffer producer at slot: "
+                                    << slot;
+
+  // Allocating a new buffer, |buffers_[slot]| should be in initial state.
+  CHECK(buffers_[slot].mGraphicBuffer == nullptr) << "AllocateBuffer: slot "
+                                                  << slot << " is not empty.";
+
+  // Create new GraphicBuffer based on the newly created |buffer_producer|. Here
+  // we have to cast |buffer_handle_t| to |native_handle_t|, it's OK because
+  // internally, GraphicBuffer is still an |ANativeWindowBuffer| and |handle|
+  // is still type of |buffer_handle_t| and bears const property.
+  sp<GraphicBuffer> graphic_buffer(new GraphicBuffer(
+      buffer_producer->width(), buffer_producer->height(),
+      buffer_producer->format(),
+      1, /* layer count */
+      buffer_producer->usage(),
+      buffer_producer->stride(),
+      const_cast<native_handle_t*>(buffer_producer->buffer()->handle()),
+      false));
+
+  CHECK_EQ(NO_ERROR, graphic_buffer->initCheck())
+      << "Failed to init GraphicBuffer.";
+  buffers_[slot].mBufferProducer = buffer_producer;
+  buffers_[slot].mGraphicBuffer = graphic_buffer;
+
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueCore::DetachBuffer(size_t slot) {
+  // Detach the buffer producer via BufferHubRPC.
+  int ret = producer_->DetachBuffer(slot);
+  if (ret < 0) {
+    LOG(ERROR) << "BufferHubQueueCore::DetachBuffer failed through RPC, ret="
+               << 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..93d7307
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -0,0 +1,390 @@
+#include "include/private/dvr/buffer_hub_queue_producer.h"
+
+namespace android {
+namespace dvr {
+
+BufferHubQueueProducer::BufferHubQueueProducer(
+    const std::shared_ptr<BufferHubQueueCore>& core)
+    : core_(core), req_buffer_count_(kInvalidBufferCount) {}
+
+status_t BufferHubQueueProducer::requestBuffer(int slot,
+                                               sp<GraphicBuffer>* buf) {
+  VLOG(1) << "requestBuffer: slot=" << slot;;
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (slot < 0 || slot >= req_buffer_count_) {
+    LOG(ERROR) << "requestBuffer: slot index " << slot << " out of range [0, "
+               << req_buffer_count_ << ")";
+    return BAD_VALUE;
+  } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    LOG(ERROR) << "requestBuffer: slot " << slot
+               << " is not owned by the producer (state = "
+               << core_->buffers_[slot].mBufferState.string() << " )";
+    return BAD_VALUE;
+  }
+
+  core_->buffers_[slot].mRequestBufferCalled = true;
+  *buf = core_->buffers_[slot].mGraphicBuffer;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setMaxDequeuedBufferCount(
+    int max_dequeued_buffers) {
+  VLOG(1) << "setMaxDequeuedBufferCount: max_dequeued_buffers="
+          << max_dequeued_buffers;
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (max_dequeued_buffers <= 0 ||
+      max_dequeued_buffers >
+          static_cast<int>(BufferHubQueue::kMaxQueueCapacity)) {
+    LOG(ERROR) << "setMaxDequeuedBufferCount: " << max_dequeued_buffers
+               << " out of range (0, " << BufferHubQueue::kMaxQueueCapacity
+               << "]";
+    return BAD_VALUE;
+  }
+
+  req_buffer_count_ = max_dequeued_buffers;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setAsyncMode(bool /* async */) {
+  LOG(ERROR) << "BufferHubQueueProducer::setAsyncMode not implemented.";
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::dequeueBuffer(int* out_slot,
+                                               sp<Fence>* out_fence,
+                                               uint32_t width, uint32_t height,
+                                               PixelFormat format,
+                                               uint32_t usage,
+                                               FrameEventHistoryDelta* /* outTimestamps */) {
+  VLOG(1) << "dequeueBuffer: w=" << width << ", h=" << height
+          << " format=" << format << ", usage=" << usage;
+
+  status_t ret;
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (static_cast<int32_t>(core_->producer_->capacity()) < req_buffer_count_) {
+    // Lazy allocation. When the capacity of |core_->producer_| has not reach
+    // |req_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++) {
+    buffer_producer =
+        core_->producer_->Dequeue(core_->dequeue_timeout_ms_, &slot);
+    if (!buffer_producer)
+      return NO_MEMORY;
+
+    if (static_cast<int>(width) == buffer_producer->width() &&
+        static_cast<int>(height) == buffer_producer->height() &&
+        static_cast<int>(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.
+    LOG(INFO) << "dequeueBuffer,: requested buffer (w=" << width
+              << ", h=" << height << ", format=" << format
+              << ") is different from the buffer returned at slot: " << slot
+              << " (w=" << buffer_producer->width()
+              << ", h=" << buffer_producer->height()
+              << ", format=" << buffer_producer->format()
+              << "). Need re-allocattion.";
+    // 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.
+  CHECK(core_->buffers_[slot].mBufferState.isFree() ||
+        core_->buffers_[slot].mBufferState.isQueued())
+      << "dequeueBuffer: slot " << slot << " is not free or queued.";
+
+  core_->buffers_[slot].mBufferState.freeQueued();
+  core_->buffers_[slot].mBufferState.dequeue();
+  VLOG(1) << "dequeueBuffer: slot=" << 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 */) {
+  LOG(ERROR) << "BufferHubQueueProducer::detachBuffer not implemented.";
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::detachNextBuffer(
+    sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */) {
+  LOG(ERROR) << "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(FATAL) << "BufferHubQueueProducer::attachBuffer not supported.";
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::queueBuffer(int slot,
+                                             const QueueBufferInput& input,
+                                             QueueBufferOutput* /* output */) {
+  VLOG(1) << "queueBuffer: slot " << slot;
+
+  int64_t timestamp;
+  sp<Fence> fence;
+
+  // TODO(jwcai) The following attributes are ignored.
+  bool is_auto_timestamp;
+  android_dataspace data_space;
+  Rect crop(Rect::EMPTY_RECT);
+  int scaling_mode;
+  uint32_t transform;
+
+  input.deflate(&timestamp, &is_auto_timestamp, &data_space, &crop,
+                &scaling_mode, &transform, &fence);
+
+  if (fence == nullptr) {
+    LOG(ERROR) << "queueBuffer: fence is NULL";
+    return BAD_VALUE;
+  }
+
+  status_t ret;
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (slot < 0 || slot >= req_buffer_count_) {
+    LOG(ERROR) << "queueBuffer: slot index " << slot << " out of range [0, "
+               << req_buffer_count_ << ")";
+    return BAD_VALUE;
+  } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    LOG(ERROR) << "queueBuffer: slot " << slot
+               << " is not owned by the producer (state = "
+               << core_->buffers_[slot].mBufferState.string() << " )";
+    return BAD_VALUE;
+  }
+
+  // Post the buffer producer with timestamp in the metadata.
+  auto buffer_producer = core_->buffers_[slot].mBufferProducer;
+  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();
+
+  // TODO(jwcai) check how to fill in output properly.
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::cancelBuffer(int slot,
+                                              const sp<Fence>& fence) {
+  VLOG(1) << (__FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (slot < 0 || slot >= req_buffer_count_) {
+    LOG(ERROR) << "cancelBuffer: slot index " << slot << " out of range [0, "
+               << req_buffer_count_ << ")";
+    return BAD_VALUE;
+  } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    LOG(ERROR) << "cancelBuffer: slot " << slot
+               << " is not owned by the producer (state = "
+               << core_->buffers_[slot].mBufferState.string() << " )";
+    return BAD_VALUE;
+  } else if (fence == NULL) {
+    LOG(ERROR) << "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;
+  VLOG(1) << "cancelBuffer: slot " << slot;
+
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::query(int what, int* out_value) {
+  VLOG(1) << (__FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (out_value == NULL) {
+    LOG(ERROR) << "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;
+    // 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_WIDTH:
+    case NATIVE_WINDOW_HEIGHT:
+    case NATIVE_WINDOW_FORMAT:
+    case NATIVE_WINDOW_STICKY_TRANSFORM:
+    case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+    case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+    case NATIVE_WINDOW_DEFAULT_DATASPACE:
+    default:
+      return BAD_VALUE;
+  }
+
+  VLOG(1) << "query: key=" << what << ", v=" << 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. Hence |connect| is a NO-OP.
+  VLOG(1) << (__FUNCTION__);
+  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. Hence |disconnect| is a NO-OP.
+  VLOG(1) << (__FUNCTION__);
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setSidebandStream(
+    const sp<NativeHandle>& stream) {
+  if (stream != NULL) {
+    // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
+    // metadata.
+    LOG(ERROR) << "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
+  // |req_buffer_count_|).
+  LOG(ERROR) << "BufferHubQueueProducer::allocateBuffers not implemented.";
+}
+
+status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) {
+  LOG(ERROR) << "BufferHubQueueProducer::allowAllocation not implemented.";
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setGenerationNumber(
+    uint32_t generation_number) {
+  VLOG(1) << (__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.
+  LOG(ERROR) << "BufferHubQueueProducer::getConsumerName not supported.";
+  return String8("BufferHubQueue::DummyConsumer");
+}
+
+status_t BufferHubQueueProducer::setSharedBufferMode(
+    bool /* shared_buffer_mode */) {
+  LOG(ERROR) << "BufferHubQueueProducer::setSharedBufferMode not implemented.";
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setAutoRefresh(bool /* auto_refresh */) {
+  LOG(ERROR) << "BufferHubQueueProducer::setAutoRefresh not implemented.";
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) {
+  VLOG(1) << (__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]) {
+  LOG(ERROR) << "BufferHubQueueProducer::getLastQueuedBuffer not implemented.";
+  return INVALID_OPERATION;
+}
+
+void BufferHubQueueProducer::getFrameTimestamps(
+    FrameEventHistoryDelta* /*outDelta*/) {
+  LOG(ERROR) << "BufferHubQueueProducer::getFrameTimestamps not implemented.";
+}
+
+status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const {
+  VLOG(1) << (__FUNCTION__);
+
+  *out_id = core_->unique_id_;
+  return NO_ERROR;
+}
+
+IBinder* BufferHubQueueProducer::onAsBinder() {
+  // BufferHubQueueProducer is a non-binder implementation of
+  // IGraphicBufferProducer.
+  LOG(WARNING) << "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..83e77d4
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -0,0 +1,311 @@
+#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 <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 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 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];
+  }
+
+  // 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;
+
+ protected:
+  BufferHubQueue(LocalChannelHandle channel, size_t meta_size);
+  BufferHubQueue(const std::string& endpoint_path, size_t meta_size);
+
+  // 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);
+
+  // Wait for buffers to be released and re-add them to the queue.
+  bool WaitForBuffers(int timeout);
+  virtual int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) = 0;
+
+  // Called when a buffer is allocated remotely.
+  virtual int OnBufferAllocated() = 0;
+
+  // 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;
+  };
+
+  // Buffer queue:
+  // |buffers_| tracks all |BufferHubBuffer|s created by this |BufferHubQueue|.
+  std::vector<std::shared_ptr<BufferHubBuffer>> buffers_;
+
+  // |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_;
+
+  // 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_;
+
+  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(int usage_set_mask,
+                                               int usage_clear_mask,
+                                               int usage_deny_set_mask,
+                                               int usage_deny_clear_mask) {
+    return BASE::Create(sizeof(Meta), usage_set_mask, usage_clear_mask,
+                        usage_deny_set_mask, usage_deny_clear_mask);
+  }
+
+  // Import a |ProducerQueue| from a channel handle.
+  template <typename Meta>
+  static std::unique_ptr<ProducerQueue> Import(LocalChannelHandle handle) {
+    return BASE::Create(std::move(handle), sizeof(Meta));
+  }
+
+  // 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(int width, int height, int format, int usage,
+                     size_t buffer_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);
+
+ 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, size_t meta_size);
+  ProducerQueue(size_t meta_size, int usage_set_mask, int usage_clear_mask,
+                int usage_deny_set_mask, int usage_deny_clear_mask);
+
+  int OnBufferReady(std::shared_ptr<BufferHubBuffer> buf) override;
+
+  // Producer buffer is always allocated from the client (i.e. local) side.
+  int OnBufferAllocated() override { return 0; }
+};
+
+class ConsumerQueue : public pdx::ClientBase<ConsumerQueue, 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 newly created buffers from the service side.
+  // Returns number of buffers successfully imported; or negative error code
+  // when buffer import fails.
+  int 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) {
+    return Dequeue(timeout, slot, meta, sizeof(*meta));
+  }
+
+  std::shared_ptr<BufferConsumer> Dequeue(int timeout, size_t* slot, void* meta,
+                                          size_t meta_size);
+
+ private:
+  friend BASE;
+
+  ConsumerQueue(LocalChannelHandle handle, size_t meta_size);
+
+  // 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) override;
+
+  int OnBufferAllocated() override;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#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..ba0c0c5
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_core.h
@@ -0,0 +1,116 @@
+#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:
+  // 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_;
+
+  // |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..5b1a7e0
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_producer.h
@@ -0,0 +1,118 @@
+#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;
+
+  static constexpr int kInvalidBufferCount = -1;
+
+  // |core_| holds the actually buffer slots.
+  std::shared_ptr<BufferHubQueueCore> core_;
+
+  // |req_buffer_count_| sets the capacity of the underlying buffer queue.
+  int32_t req_buffer_count_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_PRODUCER_H_
diff --git a/libs/vr/libbufferhubqueue/tests/Android.mk b/libs/vr/libbufferhubqueue/tests/Android.mk
new file mode 100644
index 0000000..59061e6
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/Android.mk
@@ -0,0 +1,38 @@
+LOCAL_PATH := $(call my-dir)
+
+shared_libraries := \
+	libbase \
+	libbinder \
+	libcutils \
+	libgui \
+	liblog \
+	libhardware \
+	libui \
+	libutils \
+
+static_libraries := \
+	libbufferhubqueue \
+	libbufferhub \
+	libchrome \
+	libdvrcommon \
+	libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := buffer_hub_queue-test.cpp
+LOCAL_STATIC_LIBRARIES := $(static_libraries)
+LOCAL_SHARED_LIBRARIES := $(shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+LOCAL_CFLAGS := -DTRACE=0 -O0 -g
+LOCAL_MODULE := buffer_hub_queue-test
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := buffer_hub_queue_producer-test.cpp
+LOCAL_STATIC_LIBRARIES := $(static_libraries)
+LOCAL_SHARED_LIBRARIES := $(shared_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := ${LOCAL_C_INCLUDES}
+LOCAL_CFLAGS := -DTRACE=0 -O0 -g
+LOCAL_MODULE := buffer_hub_queue_producer-test
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_NATIVE_TEST)
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..841554e
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -0,0 +1,292 @@
+#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>
+  void 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);
+    ASSERT_NE(nullptr, producer_queue_);
+
+    consumer_queue_ = producer_queue_->CreateConsumerQueue();
+    ASSERT_NE(nullptr, consumer_queue_);
+  }
+
+  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;
+
+  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;
+    auto p1 = producer_queue_->Dequeue(0, &slot);
+    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);
+    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;
+
+  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|.
+    auto consumer_null = consumer_queue_->Dequeue(0, &slot, &seq);
+    ASSERT_EQ(nullptr, consumer_null);
+    ASSERT_EQ(consumer_queue_->capacity(), i + 1);
+  }
+
+  for (size_t i = 0; i < nb_buffer; i++) {
+    // First time, there is no buffer available to dequeue.
+    auto buffer_null = consumer_queue_->Dequeue(0, &slot, &seq);
+    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);
+    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);
+    ASSERT_NE(nullptr, consumer);
+    ASSERT_EQ(seq_in, seq_out);
+  }
+}
+
+struct TestMetadata {
+  char a;
+  int32_t b;
+  int64_t c;
+};
+
+TEST_F(BufferHubQueueTest, TestMetadata) {
+  CreateQueues<TestMetadata>();
+  AllocateBuffer();
+
+  std::vector<TestMetadata> ms = {
+      {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}};
+
+  for (auto mi : ms) {
+    size_t slot;
+    auto p1 = producer_queue_->Dequeue(0, &slot);
+    ASSERT_NE(nullptr, p1);
+    ASSERT_EQ(p1->Post(LocalHandle(-1), &mi, sizeof(mi)), 0);
+    TestMetadata mo;
+    auto c1 = consumer_queue_->Dequeue(0, &slot, &mo);
+    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) {
+  CreateQueues<int64_t>();
+  AllocateBuffer();
+
+  int64_t mi = 3;
+  size_t slot;
+  auto p1 = producer_queue_->Dequeue(0, &slot);
+  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);
+  ASSERT_EQ(nullptr, c1);
+}
+
+TEST_F(BufferHubQueueTest, TestEnqueue) {
+  CreateQueues<int64_t>();
+  AllocateBuffer();
+
+  size_t slot;
+  auto p1 = producer_queue_->Dequeue(0, &slot);
+  ASSERT_NE(nullptr, p1);
+
+  int64_t mo;
+  producer_queue_->Enqueue(p1, slot);
+  auto c1 = consumer_queue_->Dequeue(0, &slot, &mo);
+  ASSERT_EQ(nullptr, c1);
+}
+
+TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
+  CreateQueues<int64_t>();
+
+  size_t s1;
+  AllocateBuffer();
+  auto p1 = producer_queue_->Dequeue(0, &s1);
+  ASSERT_NE(nullptr, p1);
+
+  // producer queue is exhausted
+  size_t s2;
+  auto p2_null = producer_queue_->Dequeue(0, &s2);
+  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);
+  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);
+  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);
+  ASSERT_NE(nullptr, c2);
+  ASSERT_EQ(cs2, s2);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageSetMask) {
+  const int set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+  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);
+
+  auto p1 = producer_queue_->Dequeue(0, &slot);
+  ASSERT_EQ(p1->usage() & set_mask, set_mask);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageClearMask) {
+  const int clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+  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);
+
+  auto p1 = producer_queue_->Dequeue(0, &slot);
+  ASSERT_EQ(p1->usage() & clear_mask, 0);
+}
+
+TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
+  const int deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
+  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;
+  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..5bb121a
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -0,0 +1,23 @@
+#include <private/dvr/buffer_hub_queue_producer.h>
+
+#include <base/logging.h>
+#include <gui/Surface.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class BufferHubQueueProducerTest : public ::testing::Test {};
+
+TEST_F(BufferHubQueueProducerTest, TempTestBufferHubQueueProducer) {
+  auto core = BufferHubQueueCore::Create();
+  sp<BufferHubQueueProducer> producer = new BufferHubQueueProducer(core);
+  sp<Surface> surface = new Surface(producer, true);
+}
+
+}  // namespace
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/Android.mk b/libs/vr/libdisplay/Android.mk
new file mode 100644
index 0000000..670bdcd
--- /dev/null
+++ b/libs/vr/libdisplay/Android.mk
@@ -0,0 +1,96 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	native_window.cpp \
+	native_buffer_queue.cpp \
+	display_client.cpp \
+	display_manager_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 \
+	vsync_client_api.cpp \
+	screenshot_client.cpp \
+	frame_history.cpp
+
+includeFiles := \
+	$(LOCAL_PATH)/include \
+	frameworks/native/vulkan/include
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	liblog \
+	libutils \
+	libEGL \
+	libGLESv2 \
+	libvulkan \
+	libui \
+	libgui \
+	libhardware \
+	libsync
+
+staticLibraries := \
+	libchrome \
+	libbufferhub \
+	libbufferhubqueue \
+	libdvrcommon \
+	libdvrgraphics \
+	libsensor \
+	libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+#LOCAL_CPPFLAGS := -UNDEBUG -DDEBUG -O0 -g
+LOCAL_CFLAGS += -DLOG_TAG=\"libdisplay\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_GRAPHICS
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libdisplay
+include $(BUILD_STATIC_LIBRARY)
+
+
+testFiles := \
+  tests/graphics_app_tests.cpp
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := graphics_app_tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+  $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+  libdisplay \
+  $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
new file mode 100644
index 0000000..cfb346d
--- /dev/null
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -0,0 +1,276 @@
+#include "include/private/dvr/display_client.h"
+
+#include <cutils/log.h>
+#include <cutils/native_handle.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<BufferProducer> DisplaySurfaceClient::AllocateBuffer(
+    uint32_t* buffer_index) {
+  auto status = InvokeRemoteMethod<DisplayRPC::AllocateBuffer>();
+  if (!status) {
+    ALOGE("DisplaySurfaceClient::AllocateBuffer: Failed to allocate buffer: %s",
+          status.GetErrorMessage().c_str());
+    return nullptr;
+  }
+
+  if (buffer_index)
+    *buffer_index = status.get().first;
+  return BufferProducer::Import(status.take().second);
+}
+
+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;
+}
+
+int DisplayClient::EnterVrMode() {
+  auto status = InvokeRemoteMethod<DisplayRPC::EnterVrMode>();
+  if (!status) {
+    ALOGE(
+        "DisplayClient::EnterVrMode: Failed to set display service to Vr mode");
+    return -status.error();
+  }
+
+  return 0;
+}
+
+int DisplayClient::ExitVrMode() {
+  auto status = InvokeRemoteMethod<DisplayRPC::ExitVrMode>();
+  if (!status) {
+    ALOGE(
+        "DisplayClient::ExitVrMode: Failed to revert display service from Vr "
+        "mode");
+    return -status.error();
+  }
+
+  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);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
new file mode 100644
index 0000000..f454b08
--- /dev/null
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -0,0 +1,109 @@
+#include "include/private/dvr/display_manager_client.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;
+}
+
+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) {
+  std::vector<std::unique_ptr<android::dvr::BufferConsumer>> buffer_list;
+  int ret = client->client->GetSurfaceBuffers(surface_id, &buffer_list);
+  if (ret < 0)
+    return ret;
+
+  *surface_buffers =
+      new DvrDisplayManagerClientSurfaceBuffers(std::move(buffer_list));
+  return ret;
+}
+
+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/libdisplay/display_manager_client_impl.cpp b/libs/vr/libdisplay/display_manager_client_impl.cpp
new file mode 100644
index 0000000..82198b9
--- /dev/null
+++ b/libs/vr/libdisplay/display_manager_client_impl.cpp
@@ -0,0 +1,57 @@
+#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;
+}
+
+int DisplayManagerClient::GetSurfaceBuffers(
+    int surface_id, std::vector<std::unique_ptr<BufferConsumer>>* consumers) {
+  auto status =
+      InvokeRemoteMethod<DisplayManagerRPC::GetSurfaceBuffers>(surface_id);
+  if (!status) {
+    ALOGE(
+        "DisplayManagerClient::GetSurfaceBuffers: Failed to get buffers for "
+        "surface_id=%d: %s",
+        surface_id, status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  std::vector<std::unique_ptr<BufferConsumer>> consumer_buffers;
+  std::vector<LocalChannelHandle> channel_handles = status.take();
+  for (auto&& handle : channel_handles) {
+    consumer_buffers.push_back(BufferConsumer::Import(std::move(handle)));
+  }
+
+  *consumers = std::move(consumer_buffers);
+  return 0;
+}
+
+}  // 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..5547f53
--- /dev/null
+++ b/libs/vr/libdisplay/dummy_native_window.cpp
@@ -0,0 +1,75 @@
+#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) {
+    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..67e4a09
--- /dev/null
+++ b/libs/vr/libdisplay/frame_history.cpp
@@ -0,0 +1,147 @@
+#include <private/dvr/frame_history.h>
+
+#include <cutils/log.h>
+#include <errno.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..64b2e99
--- /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 <base/logging.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) {
+    LOG(ERROR) << "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) {
+    LOG(ERROR) << "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..d599616
--- /dev/null
+++ b/libs/vr/libdisplay/graphics.cpp
@@ -0,0 +1,1587 @@
+#include <dvr/graphics.h>
+
+#include <sys/timerfd.h>
+#include <array>
+#include <vector>
+
+#include <cutils/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 <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 {
+
+constexpr int kDefaultDisplaySurfaceUsage =
+    GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+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=%ld", 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;
+}
+
+extern "C" int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width,
+                                        int* height, int* format) {
+  ANativeWindow* nwin = reinterpret_cast<ANativeWindow*>(win);
+  int w, h, f;
+
+  nwin->query(nwin, NATIVE_WINDOW_DEFAULT_WIDTH, &w);
+  nwin->query(nwin, NATIVE_WINDOW_DEFAULT_HEIGHT, &h);
+  nwin->query(nwin, NATIVE_WINDOW_FORMAT, &f);
+
+  if (width)
+    *width = w;
+  if (height)
+    *height = h;
+  if (format)
+    *format = f;
+
+  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);
+
+  DISALLOW_COPY_AND_ASSIGN(DvrGraphicsContext);
+};
+
+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();
+    CHECK(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);
+    CHECK_EQ(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);
+    CHECK_EQ(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);
+    CHECK_EQ(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);
+    CHECK_EQ(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());
+    CHECK_EQ(result, VK_SUCCESS);
+    CHECK_GT(device_surface_format_count, 0U);
+    CHECK_NE(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);
+    CHECK_EQ(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());
+    CHECK_EQ(result, VK_SUCCESS);
+    VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
+    // Extract presentation surface extents, image count, transform, usages,
+    // etc.
+    LOG_ASSERT(
+        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;
+    CHECK_NE(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);
+    CHECK_EQ(result, VK_SUCCESS);
+    // Create swapchain image views
+    uint32_t image_count = 0;
+    result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
+                                     &image_count, nullptr);
+    CHECK_EQ(result, VK_SUCCESS);
+    CHECK_GT(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());
+    CHECK_EQ(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]);
+      CHECK_EQ(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_ASSERT(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_ASSERT(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_ASSERT(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_ASSERT(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.
+    DCHECK(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_ASSERT(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.
+    DCHECK(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_ASSERT(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_ASSERT(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_ASSERT(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_ASSERT(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) {
+    LOG(ERROR) << "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_ASSERT(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_ASSERT(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_ASSERT(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_ASSERT(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..50d2754
--- /dev/null
+++ b/libs/vr/libdisplay/include/dvr/graphics.h
@@ -0,0 +1,475 @@
+#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
+
+// Create a stereo surface that will be lens-warped by the system.
+EGLNativeWindowType dvrCreateWarpedDisplaySurface(int* display_width,
+                                                  int* display_height);
+EGLNativeWindowType dvrCreateDisplaySurface(void);
+
+// 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];
+};
+
+// Creates a display surface 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,
+//   };
+EGLNativeWindowType dvrCreateDisplaySurfaceExtended(
+    struct DvrSurfaceParameter* parameters);
+
+int dvrGetNativeDisplayDimensions(int* native_width, int* native_height);
+
+int dvrGetDisplaySurfaceInfo(EGLNativeWindowType win, int* width, int* height,
+                             int* format);
+
+// NOTE: Only call the functions below on windows created with the API above.
+
+// Sets the display surface visible based on the boolean evaluation of
+// |visible|.
+void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window, int visible);
+
+// Sets the application z-order of the display surface. Higher values display on
+// top of lower values.
+void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window, int z_order);
+
+// 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 dvrDisplayPostEarly(EGLNativeWindowType window);
+
+// Opaque struct that represents a graphics context, the texture swap chain,
+// and surfaces.
+typedef struct DvrGraphicsContext DvrGraphicsContext;
+
+// Create the graphics context.
+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..034b7b4
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -0,0 +1,126 @@
+#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/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);
+
+  // |out_buffer_index| will receive a unique index for this buffer within the
+  // surface. The first buffer gets 0, second gets 1, and so on. This index
+  // can be used to deliver metadata for buffers that are queued for display.
+  std::shared_ptr<BufferProducer> AllocateBuffer(uint32_t* out_buffer_index);
+  std::shared_ptr<BufferProducer> AllocateBuffer() {
+    return AllocateBuffer(nullptr);
+  }
+
+  // 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_;
+
+  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);
+
+  int EnterVrMode();
+  int ExitVrMode();
+
+  std::unique_ptr<DisplaySurfaceClient> CreateDisplaySurface(
+      int width, int height, int format, int usage, int flags);
+
+ 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.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
new file mode 100644
index 0000000..f28c1e4
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
@@ -0,0 +1,73 @@
+#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;
+
+DvrDisplayManagerClient* dvrDisplayManagerClientCreate();
+
+void dvrDisplayManagerClientDestroy(DvrDisplayManagerClient* client);
+
+// 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);
+
+// Populates |surface_buffers| with the list of buffers for |surface_id|.
+// |surface_id| should be a valid ID from the list of surfaces.
+//
+// @return Returns 0 on success. Otherwise it returns a negative error value.
+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/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..645ccce
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
@@ -0,0 +1,35 @@
+#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 BufferConsumer;
+
+class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
+ public:
+  ~DisplayManagerClient() override;
+
+  int GetSurfaceList(std::vector<DisplaySurfaceInfo>* surface_list);
+
+  int GetSurfaceBuffers(
+      int surface_id, std::vector<std::unique_ptr<BufferConsumer>>* consumers);
+
+ 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..6150b35
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
@@ -0,0 +1,342 @@
+#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/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/display/client";
+
+  // Op codes.
+  enum {
+    kOpGetMetrics = 0,
+    kOpGetEdsCapture,
+    kOpCreateSurface,
+    kOpAllocateBuffer,
+    kOpSetAttributes,
+    kOpGetMetadataBuffer,
+    kOpCreateVideoMeshSurface,
+    kOpVideoMeshSurfaceCreateProducerQueue,
+    kOpEnterVrMode,
+    kOpExitVrMode,
+    kOpSetViewerParams
+  };
+
+  // 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(AllocateBuffer, kOpAllocateBuffer,
+                    std::pair<std::uint32_t, 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(EnterVrMode, kOpEnterVrMode, int(Void));
+  PDX_REMOTE_METHOD(ExitVrMode, kOpExitVrMode, int(Void));
+  PDX_REMOTE_METHOD(SetViewerParams, kOpSetViewerParams,
+                    void(const ViewerParams& viewer_params));
+};
+
+struct DisplayManagerRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/display/manager";
+
+  // Op codes.
+  enum {
+    kOpGetSurfaceList = 0,
+    kOpGetSurfaceBuffers,
+    kOpUpdateSurfaces,
+  };
+
+  // Aliases.
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+  using Void = pdx::rpc::Void;
+
+  // Methods.
+  PDX_REMOTE_METHOD(GetSurfaceList, kOpGetSurfaceList,
+                    std::vector<DisplaySurfaceInfo>(Void));
+  PDX_REMOTE_METHOD(GetSurfaceBuffers, kOpGetSurfaceBuffers,
+                    std::vector<LocalChannelHandle>(int surface_id));
+  PDX_REMOTE_METHOD(
+      UpdateSurfaces, kOpUpdateSurfaces,
+      int(const std::map<int, DisplaySurfaceAttributes>& updates));
+};
+
+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/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/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..d0eff51
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/late_latch.h
@@ -0,0 +1,186 @@
+#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_;
+};
+
+}  // 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..87e9c9f
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/native_buffer_queue.h
@@ -0,0 +1,73 @@
+#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 {
+
+// NativeBufferQueue manages a queue of NativeBufferProducers allocated from a
+// DisplaySurfaceClient. Buffers are automatically re-enqueued when released by
+// the consumer side.
+class NativeBufferQueue {
+ public:
+  // Create a queue with the given number of free buffers.
+  NativeBufferQueue(const std::shared_ptr<DisplaySurfaceClient>& surface,
+                    size_t capacity);
+  NativeBufferQueue(EGLDisplay display,
+                    const std::shared_ptr<DisplaySurfaceClient>& surface,
+                    size_t capacity);
+  ~NativeBufferQueue();
+
+  std::shared_ptr<DisplaySurfaceClient> surface() const { return surface_; }
+
+  // Dequeue a buffer from the free queue, blocking until one is available.
+  NativeBufferProducer* Dequeue();
+
+  // Enqueue a buffer at the end of the free queue.
+  void Enqueue(NativeBufferProducer* buf);
+
+  // Get the number of free buffers in the queue.
+  size_t GetFreeBufferCount() const;
+
+  // Get the total number of buffers managed by this queue.
+  size_t GetQueueCapacity() const;
+
+  // Accessors for display surface buffer attributes.
+  int width() const { return surface_->width(); }
+  int height() const { return surface_->height(); }
+  int format() const { return surface_->format(); }
+  int usage() const { return surface_->usage(); }
+
+ private:
+  // Wait for buffers to be released and enqueue them.
+  bool WaitForBuffers();
+
+  std::shared_ptr<DisplaySurfaceClient> surface_;
+
+  // A list of strong pointers to the buffers, used for managing buffer
+  // lifetime.
+  std::vector<android::sp<NativeBufferProducer>> buffers_;
+
+  // Used to implement queue semantics.
+  RingBuffer<NativeBufferProducer*> buffer_queue_;
+
+  // Epoll fd used to wait for BufferHub events.
+  int epoll_fd_;
+
+  NativeBufferQueue(const NativeBufferQueue&) = delete;
+  void operator=(NativeBufferQueue&) = delete;
+};
+
+}  // 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..a2659a6
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
+#define ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
+
+#include <base/macros.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<android::dvr::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..32fa40f
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
@@ -0,0 +1,70 @@
+#ifndef ANDROID_DVR_VSYNC_CLIENT_H_
+#define ANDROID_DVR_VSYNC_CLIENT_H_
+
+#include <stdint.h>
+
+#include <pdx/client.h>
+#include <private/dvr/vsync_client_api.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/include/private/dvr/vsync_client_api.h b/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h
new file mode 100644
index 0000000..4cdbc71
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_client_api.h
@@ -0,0 +1,44 @@
+#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 dreamos_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);
+
+// Blocks until the next vsync signal.
+// The timestamp (in ns) is written into |*timestamp_ns| when it is non-NULL.
+// Returns 0 upon success, or -errno.
+int dvr_vsync_client_wait(dvr_vsync_client* client, int64_t* timestamp_ns);
+
+// Returns the file descriptor used to communicate with the vsync service.
+int dvr_vsync_client_get_fd(dvr_vsync_client* client);
+
+// Clears the select/poll/epoll event so that subsequent calls to these
+// will not signal until the next vsync.
+int dvr_vsync_client_acknowledge(dvr_vsync_client* client);
+
+// Gets the timestamp of the last vsync signal in ns. This call has the
+// same side effects on events as acknowledge.
+int dvr_vsync_client_get_last_timestamp(dvr_vsync_client* client,
+                                        int64_t* timestamp_ns);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_VSYNC_CLIENT_API_H_
diff --git a/libs/vr/libdisplay/late_latch.cpp b/libs/vr/libdisplay/late_latch.cpp
new file mode 100644
index 0000000..3681e10
--- /dev/null
+++ b/libs/vr/libdisplay/late_latch.cpp
@@ -0,0 +1,461 @@
+#include "include/private/dvr/late_latch.h"
+
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <base/logging.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
+#include <cutils/log.h>
+
+#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) {
+  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();
+
+  LocalHandle pose_buffer_fd;
+  pose_client_ = dvrPoseCreate();
+  if (!pose_client_) {
+    LOG(ERROR) << "LateLatch Error: failed to create pose client";
+  } else {
+    int ret = privateDvrPoseGetRingBufferFd(pose_client_, &pose_buffer_fd);
+    if (ret < 0) {
+      LOG(ERROR) << "LateLatch Error: failed to get pose ring buffer";
+    }
+  }
+
+  glGenBuffers(1, &pose_buffer_object_);
+  glGenBuffers(1, &metadata_buffer_id_);
+  if (!glBindSharedBufferQCOM) {
+    LOG(ERROR) << "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.Release());
+    }
+    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.Release());
+  } 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 {
+  CHECK(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
+    LOG(ERROR) << "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 {
+  CHECK(!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..2d1e23d
--- /dev/null
+++ b/libs/vr/libdisplay/native_buffer_queue.cpp
@@ -0,0 +1,152 @@
+#include "include/private/dvr/native_buffer_queue.h"
+
+#include <base/logging.h>
+#include <cutils/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(
+    const std::shared_ptr<DisplaySurfaceClient>& surface, size_t capacity)
+    : NativeBufferQueue(nullptr, surface, capacity) {}
+
+NativeBufferQueue::NativeBufferQueue(
+    EGLDisplay display, const std::shared_ptr<DisplaySurfaceClient>& surface,
+    size_t capacity)
+    : surface_(surface),
+      buffers_(capacity),
+      buffer_queue_(capacity) {
+  CHECK(surface);
+
+  epoll_fd_ = epoll_create(64);
+  if (epoll_fd_ < 0) {
+    ALOGE("NativeBufferQueue::NativeBufferQueue: Failed to create epoll fd: %s",
+          strerror(errno));
+    return;
+  }
+
+  // The kSurfaceBufferMaxCount must be >= the capacity so that shader code
+  // can bind surface buffer array data.
+  CHECK(kSurfaceBufferMaxCount >= capacity);
+
+  for (size_t i = 0; i < capacity; i++) {
+    uint32_t buffer_index = 0;
+    auto buffer = surface_->AllocateBuffer(&buffer_index);
+    if (!buffer) {
+      ALOGE("NativeBufferQueue::NativeBufferQueue: Failed to allocate buffer!");
+      return;
+    }
+
+    // TODO(jbates): store an index associated with each buffer so that we can
+    // determine which index in DisplaySurfaceMetadata it is associated
+    // with.
+    buffers_.push_back(new NativeBufferProducer(buffer, display, buffer_index));
+    NativeBufferProducer* native_buffer = buffers_.back().get();
+
+    epoll_event event = {.events = EPOLLIN | EPOLLET,
+                         .data = {.ptr = native_buffer}};
+    if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, buffer->event_fd(), &event) <
+        0) {
+      ALOGE(
+          "NativeBufferQueue::NativeBufferQueue: Failed to add buffer producer "
+          "to epoll set: %s",
+          strerror(errno));
+      return;
+    }
+
+    Enqueue(native_buffer);
+  }
+}
+
+NativeBufferQueue::~NativeBufferQueue() {
+  if (epoll_fd_ >= 0)
+    close(epoll_fd_);
+}
+
+bool NativeBufferQueue::WaitForBuffers() {
+  ATRACE_NAME("NativeBufferQueue::WaitForBuffers");
+  // Intentionally set this to one so that we don't waste time retrieving too
+  // many buffers.
+  constexpr size_t kMaxEvents = 1;
+  std::array<epoll_event, kMaxEvents> events;
+
+  while (buffer_queue_.IsEmpty()) {
+    int num_events = epoll_wait(epoll_fd_, events.data(), events.size(), -1);
+    if (num_events < 0 && errno != EINTR) {
+      ALOGE("NativeBufferQueue:WaitForBuffers: Failed to wait for buffers: %s",
+            strerror(errno));
+      return false;
+    }
+
+    ALOGD_IF(TRACE, "NativeBufferQueue::WaitForBuffers: num_events=%d",
+             num_events);
+
+    for (int i = 0; i < num_events; i++) {
+      NativeBufferProducer* buffer =
+          static_cast<NativeBufferProducer*>(events[i].data.ptr);
+      ALOGD_IF(TRACE,
+               "NativeBufferQueue::WaitForBuffers: event %d: buffer_id=%d "
+               "events=0x%x",
+               i, buffer->buffer()->id(), events[i].events);
+
+      if (events[i].events & EPOLLIN) {
+        const int ret = buffer->GainAsync();
+        if (ret < 0) {
+          ALOGE("NativeBufferQueue::WaitForBuffers: Failed to gain buffer: %s",
+                strerror(-ret));
+          continue;
+        }
+
+        Enqueue(buffer);
+      }
+    }
+  }
+
+  return true;
+}
+
+void NativeBufferQueue::Enqueue(NativeBufferProducer* buf) {
+  ATRACE_NAME("NativeBufferQueue::Enqueue");
+  if (buffer_queue_.IsFull()) {
+    ALOGE("NativeBufferQueue::Enqueue: Queue is full!");
+    return;
+  }
+
+  buffer_queue_.Append(buf);
+}
+
+NativeBufferProducer* NativeBufferQueue::Dequeue() {
+  ATRACE_NAME("NativeBufferQueue::Dequeue");
+  ALOGD_IF(TRACE, "NativeBufferQueue::Dequeue: count=%zd",
+           buffer_queue_.GetSize());
+
+  if (buffer_queue_.IsEmpty() && !WaitForBuffers())
+    return nullptr;
+
+  NativeBufferProducer* buf = buffer_queue_.Front();
+  buffer_queue_.PopFront();
+  if (buf == nullptr) {
+    ALOGE("NativeBufferQueue::Dequeue: Buffer at tail was nullptr!!!");
+    return nullptr;
+  }
+
+  return buf;
+}
+
+size_t NativeBufferQueue::GetFreeBufferCount() const {
+  return buffer_queue_.GetSize();
+}
+
+size_t NativeBufferQueue::GetQueueCapacity() const {
+  return buffer_queue_.GetCapacity();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/native_window.cpp b/libs/vr/libdisplay/native_window.cpp
new file mode 100644
index 0000000..63c81ed
--- /dev/null
+++ b/libs/vr/libdisplay/native_window.cpp
@@ -0,0 +1,459 @@
+#include <EGL/egl.h>
+
+#include <android/native_window.h>
+#include <base/logging.h>
+#include <cutils/native_handle.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/timerfd.h>
+#include <system/window.h>
+#include <time.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/Errors.h>
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <cutils/log.h>
+
+#include <memory>
+#include <mutex>
+
+#include <dvr/graphics.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_client.h>
+#include <private/dvr/native_buffer.h>
+#include <private/dvr/native_buffer_queue.h>
+
+namespace {
+
+constexpr int kDefaultDisplaySurfaceUsage =
+    GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+constexpr int kDefaultDisplaySurfaceFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+constexpr int kWarpedDisplaySurfaceFlags = 0;
+constexpr int kUnwarpedDisplaySurfaceFlags =
+    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS |
+    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION |
+    DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC;
+constexpr int kDefaultBufferCount = 4;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// NativeWindow is an implementation of ANativeWindow. This class interacts with
+// displayd through the DisplaySurfaceClient and NativeBufferQueue.
+class NativeWindow : public ANativeObjectBase<ANativeWindow, NativeWindow,
+                                              LightRefBase<NativeWindow> > {
+ public:
+  explicit NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface);
+
+  void SetVisible(bool visible);
+  void SetZOrder(int z_order);
+  void PostEarly();
+
+ private:
+  friend class LightRefBase<NativeWindow>;
+
+  void Post(sp<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);
+
+  std::shared_ptr<DisplaySurfaceClient> surface_;
+
+  std::mutex lock_;
+  NativeBufferQueue buffer_queue_;
+  sp<NativeBufferProducer> next_post_buffer_;
+  bool next_buffer_already_posted_;
+
+  NativeWindow(const NativeWindow&) = delete;
+  void operator=(NativeWindow&) = delete;
+};
+
+NativeWindow::NativeWindow(const std::shared_ptr<DisplaySurfaceClient>& surface)
+    : surface_(surface),
+      buffer_queue_(surface, kDefaultBufferCount),
+      next_post_buffer_(nullptr),
+      next_buffer_already_posted_(false) {
+  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;
+}
+
+void NativeWindow::SetVisible(bool visible) { surface_->SetVisible(visible); }
+
+void NativeWindow::SetZOrder(int z_order) { surface_->SetZOrder(z_order); }
+
+void NativeWindow::PostEarly() {
+  ATRACE_NAME("NativeWindow::PostEarly");
+  ALOGI_IF(TRACE, "NativeWindow::PostEarly");
+
+  std::lock_guard<std::mutex> autolock(lock_);
+
+  if (!next_buffer_already_posted_) {
+    next_buffer_already_posted_ = true;
+
+    if (!next_post_buffer_.get()) {
+      next_post_buffer_ = buffer_queue_.Dequeue();
+    }
+    ATRACE_ASYNC_BEGIN("BufferPost", next_post_buffer_->buffer()->id());
+    Post(next_post_buffer_, -1);
+  }
+}
+
+void NativeWindow::Post(sp<NativeBufferProducer> buffer, int fence_fd) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+  ALOGI_IF(TRACE, "NativeWindow::Post: buffer_id=%d, fence_fd=%d",
+           buffer->buffer()->id(), fence_fd);
+  ALOGW_IF(!surface_->visible(),
+           "NativeWindow::Post: Posting buffer on invisible surface!!!");
+  buffer->Post(fence_fd, 0);
+}
+
+int NativeWindow::SetSwapInterval(ANativeWindow* window, int interval) {
+  ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval);
+  return 0;
+}
+
+int NativeWindow::DequeueBuffer(ANativeWindow* window,
+                                ANativeWindowBuffer** buffer, int* fence_fd) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+
+  NativeWindow* self = getSelf(window);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  if (!self->next_post_buffer_.get()) {
+    self->next_post_buffer_ = self->buffer_queue_.Dequeue();
+  }
+  ATRACE_ASYNC_BEGIN("BufferDraw", self->next_post_buffer_->buffer()->id());
+  *fence_fd = self->next_post_buffer_->ClaimReleaseFence().Release();
+  *buffer = self->next_post_buffer_.get();
+
+  ALOGI_IF(TRACE, "NativeWindow::DequeueBuffer: fence_fd=%d", *fence_fd);
+  return 0;
+}
+
+int NativeWindow::QueueBuffer(ANativeWindow* window,
+                              ANativeWindowBuffer* buffer, int fence_fd) {
+  ATRACE_NAME("NativeWindow::QueueBuffer");
+  ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd);
+
+  NativeWindow* self = getSelf(window);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  NativeBufferProducer* native_buffer =
+      static_cast<NativeBufferProducer*>(buffer);
+  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+  bool do_post = true;
+  if (self->next_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.
+    DCHECK(native_buffer == self->next_post_buffer_.get());
+    if (native_buffer == self->next_post_buffer_.get()) {
+      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->next_buffer_already_posted_ = false;
+  self->next_post_buffer_ = nullptr;
+
+  return NO_ERROR;
+}
+
+int NativeWindow::CancelBuffer(ANativeWindow* window,
+                               ANativeWindowBuffer* buffer, int fence_fd) {
+  ATRACE_NAME("NativeWindow::CancelBuffer");
+  ALOGI_IF(TRACE, "NativeWindow::CancelBuffer: fence_fd: %d", fence_fd);
+
+  NativeWindow* self = getSelf(window);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  NativeBufferProducer* native_buffer =
+      static_cast<NativeBufferProducer*>(buffer);
+  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+  ATRACE_INT("CancelBuffer", native_buffer->buffer()->id());
+  bool do_enqueue = true;
+  if (self->next_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.
+    DCHECK(native_buffer == self->next_post_buffer_.get());
+    if (native_buffer == self->next_post_buffer_.get()) {
+      do_enqueue = false;
+    }
+  }
+  if (do_enqueue) {
+    self->buffer_queue_.Enqueue(native_buffer);
+  }
+  if (fence_fd >= 0)
+    close(fence_fd);
+  self->next_buffer_already_posted_ = false;
+  self->next_post_buffer_ = nullptr;
+
+  return NO_ERROR;
+}
+
+int NativeWindow::Query(const ANativeWindow* window, int what, int* value) {
+  NativeWindow* self = getSelf(const_cast<ANativeWindow*>(window));
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  switch (what) {
+    case NATIVE_WINDOW_WIDTH:
+      *value = self->surface_->width();
+      return NO_ERROR;
+    case NATIVE_WINDOW_HEIGHT:
+      *value = self->surface_->height();
+      return NO_ERROR;
+    case NATIVE_WINDOW_FORMAT:
+      *value = self->surface_->format();
+      return NO_ERROR;
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+      *value = 1;
+      return NO_ERROR;
+    case NATIVE_WINDOW_CONCRETE_TYPE:
+      *value = NATIVE_WINDOW_SURFACE;
+      return NO_ERROR;
+    case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+      *value = 1;
+      return NO_ERROR;
+    case NATIVE_WINDOW_DEFAULT_WIDTH:
+      *value = self->surface_->width();
+      return NO_ERROR;
+    case NATIVE_WINDOW_DEFAULT_HEIGHT:
+      *value = self->surface_->height();
+      return NO_ERROR;
+    case NATIVE_WINDOW_TRANSFORM_HINT:
+      *value = 0;
+      return NO_ERROR;
+  }
+
+  *value = 0;
+  return BAD_VALUE;
+}
+
+int NativeWindow::Perform(ANativeWindow* window, int operation, ...) {
+  NativeWindow* self = getSelf(window);
+  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 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 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 NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_SET_USAGE: {
+      int usage = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage);
+      return 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 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 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 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 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;
+}
+
+int NativeWindow::DequeueBuffer_DEPRECATED(ANativeWindow* window,
+                                           ANativeWindowBuffer** buffer) {
+  int fence_fd = -1;
+  int ret = DequeueBuffer(window, buffer, &fence_fd);
+
+  // wait for fence
+  if (ret == NO_ERROR && fence_fd != -1)
+    close(fence_fd);
+
+  return ret;
+}
+
+int NativeWindow::CancelBuffer_DEPRECATED(ANativeWindow* window,
+                                          ANativeWindowBuffer* buffer) {
+  return CancelBuffer(window, buffer, -1);
+}
+
+int NativeWindow::QueueBuffer_DEPRECATED(ANativeWindow* window,
+                                         ANativeWindowBuffer* buffer) {
+  return QueueBuffer(window, buffer, -1);
+}
+
+int NativeWindow::LockBuffer_DEPRECATED(ANativeWindow* /*window*/,
+                                        ANativeWindowBuffer* /*buffer*/) {
+  return NO_ERROR;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+static EGLNativeWindowType CreateDisplaySurface(int* display_width,
+                                                int* display_height, int format,
+                                                int usage, int flags) {
+  auto client = android::dvr::DisplayClient::Create();
+  if (!client) {
+    ALOGE("Failed to create display client!");
+    return nullptr;
+  }
+
+  // TODO(eieio,jbates): Consider passing flags and other parameters to get
+  // metrics based on specific surface requirements.
+  android::dvr::SystemDisplayMetrics metrics;
+  const int ret = client->GetDisplayMetrics(&metrics);
+  if (ret < 0) {
+    ALOGE("Failed to get display metrics: %s", strerror(-ret));
+    return nullptr;
+  }
+
+  int width, height;
+
+  if (flags & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION) {
+    width = metrics.display_native_width;
+    height = metrics.display_native_height;
+  } else {
+    width = metrics.distorted_width;
+    height = metrics.distorted_height;
+  }
+
+  std::shared_ptr<android::dvr::DisplaySurfaceClient> surface =
+      client->CreateDisplaySurface(width, height, format, usage, flags);
+
+  if (display_width)
+    *display_width = metrics.display_native_width;
+  if (display_height)
+    *display_height = metrics.display_native_height;
+
+  // Set the surface visible by default.
+  // TODO(eieio,jbates): Remove this from here and set visible somewhere closer
+  // to the application to account for situations where the application wants to
+  // create surfaces that will be used later or shouldn't be visible yet.
+  surface->SetVisible(true);
+
+  return new android::dvr::NativeWindow(surface);
+}
+
+std::shared_ptr<android::dvr::DisplaySurfaceClient> CreateDisplaySurfaceClient(
+    struct DvrSurfaceParameter* parameters,
+    /*out*/ android::dvr::SystemDisplayMetrics* metrics);
+
+extern "C" EGLNativeWindowType dvrCreateDisplaySurfaceExtended(
+    struct DvrSurfaceParameter* parameters) {
+  android::dvr::SystemDisplayMetrics metrics;
+  auto surface = CreateDisplaySurfaceClient(parameters, &metrics);
+  if (!surface) {
+    ALOGE("Failed to create display surface client");
+    return nullptr;
+  }
+  return new android::dvr::NativeWindow(surface);
+}
+
+extern "C" EGLNativeWindowType dvrCreateDisplaySurface() {
+  return CreateDisplaySurface(NULL, NULL, kDefaultDisplaySurfaceFormat,
+                              kDefaultDisplaySurfaceUsage,
+                              kUnwarpedDisplaySurfaceFlags);
+}
+
+extern "C" EGLNativeWindowType dvrCreateWarpedDisplaySurface(
+    int* display_width, int* display_height) {
+  return CreateDisplaySurface(
+      display_width, display_height, kDefaultDisplaySurfaceFormat,
+      kDefaultDisplaySurfaceUsage, kWarpedDisplaySurfaceFlags);
+}
+
+extern "C" void dvrDisplaySurfaceSetVisible(EGLNativeWindowType window,
+                                            int visible) {
+  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
+  native_window->SetVisible(visible);
+}
+
+extern "C" void dvrDisplaySurfaceSetZOrder(EGLNativeWindowType window,
+                                           int z_order) {
+  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
+  native_window->SetZOrder(z_order);
+}
+
+extern "C" void dvrDisplayPostEarly(EGLNativeWindowType window) {
+  auto native_window = reinterpret_cast<android::dvr::NativeWindow*>(window);
+  native_window->PostEarly();
+}
diff --git a/libs/vr/libdisplay/native_window_test.cpp b/libs/vr/libdisplay/native_window_test.cpp
new file mode 100644
index 0000000..2f3bc33
--- /dev/null
+++ b/libs/vr/libdisplay/native_window_test.cpp
@@ -0,0 +1,43 @@
+#include <iostream>
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <dvr/graphics.h>
+#include <private/dvr/display_client.h>
+
+#include <cpp_free_mock/cpp_free_mock.h>
+
+// Checks querying the VSync of the device on display surface creation.
+TEST(CreateDisplaySurface, QueryVSyncPeriod) {
+  using ::testing::_;
+
+  const uint64_t kExpectedVSync = 123456;
+
+  // We only care about the expected VSync value
+  android::dvr::DisplayMetrics metrics;
+  metrics.vsync_period_ns = kExpectedVSync;
+
+  uint64_t outPeriod;
+
+  DvrSurfaceParameter display_params[] = {
+      DVR_SURFACE_PARAMETER_IN(WIDTH, 256),
+      DVR_SURFACE_PARAMETER_IN(HEIGHT, 256),
+      DVR_SURFACE_PARAMETER_OUT(VSYNC_PERIOD, &outPeriod),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+
+  // inject the mocking code to the target method
+  auto mocked_function =
+      MOCKER(&android::dvr::DisplayClient::GetDisplayMetrics);
+
+  // instrument the mock function to return our custom metrics
+  EXPECT_CALL(*mocked_function, MOCK_FUNCTION(_, _))
+      .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(metrics),
+                                 ::testing::Return(0)));
+
+  ASSERT_NE(nullptr, dvrCreateDisplaySurfaceExtended(display_params));
+
+  EXPECT_EQ(kExpectedVSync, outPeriod);
+}
diff --git a/libs/vr/libdisplay/screenshot_client.cpp b/libs/vr/libdisplay/screenshot_client.cpp
new file mode 100644
index 0000000..78f5e0f
--- /dev/null
+++ b/libs/vr/libdisplay/screenshot_client.cpp
@@ -0,0 +1,66 @@
+#include "include/private/dvr/screenshot_client.h"
+
+#include <cutils/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/graphics_app_tests.cpp b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
new file mode 100644
index 0000000..7ea3952
--- /dev/null
+++ b/libs/vr/libdisplay/tests/graphics_app_tests.cpp
@@ -0,0 +1,177 @@
+#include <dvr/graphics.h>
+#include <gtest/gtest.h>
+
+TEST(GraphicsAppTests, CreateWarpedDisplaySurfaceParams) {
+  int width = 0, height = 0;
+  EGLNativeWindowType window = dvrCreateWarpedDisplaySurface(&width, &height);
+  EXPECT_GT(width, 0);
+  EXPECT_GT(height, 0);
+  EXPECT_NE(window, nullptr);
+}
+
+TEST(GraphicsAppTests, CreateDisplaySurface) {
+  EGLNativeWindowType window = dvrCreateDisplaySurface();
+  EXPECT_NE(window, nullptr);
+}
+
+TEST(GraphicsAppTests, CreateDisplaySurfaceExtended) {
+  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,
+  };
+
+  EGLNativeWindowType window = dvrCreateDisplaySurfaceExtended(surface_params);
+  EXPECT_NE(window, nullptr);
+  EXPECT_GT(display_width, 0);
+  EXPECT_GT(display_height, 0);
+  EXPECT_GT(surface_width, 0);
+  EXPECT_GT(surface_height, 0);
+  EXPECT_GT(inter_lens_meters, 0);
+  EXPECT_GT(left_fov[0], 0);
+  EXPECT_GT(left_fov[1], 0);
+  EXPECT_GT(left_fov[2], 0);
+  EXPECT_GT(left_fov[3], 0);
+  EXPECT_GT(right_fov[0], 0);
+  EXPECT_GT(right_fov[1], 0);
+  EXPECT_GT(right_fov[2], 0);
+  EXPECT_GT(right_fov[3], 0);
+}
+
+TEST(GraphicsAppTests, GetNativeDisplayDimensions) {
+  int width, height;
+  dvrGetNativeDisplayDimensions(&width, &height);
+  EXPECT_GT(width, 0);
+  EXPECT_GT(height, 0);
+}
+
+TEST(GraphicsAppTests, GetDisplaySurfaceInfo) {
+  int ret, width, height, format;
+  EGLNativeWindowType window = dvrCreateDisplaySurface();
+  ASSERT_NE(window, nullptr);
+  ret = dvrGetDisplaySurfaceInfo(window, &width, &height, &format);
+  ASSERT_EQ(0, ret);
+  ASSERT_GT(width, 0);
+  ASSERT_GT(height, 0);
+  ASSERT_NE(0, format);
+}
+
+// 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..04cc194
--- /dev/null
+++ b/libs/vr/libdisplay/video_mesh_surface_client.cpp
@@ -0,0 +1,61 @@
+#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<VideoMeshSurfaceBufferMetadata>(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..c4cad50
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_client.cpp
@@ -0,0 +1,72 @@
+#include "include/private/dvr/vsync_client.h"
+
+#include <cutils/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();
+  }
+  *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/libdisplay/vsync_client_api.cpp b/libs/vr/libdisplay/vsync_client_api.cpp
new file mode 100644
index 0000000..56103ed
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_client_api.cpp
@@ -0,0 +1,34 @@
+#include "include/private/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_wait(dvr_vsync_client* client, int64_t* timestamp_ns) {
+  return static_cast<android::dvr::VSyncClient*>(client)->Wait(timestamp_ns);
+}
+
+int dvr_vsync_client_get_fd(dvr_vsync_client* client) {
+  return static_cast<android::dvr::VSyncClient*>(client)->GetFd();
+}
+
+int dvr_vsync_client_acknowledge(dvr_vsync_client* client) {
+  return static_cast<android::dvr::VSyncClient*>(client)->Acknowledge();
+}
+
+int dvr_vsync_client_get_last_timestamp(dvr_vsync_client* client,
+                                        int64_t* timestamp_ns) {
+  return static_cast<android::dvr::VSyncClient*>(client)->GetLastTimestamp(
+      timestamp_ns);
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvrcommon/Android.mk b/libs/vr/libdvrcommon/Android.mk
new file mode 100644
index 0000000..72afab2
--- /dev/null
+++ b/libs/vr/libdvrcommon/Android.mk
@@ -0,0 +1,81 @@
+# 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 := \
+	frame_time_history.cpp \
+	revision.cpp \
+	revision_path.cpp \
+	sync_util.cpp \
+
+includeFiles := \
+  $(LOCAL_PATH)/include \
+  external/eigen \
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	liblog \
+	libutils \
+	libEGL \
+	libGLESv2 \
+	libui \
+	libgui \
+	libhardware
+
+staticLibraries := \
+	libchrome \
+	libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_CFLAGS += -DLOG_TAG=\"libdvrcommon\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libdvrcommon
+include $(BUILD_STATIC_LIBRARY)
+
+testFiles := \
+  tests/numeric_test.cpp \
+  tests/pose_test.cpp \
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdvrcommon_test
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+  $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+  libgmock_main \
+  libgmock \
+  libgtest \
+  $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
+
diff --git a/libs/vr/libdvrcommon/frame_time_history.cpp b/libs/vr/libdvrcommon/frame_time_history.cpp
new file mode 100644
index 0000000..796498c
--- /dev/null
+++ b/libs/vr/libdvrcommon/frame_time_history.cpp
@@ -0,0 +1,37 @@
+#include <private/dvr/frame_time_history.h>
+
+#include <cutils/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..2dbb5f2
--- /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 (currently in dreamos/system/core/applications),
+// 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..7db681a
--- /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 <base/logging.h>
+
+#ifndef NDEBUG
+#define CHECK_GL()                          \
+  do {                                      \
+    const GLenum err = glGetError();        \
+    if (err != GL_NO_ERROR) {               \
+      LOG(ERROR) << "OpenGL error " << err; \
+    }                                       \
+  } while (0)
+
+#define CHECK_GL_FBO()                                        \
+  do {                                                        \
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); \
+    switch (status) {                                         \
+      case GL_FRAMEBUFFER_COMPLETE:                           \
+        break;                                                \
+      case GL_FRAMEBUFFER_UNSUPPORTED:                        \
+        LOG(ERROR) << "GL_FRAMEBUFFER_UNSUPPORTED";           \
+        break;                                                \
+      default:                                                \
+        LOG(ERROR) << "FBO user error: " << 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..8df741c
--- /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 <base/logging.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()) {
+      LOG(WARNING) << "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..c9f7f8e
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
@@ -0,0 +1,63 @@
+#ifndef ANDROID_DVR_LOG_HELPERS_H_
+#define ANDROID_DVR_LOG_HELPERS_H_
+
+#include <iomanip>
+
+#include <base/logging.h>
+#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/numeric.h b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
new file mode 100644
index 0000000..584236a
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/numeric.h
@@ -0,0 +1,178 @@
+#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) {
+  using Matrix1_t = Eigen::MatrixBase<Derived1>;
+  using Matrix2_t = Eigen::MatrixBase<Derived2>;
+
+  EIGEN_STATIC_ASSERT_SAME_MATRIX_SIZE(Matrix1_t, Matrix2_t);
+
+  Derived1 result = Matrix1_t::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..71d4c8c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/platform_defines.h
@@ -0,0 +1,16 @@
+#ifndef ANDROID_DVR_PLATFORM_DEFINES_H_
+#define ANDROID_DVR_PLATFORM_DEFINES_H_
+
+// Platform-specific macros and defines.
+
+// QCOM's GRALLOC_USAGE_PRIVATE_ALLOC_UBWC usage bit.
+#define GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION GRALLOC_USAGE_PRIVATE_1
+
+// 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/revision.h b/libs/vr/libdvrcommon/include/private/dvr/revision.h
new file mode 100644
index 0000000..dda0fce
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/revision.h
@@ -0,0 +1,47 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// List of DreamOS products
+typedef enum DvrProduct {
+  DVR_PRODUCT_UNKNOWN,
+  DVR_PRODUCT_A00,
+  DVR_PRODUCT_A65R,
+  DVR_PRODUCT_TWILIGHT = DVR_PRODUCT_A65R
+} DvrProduct;
+
+// List of possible revisions.
+typedef enum DvrRevision {
+  DVR_REVISION_UNKNOWN,
+  DVR_REVISION_P1,
+  DVR_REVISION_P2,
+  DVR_REVISION_P3,
+} DvrRevision;
+
+// Query the device's product.
+//
+// @return DvrProduct value, or DvrProductUnknown on error.
+DvrProduct dvr_get_product();
+
+// Query the device's revision.
+//
+// @return DvrRevision value, or DvrRevisionUnknown on error.
+DvrRevision dvr_get_revision();
+
+// Returns the device's board revision string.
+//
+// @return NULL-terminated string such as 'a00-p1'.
+const char* dvr_get_product_revision_str();
+
+// Returns the device's serial number.
+//
+// @return Returns NULL on error, or a NULL-terminated string.
+const char* dvr_get_serial_number();
+
+#ifdef __cplusplus
+}
+#endif  // extern "C"
+#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_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/revision.cpp b/libs/vr/libdvrcommon/revision.cpp
new file mode 100644
index 0000000..ae8603f
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision.cpp
@@ -0,0 +1,175 @@
+#include "private/dvr/revision.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <base/logging.h>
+
+#include "revision_path.h"
+
+namespace {
+
+// Allows quicker access to the product revision. If non-zero, then
+// the product revision file has already been processed.
+static bool global_product_revision_processed = false;
+
+static bool global_serial_number_processed = false;
+
+// The product.
+static DvrProduct global_product = DVR_PRODUCT_UNKNOWN;
+
+// The revision.
+static DvrRevision global_revision = DVR_REVISION_UNKNOWN;
+
+// Maximum size of the product revision string.
+constexpr int kProductRevisionStringSize = 32;
+
+// Maximum size of the serial number.
+constexpr int kSerialNumberStringSize = 32;
+
+// The product revision string.
+static char global_product_revision_str[kProductRevisionStringSize + 1] = "";
+
+// The serial number string
+static char global_serial_number[kSerialNumberStringSize + 1] = "";
+
+// Product and revision combinations.
+struct DvrProductRevision {
+  const char* str;
+  DvrProduct product;
+  DvrRevision revision;
+};
+
+// Null-terminated list of all product and revision combinations.
+static constexpr DvrProductRevision kProductRevisions[] = {
+    {"a00-p1", DVR_PRODUCT_A00, DVR_REVISION_P1},
+    {"a00-p2", DVR_PRODUCT_A00, DVR_REVISION_P2},
+    {"a00-p3", DVR_PRODUCT_A00, DVR_REVISION_P3},
+    {"twilight-p1", DVR_PRODUCT_A65R, DVR_REVISION_P1},
+    {"twilight-p2", DVR_PRODUCT_A65R, DVR_REVISION_P2},
+    {NULL, DVR_PRODUCT_UNKNOWN, DVR_REVISION_UNKNOWN}};
+
+// Read the product revision string, and store the global data.
+static void process_product_revision() {
+  int fd;
+  ssize_t read_rc;
+  const DvrProductRevision* product_revision = kProductRevisions;
+
+  // Of course in a multi-threaded environment, for a few microseconds
+  // during process startup, it is possible that this function will be
+  // called and execute fully multiple times. That is why the product
+  // revision string is statically allocated.
+
+  if (global_product_revision_processed)
+    return;
+
+  // Whether there was a failure or not, we don't want to do this again.
+  // Upon failure it's most likely to fail again anyway.
+
+  fd = open(dvr_product_revision_file_path(), O_RDONLY);
+  if (fd < 0) {
+    PLOG(ERROR) << "Could not open '" << dvr_product_revision_file_path()
+                << "' to get product revision";
+    global_product_revision_processed = true;
+    return;
+  }
+
+  read_rc = read(fd, global_product_revision_str, kProductRevisionStringSize);
+  if (read_rc <= 0) {
+    PLOG(ERROR) << "Could not read from '" << dvr_product_revision_file_path()
+                << "'";
+    global_product_revision_processed = true;
+    return;
+  }
+
+  close(fd);
+
+  global_product_revision_str[read_rc] = '\0';
+
+  while (product_revision->str) {
+    if (!strcmp(product_revision->str, global_product_revision_str))
+      break;
+    product_revision++;
+  }
+
+  if (product_revision->str) {
+    global_product = product_revision->product;
+    global_revision = product_revision->revision;
+  } else {
+    LOG(ERROR) << "Unable to match '" << global_product_revision_str
+               << "' to a product/revision.";
+  }
+
+  global_product_revision_processed = true;
+}
+
+}  // anonymous namespace
+
+extern "C" DvrProduct dvr_get_product() {
+  process_product_revision();
+  return global_product;
+}
+
+extern "C" DvrRevision dvr_get_revision() {
+  process_product_revision();
+  return global_revision;
+}
+
+extern "C" const char* dvr_get_product_revision_str() {
+  process_product_revision();
+  return global_product_revision_str;
+}
+
+extern "C" const char* dvr_get_serial_number() {
+  process_product_revision();
+  if (global_product == DVR_PRODUCT_A00) {
+    if (!global_serial_number_processed) {
+#ifdef DVR_HOST
+      global_serial_number_processed = true;
+#else
+      int width = 4;
+      uintptr_t addr = 0x00074138;
+      uintptr_t endaddr = addr + width - 1;
+
+      int fd = open("/dev/mem", O_RDWR | O_SYNC);
+      if (fd < 0) {
+        if (errno == EPERM)
+          global_serial_number_processed = true;
+        fprintf(stderr, "cannot open /dev/mem\n");
+        return global_serial_number;
+      }
+
+      off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
+      size_t mmap_size = endaddr - mmap_start + 1;
+      mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+
+      void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+                          mmap_start);
+
+      if (page == MAP_FAILED) {
+        global_serial_number_processed = true;
+        fprintf(stderr, "cannot mmap region\n");
+        close(fd);
+        return global_serial_number;
+      }
+
+      uint32_t* x =
+          reinterpret_cast<uint32_t*>((((uintptr_t)page) + (addr & 4095)));
+      snprintf(global_serial_number, kSerialNumberStringSize, "%08x", *x);
+      global_serial_number_processed = true;
+
+      munmap(page, mmap_size);
+      close(fd);
+#endif
+    }
+    return global_serial_number;
+  } else {
+    return nullptr;
+  }
+}
diff --git a/libs/vr/libdvrcommon/revision_path.cpp b/libs/vr/libdvrcommon/revision_path.cpp
new file mode 100644
index 0000000..c49f9aa
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision_path.cpp
@@ -0,0 +1,15 @@
+#include "revision_path.h"
+
+namespace {
+
+// The path to the product revision file.
+static const char* kProductRevisionFilePath =
+    "/sys/firmware/devicetree/base/goog,board-revision";
+
+}  // anonymous namespace
+
+// This exists in a separate file so that it can be replaced for
+// testing.
+const char* dvr_product_revision_file_path() {
+  return kProductRevisionFilePath;
+}
diff --git a/libs/vr/libdvrcommon/revision_path.h b/libs/vr/libdvrcommon/revision_path.h
new file mode 100644
index 0000000..afcea46
--- /dev/null
+++ b/libs/vr/libdvrcommon/revision_path.h
@@ -0,0 +1,9 @@
+#ifndef ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
+#define ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
+
+// Returns the revision file path.
+// This exists in a separate file so that it can be replaced for
+// testing.
+const char* dvr_product_revision_file_path();
+
+#endif  // ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_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/libdvrcommon/tests/revision_app_tests.cpp b/libs/vr/libdvrcommon/tests/revision_app_tests.cpp
new file mode 100644
index 0000000..772481b
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/revision_app_tests.cpp
@@ -0,0 +1,34 @@
+#include <dvr/test/app_test.h>
+#include <gtest/gtest.h>
+#include <private/dvr/revision.h>
+
+// Making sure this information is not available
+// inside the sandbox
+
+namespace {
+
+TEST(RevisionTests, GetProduct) {
+  ASSERT_EQ(DVR_PRODUCT_UNKNOWN, dvr_get_product());
+}
+
+TEST(RevisionTests, GetRevision) {
+  ASSERT_EQ(DVR_REVISION_UNKNOWN, dvr_get_revision());
+}
+
+TEST(RevisionTests, GetRevisionStr) {
+  ASSERT_STREQ("", dvr_get_product_revision_str());
+}
+
+TEST(RevisionTests, GetSerialNo) {
+  ASSERT_EQ(nullptr, dvr_get_serial_number());
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  dreamos::test::AppTestBegin();
+  ::testing::InitGoogleTest(&argc, argv);
+  int result = RUN_ALL_TESTS();
+  dreamos::test::AppTestEnd(result);
+  return result;
+}
diff --git a/libs/vr/libdvrcommon/tests/revision_tests.cpp b/libs/vr/libdvrcommon/tests/revision_tests.cpp
new file mode 100644
index 0000000..9abf480
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/revision_tests.cpp
@@ -0,0 +1,27 @@
+#include <gtest/gtest.h>
+#include <private/dvr/revision.h>
+
+namespace {
+
+TEST(RevisionTests, GetProduct) {
+  ASSERT_NE(DVR_PRODUCT_UNKNOWN, dvr_get_product());
+}
+
+TEST(RevisionTests, GetRevision) {
+  ASSERT_NE(DVR_REVISION_UNKNOWN, dvr_get_revision());
+}
+
+TEST(RevisionTests, GetRevisionStr) {
+  ASSERT_NE(nullptr, dvr_get_product_revision_str());
+}
+
+TEST(RevisionTests, GetSerialNo) {
+  ASSERT_NE(nullptr, dvr_get_serial_number());
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/libs/vr/libdvrgraphics/Android.mk b/libs/vr/libdvrgraphics/Android.mk
new file mode 100644
index 0000000..3d84319
--- /dev/null
+++ b/libs/vr/libdvrgraphics/Android.mk
@@ -0,0 +1,35 @@
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	blur.cpp \
+	debug_text.cpp \
+	egl_image.cpp \
+	gpu_profiler.cpp \
+	shader_program.cpp \
+	timer_query.cpp \
+	vr_gl_extensions.cpp \
+
+includeFiles := \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	libchrome \
+	libbufferhub \
+	libdvrcommon \
+	libpdx_default_transport \
+
+sharedLibraries := \
+	libcutils \
+	libbase \
+	libpng
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libdvrgraphics
+include $(BUILD_STATIC_LIBRARY)
+
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..7365b0e
--- /dev/null
+++ b/libs/vr/libdvrgraphics/blur.cpp
@@ -0,0 +1,247 @@
+#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 <base/logging.h>
+#include <base/strings/string_number_conversions.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) {
+  CHECK(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) {
+  CHECK(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..d252a34
--- /dev/null
+++ b/libs/vr/libdvrgraphics/gpu_profiler.cpp
@@ -0,0 +1,248 @@
+#include "include/private/dvr/graphics/gpu_profiler.h"
+
+#include <cutils/log.h>
+
+#include <private/dvr/clock_ns.h>
+
+namespace android {
+namespace dvr {
+
+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) {
+  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) {
+  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),
+      sync_with_cpu_time_(false),
+      gl_timer_offset_ns_(0) {
+  SyncGlTimebase();
+}
+
+GpuProfiler::~GpuProfiler() {}
+
+bool GpuProfiler::IsGpuProfilingSupported() const {
+  // TODO(jbates) check for GL_EXT_disjoint_timer_query
+  return true;
+}
+
+GLuint GpuProfiler::TryAllocateGlQueryId() {
+  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::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..2905d00
--- /dev/null
+++ b/libs/vr/libdvrgraphics/include/private/dvr/graphics/gpu_profiler.h
@@ -0,0 +1,215 @@
+#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_; }
+
+  // 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);
+
+ 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);
+
+    int64_t total_elapsed_ns = 0;
+    int64_t enter_timestamp_ns = 0;
+    int num_events = 0;
+  };
+
+  // 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_;
+
+  // 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..bf36eff
--- /dev/null
+++ b/libs/vr/libdvrgraphics/shader_program.cpp
@@ -0,0 +1,165 @@
+#include "include/private/dvr/graphics/shader_program.h"
+
+#include <regex>
+#include <sstream>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+
+namespace {
+
+static bool CompileShader(GLuint shader, const std::string& shader_string) {
+  std::string prefix = "";
+  if (!base::StartsWith(shader_string, "#version",
+                        base::CompareCase::SENSITIVE)) {
+    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);
+    LOG(ERROR) << "Shader Failed to compile: " << *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);
+    LOG(ERROR) << "Shader failed to link: " << 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);
+    LOG(ERROR) << "Shader failed to link: " << 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..dcc6216
--- /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 <base/logging.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) {
+    LOG(ERROR) << "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) {
+    LOG(ERROR) << "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.mk b/libs/vr/libeds/Android.mk
new file mode 100644
index 0000000..0345f6d
--- /dev/null
+++ b/libs/vr/libeds/Android.mk
@@ -0,0 +1,89 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	eds.cpp \
+	eds_mesh.cpp \
+	composite_hmd.cpp \
+	cpu_thread_pose_updater.cpp \
+	display_metrics.cpp \
+	distortion_renderer.cpp \
+	lucid_metrics.cpp \
+	lucid_pose_tracker.cpp \
+	lookup_radial_distortion.cpp \
+	polynomial_radial_distortion.cpp
+
+includeFiles += \
+	$(LOCAL_PATH)/include
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	liblog \
+	libEGL \
+	libGLESv1_CM \
+	libGLESv2 \
+	libvulkan
+
+staticLibraries := \
+	libchrome \
+	libdisplay \
+	libdvrcommon \
+	libdvrgraphics \
+	libsensor \
+	libpdx_default_transport \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -Wno-unused-parameter
+# Enable debug options below to show GL errors and use gdb.
+# LOCAL_CFLAGS += -UNDEBUG -DDEBUG -O0 -g
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libeds
+include $(BUILD_STATIC_LIBRARY)
+
+
+testFiles := \
+  tests/eds_app_tests.cpp
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := eds_app_tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+  libhardware \
+  libsync \
+  $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+  libgmock_main \
+  libgmock \
+  libdisplay \
+  libeds \
+  libbufferhub \
+  $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libeds/composite_hmd.cpp b/libs/vr/libeds/composite_hmd.cpp
new file mode 100644
index 0000000..d29cd65
--- /dev/null
+++ b/libs/vr/libeds/composite_hmd.cpp
@@ -0,0 +1,256 @@
+#include "include/private/dvr/composite_hmd.h"
+
+#include <base/logging.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;
+
+  CHECK_NE(0.0f, display_width_meters);
+  CHECK_NE(0.0f, display_height_meters);
+  CHECK_NE(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/cpu_thread_pose_updater.cpp b/libs/vr/libeds/cpu_thread_pose_updater.cpp
new file mode 100644
index 0000000..5b8a734
--- /dev/null
+++ b/libs/vr/libeds/cpu_thread_pose_updater.cpp
@@ -0,0 +1,86 @@
+#include "include/private/dvr/cpu_thread_pose_updater.h"
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#define ATRACE_TAG ATRACE_TAG_INPUT
+#include <utils/Trace.h>
+
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+
+namespace android {
+namespace dvr {
+
+CpuThreadPoseUpdater::CpuThreadPoseUpdater()
+    : stop_request_(false), update_period_us_(0), count_(0) {}
+
+CpuThreadPoseUpdater::~CpuThreadPoseUpdater() { StopAndJoin(); }
+
+void CpuThreadPoseUpdater::Start(volatile RawPosePair* mapped_pose_buffer,
+                                 int period_us) {
+  mapped_pose_buffer_ = mapped_pose_buffer;
+  update_period_us_ = period_us;
+  stop_request_ = false;
+
+  // First buffer is odd (starts at 1), second is even (starts at 2).
+  count_ = 0;
+  mapped_pose_buffer_->pose1.Reset(++count_);
+  mapped_pose_buffer_->pose2.Reset(++count_);
+
+  update_thread_ = std::thread(&CpuThreadPoseUpdater::UpdateThread, this);
+}
+
+void CpuThreadPoseUpdater::StopAndJoin() {
+  stop_request_ = true;
+  if (update_thread_.joinable()) {
+    update_thread_.join();
+  }
+}
+
+void CpuThreadPoseUpdater::UpdateThread() {
+  prctl(PR_SET_NAME, reinterpret_cast<intptr_t>("CpuPoseUpdater"),
+        0, 0, 0);
+
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+  for (;;) {
+    if (stop_request_) {
+      break;
+    }
+
+    ++count_;
+
+    // Choose the writable pose based on whether count is odd or even.
+    volatile RawPose* out_pose = nullptr;
+    if (count_ & 1) {
+      out_pose = &mapped_pose_buffer_->pose1;
+    } else {
+      out_pose = &mapped_pose_buffer_->pose2;
+    }
+
+    {
+      ATRACE_NAME("GetPose");
+      Posef pose = pose_tracker_.GetPose(GetSystemClockNs());
+      out_pose->qx = pose.GetRotation().x();
+      out_pose->qy = pose.GetRotation().y();
+      out_pose->qz = pose.GetRotation().z();
+      out_pose->qw = pose.GetRotation().w();
+      out_pose->px = pose.GetPosition()[0];
+      out_pose->py = pose.GetPosition()[1];
+      out_pose->pz = pose.GetPosition()[2];
+      // Atomically store the count so that it hits memory last:
+      out_pose->count.store(count_, std::memory_order_release);
+    }
+
+    // Sleep to simulate the IMU update process.
+    usleep(update_period_us_);
+    // TODO(jbates) sleep_for returns immediately, we need to fix our toolchain!
+    // int64_t c1 = GetSystemClockNs();
+    // std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    // int64_t c2 = GetSystemClockNs();
+    // fprintf(stderr, "%lld us\n", (long long)(c2 - c1) / 1000);
+  }
+}
+
+}  // namesapce dvr
+}  // namesapce 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..a19843f
--- /dev/null
+++ b/libs/vr/libeds/distortion_renderer.cpp
@@ -0,0 +1,793 @@
+#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 <base/logging.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 {
+    CHECK_EQ(num_layers, 1);
+  }
+  if (blend_with_previous_layer) {
+    // Check for unsupported shader combinations:
+    CHECK_EQ(num_layers, 1);
+    CHECK_EQ(use_alpha_vignette, false);
+    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);
+  CHECK(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());
+
+  CHECK(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) {
+      LOG(ERROR) << "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) {
+    LOG(ERROR) << "Too many textures for DistortionRenderer";
+    num_textures = kMaxLayers;
+  }
+
+  CHECK(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) {
+    LOG(ERROR) << "Accessing invalid layer " << layer_id << std::endl;
+    return false;
+  }
+
+  if (late_latch_[layer_id] != nullptr) {
+    late_latch_[layer_id]->CaptureOutputData(out_data);
+    return true;
+  } else {
+    LOG(ERROR) << "Late latch shader not enabled." << std::endl;
+    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..2c7dc2f
--- /dev/null
+++ b/libs/vr/libeds/eds_mesh.cpp
@@ -0,0 +1,136 @@
+#include "include/private/dvr/eds_mesh.h"
+
+#include <math.h>
+
+#include <base/logging.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) {
+  CHECK_GT(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/cpu_thread_pose_updater.h b/libs/vr/libeds/include/private/dvr/cpu_thread_pose_updater.h
new file mode 100644
index 0000000..6a2c8a6
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/cpu_thread_pose_updater.h
@@ -0,0 +1,48 @@
+#ifndef ANDROID_DVR_CPU_THREAD_POSE_UPDATER_H_
+#define ANDROID_DVR_CPU_THREAD_POSE_UPDATER_H_
+
+#include <atomic>
+#include <thread>
+
+#include <private/dvr/lucid_pose_tracker.h>
+#include <private/dvr/raw_pose.h>
+
+namespace android {
+namespace dvr {
+
+// Temporary version of pose updater that uses a CPU thread to update
+// the pose buffer. Note that this thread starts and runs indefinitely
+class CpuThreadPoseUpdater {
+ public:
+  CpuThreadPoseUpdater();
+  ~CpuThreadPoseUpdater();
+
+  // Start the thread to update the given buffer with the given number of
+  // microseconds between updates.
+  void Start(volatile RawPosePair* mapped_pose_buffer, int period_us);
+
+  void StopAndJoin();
+
+ private:
+  void UpdateThread();
+
+  volatile RawPosePair* mapped_pose_buffer_;
+
+  // Pose update thread.
+  std::thread update_thread_;
+
+  volatile bool stop_request_;
+
+  // Update period in microseconds.
+  int update_period_us_;
+
+  // Current pose count, used to avoid writing to the same buffer that is being
+  // read by the GPU.
+  uint32_t count_;
+  LucidPoseTracker pose_tracker_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_CPU_THREAD_POSE_UPDATER_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..e1c8114
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/distortion_renderer.h
@@ -0,0 +1,233 @@
+#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/lucid_pose_tracker.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/lookup_radial_distortion.h b/libs/vr/libeds/include/private/dvr/lookup_radial_distortion.h
new file mode 100644
index 0000000..56fc5db
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/lookup_radial_distortion.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_LOOKUP_RADIAL_DISTORTION_H_
+#define ANDROID_DVR_LOOKUP_RADIAL_DISTORTION_H_
+
+#include <vector>
+
+#include <private/dvr/color_channel_distortion.h>
+
+namespace android {
+namespace dvr {
+
+// LookupRadialDistortion implements a radial distortion based using using a
+// vector of tan(angle) -> multipliers.  This can use measured data directly.
+class LookupRadialDistortion : public ColorChannelDistortion {
+ public:
+  // lookup.x = tan(angle), lookup.y = distance from center multiplier.
+  explicit LookupRadialDistortion(const vec2* lookup, size_t count);
+
+  vec2 Distort(vec2 p) const override;
+  vec2 DistortInverse(vec2 p) const override;
+
+ private:
+  float DistortionFactor(float r) const;
+  float DistortRadius(float r) const;
+
+  std::vector<vec2> lookup_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LOOKUP_RADIAL_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/lucid_metrics.h b/libs/vr/libeds/include/private/dvr/lucid_metrics.h
new file mode 100644
index 0000000..0e4ada4
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/lucid_metrics.h
@@ -0,0 +1,22 @@
+#ifndef ANDROID_DVR_LUCID_METRICS_H_
+#define ANDROID_DVR_LUCID_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_LUCID_METRICS_H_
diff --git a/libs/vr/libeds/include/private/dvr/lucid_pose_tracker.h b/libs/vr/libeds/include/private/dvr/lucid_pose_tracker.h
new file mode 100644
index 0000000..4ceda5a
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/lucid_pose_tracker.h
@@ -0,0 +1,49 @@
+#ifndef ANDROID_DVR_LUCID_POSE_TRACKER_H_
+#define ANDROID_DVR_LUCID_POSE_TRACKER_H_
+
+#include <memory>
+
+#include <dvr/pose_client.h>
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// Provides pose tracking via the system pose service.
+class LucidPoseTracker {
+ public:
+  // When set, the pose service is ignored and the given pose is always returned
+  // by GetPose. As long as this is called before any LucidPoseTracker is
+  // used, the pose service will not be created.
+  // Threading: this is not thread safe.
+  static void SetPoseOverride(const Posef& pose);
+
+  // Reset prior override pose.
+  static void ClearPoseOverride();
+
+  LucidPoseTracker();
+  ~LucidPoseTracker();
+
+  // Currently GetPose() will ignore timestamp_ns and always return the most
+  // recent orientation.
+  // TODO(stefanus): support prediction.
+  Posef GetPose(uint64_t timestamp_ns);
+
+ private:
+  static bool is_override_pose_;
+  static Posef override_pose_;
+
+  DvrPose* pose_client_;
+
+  // The most recent pose.
+  Posef latest_pose_;
+
+  // The time stamp corresponding to when latest_pose_ was last updated.
+  uint64_t latest_timestamp_ns_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LUCID_POSE_TRACKER_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/lookup_radial_distortion.cpp b/libs/vr/libeds/lookup_radial_distortion.cpp
new file mode 100644
index 0000000..2cee863
--- /dev/null
+++ b/libs/vr/libeds/lookup_radial_distortion.cpp
@@ -0,0 +1,47 @@
+#include "include/private/dvr/lookup_radial_distortion.h"
+
+namespace android {
+namespace dvr {
+
+LookupRadialDistortion::LookupRadialDistortion(const vec2* lookup, size_t count)
+    : lookup_(lookup, lookup + count) {}
+
+float LookupRadialDistortion::DistortionFactor(float r) const {
+  for (size_t i = 1; i < lookup_.size(); ++i) {
+    if (lookup_[i].x() > r) {
+      float t =
+          (r - lookup_[i - 1].x()) / (lookup_[i].x() - lookup_[i - 1].x());
+      return lookup_[i - 1].y() + t * (lookup_[i].y() - lookup_[i - 1].y());
+    }
+  }
+  return lookup_.back().y();
+}
+
+float LookupRadialDistortion::DistortRadius(float r) const {
+  return r * DistortionFactor(r);
+}
+
+vec2 LookupRadialDistortion::Distort(vec2 p) const {
+  return p * DistortionFactor(p.norm());
+}
+
+vec2 LookupRadialDistortion::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;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/lucid_metrics.cpp b/libs/vr/libeds/lucid_metrics.cpp
new file mode 100644
index 0000000..690c326
--- /dev/null
+++ b/libs/vr/libeds/lucid_metrics.cpp
@@ -0,0 +1,327 @@
+#include "include/private/dvr/display_metrics.h"
+#include <private/dvr/head_mount_metrics.h>
+#include <private/dvr/identity_distortion.h>
+#include <private/dvr/lookup_radial_distortion.h>
+#include <private/dvr/lucid_metrics.h>
+#include <private/dvr/types.h>
+
+namespace {
+
+// These numbers are specific to the OnePlus One and therefore
+// temporary until we advance to the next Lucid development platform.
+
+// Head mount metrics for Lucid A00
+static const float kDefaultInterLensDistance = 0.064f;  // 64mm
+static const float kDefaultTrayToLensDistance = 0.035f;
+static const float kDefaultVirtualEyeToScreenDistance = 0.042f;
+static const android::dvr::HeadMountMetrics::VerticalAlignment
+    kDefaultVerticalAlignment = android::dvr::HeadMountMetrics::kCenter;
+static const float kDefaultFovHalfAngleInsideH = 43.7f * M_PI / 180.0f;
+static const float kDefaultFovHalfAngleOutsideH = 47.8f * M_PI / 180.0f;
+static const float kDefaultFovHalfAngleV = 54.2f * M_PI / 180.0f;
+
+// Screen size in meters for Lucid (Nexus 6 display in portrait mode).
+static const android::dvr::vec2 kScreenSizeInMeters(0.0742177f, 0.131943f);
+
+// Border size in meters for the OnePlus One.
+static const float kScreenBorderSize = 0.004f;
+
+// Refresh rate.
+static const float kScreenRefreshRate = 60.0f;
+
+// Lucid display orientation is portrait.
+static const android::dvr::DisplayOrientation kDisplayOrientation =
+    android::dvr::DisplayOrientation::kPortrait;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// The distortion lookup tables were generated via a raytraced lens simulation.
+// Please see for full calculations:
+// https://docs.google.com/a/google.com/spreadsheets/d/
+//       15cfHmCw5mHVOQ1rAJxMhta4q0e8zzcUDka1nRkfl7pY/edit?usp=sharing
+LookupRadialDistortion* GetBlueDistortionLookup() {
+  // clang-format off
+  vec2 kBlueDistortionLookup[] = {
+    {0.00000000000f, 1.00000000000f},
+    {0.01888626190f, 1.00096958278f},
+    {0.03777223810f, 1.00133301793f},
+    {0.05665761905f, 1.00193985168f},
+    {0.07554214286f, 1.00279048731f},
+    {0.09442542857f, 1.00388751781f},
+    {0.11330704762f, 1.00523363045f},
+    {0.13218657143f, 1.00683149424f},
+    {0.15106340476f, 1.00868516849f},
+    {0.16993695238f, 1.01079861126f},
+    {0.18880640476f, 1.01317712726f},
+    {0.20767092857f, 1.01582607321f},
+    {0.22652945238f, 1.01875203063f},
+    {0.24538078571f, 1.02196207850f},
+    {0.26422352381f, 1.02546421601f},
+    {0.28305602381f, 1.02926737969f},
+    {0.30187640476f, 1.03338139216f},
+    {0.32068252381f, 1.03781702504f},
+    {0.33947190476f, 1.04258620905f},
+    {0.35824171429f, 1.04770206653f},
+    {0.37698869048f, 1.05317909331f},
+    {0.39570916667f, 1.05903306635f},
+    {0.41439900000f, 1.06528124790f},
+    {0.43305350000f, 1.07194257391f},
+    {0.45166738095f, 1.07903777957f},
+    {0.47023471429f, 1.08658953759f},
+    {0.48874897619f, 1.09462239798f},
+    {0.50720285714f, 1.10316330018f},
+    {0.52558835714f, 1.11224144183f},
+    {0.54389669048f, 1.12188861421f},
+    {0.56211826190f, 1.13213939967f},
+    {0.58024261905f, 1.14303145047f},
+    {0.59825847619f, 1.15460566091f},
+    {0.61615335714f, 1.16690711338f},
+    {0.63391345238f, 1.17998560444f},
+    {0.65152300000f, 1.19389708987f},
+    {0.66896328571f, 1.20870580446f},
+    {0.68621100000f, 1.22448751087f},
+    {0.70323578571f, 1.24133415620f},
+    {0.71999716667f, 1.25935962776f},
+    {0.73643969048f, 1.27870875648f},
+    {0.75250778571f, 1.29953256670f},
+    {0.76817614286f, 1.32193822000f},
+    {0.78342009524f, 1.34604270338f},
+    {0.79828314286f, 1.37185833833f},
+    {0.81267376190f, 1.39964322604f},
+    {0.82656559524f, 1.42955958262f},
+    {0.83983054762f, 1.46196539657f},
+    {0.85234333333f, 1.49724142650f},
+    {0.86394971429f, 1.53585530271f},
+    {0.87422461905f, 1.57881139444f},
+    {0.88382583095f, 1.62091537826f},
+    {0.89571361286f, 1.67610209261f},
+    {0.90490389167f, 1.72118819668f},
+    {0.91526452143f, 1.77496904481f},
+    {0.92651365452f, 1.83722833673f},
+    {0.93437489976f, 1.88337590145f},
+    {0.94654105500f, 1.95937892848f},
+    {0.95476685095f, 2.01469745492f},
+    {0.96720383310f, 2.10451495481f},
+    {0.97546726405f, 2.16904926656f},
+    {0.98774046786f, 2.27302748020f},
+    {0.99579206762f, 2.34720582421f},
+    {1.00763328857f, 2.46603526105f},
+    {1.01533118405f, 2.55049232288f},
+    {1.02287120929f, 2.63936582235f}
+  };
+  // clang-format on
+  return new LookupRadialDistortion(
+      kBlueDistortionLookup, sizeof(kBlueDistortionLookup) / sizeof(vec2));
+}
+
+LookupRadialDistortion* GetGreenDistortionLookup() {
+  // clang-format off
+  vec2 kGreenDistortionLookup[] = {
+    {0.00000000000f, 1.00000000000f},
+    {0.01898883333f, 1.00000000000f},
+    {0.03797750000f, 1.00000000000f},
+    {0.05696585714f, 1.00000000000f},
+    {0.07595369048f, 1.00000000000f},
+    {0.09494078571f, 1.00000000000f},
+    {0.11392685714f, 1.00000000000f},
+    {0.13291157143f, 1.00000000000f},
+    {0.15189450000f, 1.00176560670f},
+    {0.17087511905f, 1.00384553961f},
+    {0.18985280952f, 1.00618614484f},
+    {0.20882680952f, 1.00879302066f},
+    {0.22779623810f, 1.01167234096f},
+    {0.24675997619f, 1.01483135203f},
+    {0.26571680952f, 1.01827767641f},
+    {0.28466519048f, 1.02202026825f},
+    {0.30360342857f, 1.02606859705f},
+    {0.32252950000f, 1.03043334057f},
+    {0.34144104762f, 1.03512630376f},
+    {0.36033538095f, 1.04016038545f},
+    {0.37920942857f, 1.04554970984f},
+    {0.39805966667f, 1.05130981266f},
+    {0.41688209524f, 1.05745768999f},
+    {0.43567214286f, 1.06401204155f},
+    {0.45442473810f, 1.07099310305f},
+    {0.47313411905f, 1.07842314596f},
+    {0.49179388095f, 1.08632639514f},
+    {0.51039692857f, 1.09472920992f},
+    {0.52893538095f, 1.10366038032f},
+    {0.54740061905f, 1.11315113705f},
+    {0.56578326190f, 1.12323535769f},
+    {0.58407300000f, 1.13395008040f},
+    {0.60225871429f, 1.14533547370f},
+    {0.62032809524f, 1.15743581542f},
+    {0.63826750000f, 1.17030000749f},
+    {0.65606135714f, 1.18398295206f},
+    {0.67369107143f, 1.19854780583f},
+    {0.69113350000f, 1.21406895255f},
+    {0.70835842857f, 1.23063670464f},
+    {0.72532545238f, 1.24836302903f},
+    {0.74197478571f, 1.26739777609f},
+    {0.75822164286f, 1.28793886907f},
+    {0.77407361905f, 1.31003521318f},
+    {0.78948523810f, 1.33383710115f},
+    {0.80448471429f, 1.35938255065f},
+    {0.81901733333f, 1.38686361242f},
+    {0.83305214286f, 1.41644808409f},
+    {0.84646438095f, 1.44848277406f},
+    {0.85912733333f, 1.48334485259f},
+    {0.87088369048f, 1.52149970074f},
+    {0.88131250000f, 1.56392750036f},
+    {0.89105132929f, 1.60552684742f},
+    {0.90312479476f, 1.66002695068f},
+    {0.91244067452f, 1.70458805205f},
+    {0.92297971714f, 1.75767475825f},
+    {0.93440940905f, 1.81916050294f},
+    {0.94237194976f, 1.86478635937f},
+    {0.95471202405f, 1.93989738862f},
+    {0.96305355738f, 1.99457325750f},
+    {0.97567372071f, 2.08333293385f},
+    {0.98407229071f, 2.14708073108f},
+    {0.99653762071f, 2.24981649552f},
+    {1.00471276167f, 2.32311751786f},
+    {1.01672394000f, 2.44057411530f},
+    {1.02452363381f, 2.52407947994f},
+    {1.03216732667f, 2.61194301580f}
+  };
+  // clang-format on
+  return new LookupRadialDistortion(
+      kGreenDistortionLookup, sizeof(kGreenDistortionLookup) / sizeof(vec2));
+}
+
+LookupRadialDistortion* GetRedDistortionLookup() {
+  // clang-format off
+  vec2 kRedDistortionLookup[] = {
+    {0.00000000000f, 1.00000000000f},
+    {0.01906776190f, 1.00000000000f},
+    {0.03813547619f, 1.00000000000f},
+    {0.05720304762f, 1.00000000000f},
+    {0.07627040476f, 1.00000000000f},
+    {0.09533740476f, 1.00000000000f},
+    {0.11440385714f, 1.00000000000f},
+    {0.13346952381f, 1.00000000000f},
+    {0.15253409524f, 1.00000000000f},
+    {0.17159714286f, 1.00000000000f},
+    {0.19065814286f, 1.00053530030f},
+    {0.20971645238f, 1.00310924426f},
+    {0.22877123810f, 1.00595236192f},
+    {0.24782154762f, 1.00907150786f},
+    {0.26686623810f, 1.01247435420f},
+    {0.28590388095f, 1.01616968529f},
+    {0.30493288095f, 1.02016688932f},
+    {0.32395133333f, 1.02447646681f},
+    {0.34295697619f, 1.02911011406f},
+    {0.36194726190f, 1.03408046560f},
+    {0.38091921429f, 1.03940151599f},
+    {0.39986942857f, 1.04508858434f},
+    {0.41879402381f, 1.05115843585f},
+    {0.43768857143f, 1.05762946333f},
+    {0.45654809524f, 1.06452169646f},
+    {0.47536695238f, 1.07185711363f},
+    {0.49413888095f, 1.07965956927f},
+    {0.51285690476f, 1.08795508025f},
+    {0.53151326190f, 1.09677206014f},
+    {0.55009952381f, 1.10614118417f},
+    {0.56860633333f, 1.11609607621f},
+    {0.58702361905f, 1.12667304464f},
+    {0.60534028571f, 1.13791190276f},
+    {0.62354421429f, 1.14985618930f},
+    {0.64162188095f, 1.16255413653f},
+    {0.65955780952f, 1.17605992962f},
+    {0.67733352381f, 1.19043584317f},
+    {0.69492602381f, 1.20575517508f},
+    {0.71230514286f, 1.22210708787f},
+    {0.72943057143f, 1.23960199799f},
+    {0.74623921429f, 1.25839340501f},
+    {0.76262400000f, 1.27871385661f},
+    {0.77861754762f, 1.30056919119f},
+    {0.79415866667f, 1.32413401001f},
+    {0.80926385714f, 1.34946540639f},
+    {0.82390640476f, 1.37670655635f},
+    {0.83805190476f, 1.40602920817f},
+    {0.85157807143f, 1.43777181543f},
+    {0.86435700000f, 1.47230885729f},
+    {0.87622914286f, 1.51010361811f},
+    {0.88677650000f, 1.55211817236f},
+    {0.89663317738f, 1.59330127207f},
+    {0.90883197952f, 1.64729627820f},
+    {0.91827594357f, 1.69138814689f},
+    {0.92892199405f, 1.74398939784f},
+    {0.94047261548f, 1.80490554711f},
+    {0.94852659262f, 1.85009630648f},
+    {0.96099790167f, 1.92451421938f},
+    {0.96945317500f, 1.97863645920f},
+    {0.98221554286f, 2.06656418112f},
+    {0.99069599476f, 2.12974390154f},
+    {1.00331392976f, 2.23149730290f},
+    {1.01157138762f, 2.30414058939f},
+    {1.02372409452f, 2.42049694265f},
+    {1.03162992905f, 2.50318810924f},
+    {1.03934762000f, 2.59027212626f}
+  };
+  // clang-format on
+  return new LookupRadialDistortion(
+      kRedDistortionLookup, sizeof(kRedDistortionLookup) / sizeof(vec2));
+}
+
+HeadMountMetrics CreateHeadMountMetrics(const FieldOfView& l_fov,
+                                        const FieldOfView& r_fov) {
+  std::shared_ptr<ColorChannelDistortion> default_distortion_r(
+      GetRedDistortionLookup());
+  std::shared_ptr<ColorChannelDistortion> default_distortion_g(
+      GetGreenDistortionLookup());
+  std::shared_ptr<ColorChannelDistortion> default_distortion_b(
+      GetBlueDistortionLookup());
+
+  return HeadMountMetrics(
+      kDefaultInterLensDistance, kDefaultTrayToLensDistance,
+      kDefaultVirtualEyeToScreenDistance, kDefaultVerticalAlignment, l_fov,
+      r_fov, default_distortion_r, default_distortion_g, default_distortion_b,
+      HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+      HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+      kDefaultInterLensDistance / 2.0f);
+}
+
+HeadMountMetrics CreateHeadMountMetrics() {
+  FieldOfView l_fov(kDefaultFovHalfAngleOutsideH, kDefaultFovHalfAngleInsideH,
+                    kDefaultFovHalfAngleV, kDefaultFovHalfAngleV);
+  FieldOfView r_fov(kDefaultFovHalfAngleInsideH, kDefaultFovHalfAngleOutsideH,
+                    kDefaultFovHalfAngleV, kDefaultFovHalfAngleV);
+
+  return CreateHeadMountMetrics(l_fov, r_fov);
+}
+
+DisplayMetrics CreateDisplayMetrics(vec2i screen_size) {
+  vec2 meters_per_pixel(
+      kScreenSizeInMeters[0] / static_cast<float>(screen_size[0]),
+      kScreenSizeInMeters[1] / static_cast<float>(screen_size[1]));
+  return DisplayMetrics(screen_size, meters_per_pixel, kScreenBorderSize,
+                        1000.0f / kScreenRefreshRate, kDisplayOrientation);
+}
+
+HeadMountMetrics CreateUndistortedHeadMountMetrics() {
+  FieldOfView l_fov(kDefaultFovHalfAngleOutsideH, kDefaultFovHalfAngleInsideH,
+                    kDefaultFovHalfAngleV, kDefaultFovHalfAngleV);
+  FieldOfView r_fov(kDefaultFovHalfAngleInsideH, kDefaultFovHalfAngleOutsideH,
+                    kDefaultFovHalfAngleV, kDefaultFovHalfAngleV);
+  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(kDefaultInterLensDistance, kDefaultTrayToLensDistance,
+                          kDefaultVirtualEyeToScreenDistance,
+                          kDefaultVerticalAlignment, l_fov, r_fov,
+                          distortion_all, distortion_all, distortion_all,
+                          HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+                          HeadMountMetrics::EyeOrientation::kCCW0Degrees,
+                          kDefaultInterLensDistance / 2.0f);
+}
+
+}  // namespace dvr
+}  // namespace dvr
diff --git a/libs/vr/libeds/lucid_pose_tracker.cpp b/libs/vr/libeds/lucid_pose_tracker.cpp
new file mode 100644
index 0000000..c321bb0
--- /dev/null
+++ b/libs/vr/libeds/lucid_pose_tracker.cpp
@@ -0,0 +1,90 @@
+#include "include/private/dvr/lucid_pose_tracker.h"
+
+#define LOG_TAG "LucidPoseTracker"
+#include <cutils/log.h>
+
+#include <private/dvr/clock_ns.h>
+
+namespace android {
+namespace dvr {
+
+bool LucidPoseTracker::is_override_pose_ = false;
+Posef LucidPoseTracker::override_pose_ = Posef();
+
+void LucidPoseTracker::SetPoseOverride(const Posef& pose) {
+  is_override_pose_ = true;
+  override_pose_ = pose;
+}
+
+void LucidPoseTracker::ClearPoseOverride() {
+  is_override_pose_ = false;
+  override_pose_ = Posef();
+}
+
+LucidPoseTracker::LucidPoseTracker() : pose_client_(NULL) {}
+
+LucidPoseTracker::~LucidPoseTracker() {
+  if (pose_client_) {
+    dvrPoseDestroy(pose_client_);
+  }
+}
+
+Posef LucidPoseTracker::GetPose(uint64_t timestamp_ns) {
+  if (is_override_pose_) {
+    return override_pose_;
+  }
+
+  if (!pose_client_) {
+    pose_client_ = dvrPoseCreate();
+
+    if (!pose_client_) {
+      ALOGE("No pose service, returning identity pose");
+      return Posef();
+    }
+  }
+
+  DvrPoseState state;
+  dvrPosePoll(pose_client_, &state);
+
+  const vec4 head_rotation_in_start_quat(
+      state.head_from_start_rotation.x, state.head_from_start_rotation.y,
+      state.head_from_start_rotation.z, state.head_from_start_rotation.w);
+
+  // When the pose service hasn't computed a pose yet, it returns a zero
+  // quaternion; just use the identity rotation in that case.
+  // TODO(stefanus): Find a better way to signal and check this.
+  if (head_rotation_in_start_quat.squaredNorm() < 0.5f) {
+    latest_pose_.SetRotation(quat::Identity());
+  } else {
+    latest_pose_.SetRotation(
+        quat(head_rotation_in_start_quat.w(), head_rotation_in_start_quat.x(),
+             head_rotation_in_start_quat.y(), head_rotation_in_start_quat.z())
+            .normalized());
+  }
+
+  const vec3 head_position_in_start(state.head_from_start_translation.x,
+                                    state.head_from_start_translation.y,
+                                    state.head_from_start_translation.z);
+  latest_pose_.SetPosition(head_position_in_start);
+
+  latest_timestamp_ns_ = GetSystemClockNs();
+
+  // PoseState pose_state;
+  // pose_state.timestamp_ns = latest_timestamp_ns_;
+  // pose_state.sensor_from_start_rotation =
+  //    ion::math::Rotationd::FromQuaternion(ion::math::Vector4d(
+  //        state.head_from_start_rotation.x, state.head_from_start_rotation.y,
+  //        state.head_from_start_rotation.z,
+  //        state.head_from_start_rotation.w));
+  //// TODO(stefanus): Determine the first derivative of the rotation and set it
+  //// here.
+  // pose_state.sensor_from_start_rotation_velocity =
+  // ion::math::Vector3d::Zero();
+
+  // TODO(stefanus): perform prediction.
+
+  return latest_pose_;
+}
+
+}  // namespace dvr
+}  // namespace android
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..1742736
--- /dev/null
+++ b/libs/vr/libeds/tests/eds_app_tests.cpp
@@ -0,0 +1,141 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include <base/logging.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/libgvr/.clang-format b/libs/vr/libgvr/.clang-format
new file mode 100644
index 0000000..3287160
--- /dev/null
+++ b/libs/vr/libgvr/.clang-format
@@ -0,0 +1,2 @@
+Language: Cpp
+DisableFormat: true
diff --git a/libs/vr/libgvr/Android.mk b/libs/vr/libgvr/Android.mk
new file mode 100644
index 0000000..0fcf94b
--- /dev/null
+++ b/libs/vr/libgvr/Android.mk
@@ -0,0 +1,133 @@
+# 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_dirs := \
+  $(LOCAL_PATH)/include \
+  $(LOCAL_PATH)/prebuilt/include
+
+# Java platform library for the system implementation of the GVR API.
+include $(CLEAR_VARS)
+LOCAL_MODULE := gvr_platform
+LOCAL_MODULE_STEM := com.google.vr.gvr.platform
+LOCAL_REQUIRED_MODULES := libgvr_system_loader libgvr_system
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+include $(BUILD_JAVA_LIBRARY)
+
+# Library to perform dlopen on the actual platform library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr_system_loader
+LOCAL_SRC_FILES := library_loader.cpp
+include $(BUILD_SHARED_LIBRARY)
+
+# Shared library implementing the GVR API.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr_system
+
+LOCAL_SRC_FILES := \
+    shim_gvr.cpp \
+    shim_gvr_controller.cpp \
+    shim_gvr_private.cpp \
+    deviceparams/CardboardDevice.nolite.proto
+
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+LOCAL_C_INCLUDES := $(include_dirs)
+LOCAL_C_INCLUDES += $(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_dirs)
+
+gvr_api_linker_script := $(LOCAL_PATH)/exported_apis.lds
+LOCAL_ADDITIONAL_DEPENDENCIES := $(gvr_api_linker_script)
+
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES
+LOCAL_LDFLAGS += -Wl,-version-script,$(gvr_api_linker_script)
+
+LOCAL_SHARED_LIBRARIES := \
+    libandroid_runtime \
+    libbase \
+    libbinder \
+    libcutils \
+    libutils \
+    libgui \
+    libui \
+    libEGL \
+    libGLESv2 \
+    libvulkan \
+    libhardware \
+    liblog \
+    libsync \
+    libevent \
+    libprotobuf-cpp-full
+
+LOCAL_STATIC_LIBRARIES := \
+    libdisplay \
+    libbufferhub \
+    libbufferhubqueue \
+    libchrome \
+    libdvrcommon \
+    libeds \
+    libdvrgraphics \
+    libsensor \
+    libperformance \
+    libpdx_default_transport \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Prebuilt shared library for libgvr_audio.so
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr_audio
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_SUFFIX := .so
+LOCAL_MULTILIB := both
+LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
+LOCAL_SRC_FILES_arm := prebuilt/lib/android_arm/libgvr_audio.so
+LOCAL_SRC_FILES_arm64 := prebuilt/lib/android_arm64/libgvr_audio.so
+LOCAL_SRC_FILES_x86 := prebuilt/lib/android_x86/libgvr_audio.so
+LOCAL_SRC_FILES_x86_64 := prebuilt/lib/android_x86_64/libgvr_audio.so
+include $(BUILD_PREBUILT)
+
+# Prebuilt shared library for libgvr.so
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/prebuilt/include
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_SUFFIX := .so
+LOCAL_MULTILIB := both
+LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
+LOCAL_SRC_FILES_arm := prebuilt/lib/android_arm/libgvr.so
+LOCAL_SRC_FILES_arm64 := prebuilt/lib/android_arm64/libgvr.so
+LOCAL_SRC_FILES_x86 := prebuilt/lib/android_x86/libgvr.so
+LOCAL_SRC_FILES_x86_64 := prebuilt/lib/android_x86_64/libgvr.so
+include $(BUILD_PREBUILT)
+
+# Prebuilt Java static library for common_library.aar
+include $(CLEAR_VARS)
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
+    gvr_common_library_aar:prebuilt/lib/common_library.aar
+include $(BUILD_MULTI_PREBUILT)
+
+# Dummy libgvr_ext to be used along side libgvr.so prebuilt.
+# This shall be replaced with Google3 prebuilts in future.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libgvr_ext
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := dummy_gvr_ext.cpp
+LOCAL_STATIC_LIBRARIES := libchrome
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES += libgvr
+LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/vr/libgvr/CPPLINT.cfg b/libs/vr/libgvr/CPPLINT.cfg
new file mode 100644
index 0000000..2da1c50
--- /dev/null
+++ b/libs/vr/libgvr/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard,-build/include_alpha
diff --git a/libs/vr/libgvr/README.dreamos b/libs/vr/libgvr/README.dreamos
new file mode 100644
index 0000000..d847210
--- /dev/null
+++ b/libs/vr/libgvr/README.dreamos
@@ -0,0 +1 @@
+Files under public/vr/gvr were imported from the public Google VR SDK.
diff --git a/libs/vr/libgvr/com.google.vr.gvr.platform.xml b/libs/vr/libgvr/com.google.vr.gvr.platform.xml
new file mode 100644
index 0000000..9297c7f
--- /dev/null
+++ b/libs/vr/libgvr/com.google.vr.gvr.platform.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>
+    <library name="com.google.vr.gvr.platform"
+             file="/system/framework/com.google.vr.gvr.platform.jar" />
+</permissions>
diff --git a/libs/vr/libgvr/deviceparams/CardboardDevice.nolite.proto b/libs/vr/libgvr/deviceparams/CardboardDevice.nolite.proto
new file mode 100644
index 0000000..77b5d72
--- /dev/null
+++ b/libs/vr/libgvr/deviceparams/CardboardDevice.nolite.proto
@@ -0,0 +1,299 @@
+
+syntax = "proto2";
+
+option java_package = "com.google.vrtoolkit.cardboard.proto";
+option java_outer_classname = "CardboardDevice";
+option optimize_for = SPEED;
+
+package proto;
+
+
+/**
+ * Message describing properties of a VR head mount device (HMD) which uses an
+ * interchangeable smartphone as a display (e.g. Google Cardboard).
+ *
+ * While some properties are certain (e.g. inter_lens_distance), others
+ * represent nominal values which may be refined depending on context (e.g.
+ * viewport_angles).
+ *
+ * Lengths are in meters unless noted otherwise.  Fields are _required_
+ * unless noted otherwise.
+ *
+ * Some context on why this set of parameters are deemed necessary and
+ * sufficient:
+ *    * FOV scale can be reasonably approximated from lens-to-screen distance
+ *      and display size (i.e. knowing lens focal length isn't crucial).
+ *    * Lenses are assumed to be horizontally centered with respect to
+ *      display.
+ *    * The display is not necessarily vertically centered.  For interchangeable
+ *      phones where the device rests against a tray, we can derive
+ *      the vertical offset from tray-to-lens height along with phone-specific
+ *      bezel and screen sizes (supplied separately).
+ */
+message DeviceParams {
+  // String identifying the device's vendor (e.g. "Google, Inc.").
+  // A device's [vendor, model] pair is expected to be globally unique.
+  optional string vendor = 1;
+
+  // String identifying the device's model, including revision info if
+  // needed (e.g. "Cardboard v1").  A device's [vendor, model] pair is
+  // expected to be globally unique.
+  optional string model = 2;
+
+  // Distance from the display screen to the optical center of lenses.
+  // This is a required field for distortion rendering, and must be positive.
+  optional float screen_to_lens_distance = 3;
+
+  // Horizontal distance between optical center of the lenses.
+  // This is a required field for distortion rendering, and must be positive.
+  optional float inter_lens_distance = 4;
+
+  // Four-element tuple (left, right, bottom, top) of left eye's view extent
+  // angles relative to center, assuming the following:
+  //     * eye is aligned with optical center of lens
+  //     * display screen is equal or larger than extents viewable through lens
+  //     * nominal eye-to-lens distance
+  //     * mirrored field of view will be applied to the right eye
+  // These values are essentially used as an optimization to avoid rendering
+  // pixels which can't be seen.
+  // This is a required field for distortion rendering, and angles must be
+  // positive.
+  repeated float left_eye_field_of_view_angles = 5 [packed = true];
+
+  enum VerticalAlignmentType {
+    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
+  }
+
+  // Set according to vertical alignment strategy-- see enum comments above.
+  // NOTE: If you set this to CENTER, see special instructions for the
+  // tray_to_lens_distance field below.
+  optional VerticalAlignmentType vertical_alignment = 11 [default = BOTTOM];
+
+  // If the phone is aligned vertically within the device by resting against
+  // a fixed top or bottom tray, this is the distance from the tray to
+  // optical center of the lenses.
+  // This is a required field for distortion rendering, and must be positive.
+  // NOTE: Due to a bug in initial versions of the SDK's, this field
+  // must be set explicitly to .035 when vertical_alignment = CENTER.
+  optional float tray_to_lens_distance = 6;
+
+  // Coefficients Ki for pincushion distortion function which maps
+  // from position on real screen to virtual screen (i.e. texture) relative
+  // to optical center:
+  //
+  //    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.  Tan-angle units can be
+  // computed as distance on the screen divided by distance from the
+  // virtual eye to the screen.
+  repeated float distortion_coefficients = 7 [packed = true];
+  // Slots 8, 9 reserved for per-color channel distortion.
+
+  // Optionally, whether the head mount uses a magnet in any part of its
+  // design.  Intended as hint as to whether phone's magnetometer is
+  // available for tasks such as orientation tracking.
+  optional bool has_magnet = 10;
+
+  enum ButtonType {
+    // No physical button, and touch screen is not easily accessible.
+    NONE = 0;
+    // HMD has integrated magnet switch similar to original Cardboard.
+    MAGNET = 1;
+    // At least a portion of touch screen is easily accessible to user for taps.
+    TOUCH = 2;
+    // Touch screen is triggered indirectly via integrated button on the HMD.
+    INDIRECT_TOUCH = 3;
+  }
+
+  // Specify primary input mechanism of the HMD.  Intended for advisory
+  // purposes only, to address simple questions such as "can HMD
+  // be used with apps requiring a physical button event?" or "what icon
+  // should be used to represent button action to the user?".
+  optional ButtonType primary_button = 12 [default = MAGNET];
+
+  // Some internal data for Cardboard.  This data is not intended to be
+  // set or used by developers, and any data in this proto is not guaranteed
+  // to be supported with new SDK updates.
+  optional CardboardInternalParams internal = 1729;
+
+  // Optionally, specifies the additional parameters that are necessary for
+  // a Daydream-ready headset. This field is non-null if the headset is
+  // Daydream-ready.
+  // TODO(b/30112366) The inclusion of this message inside a DeviceParams is a
+  // somewhat ugly result of some historical choices in the SDK. We should
+  // consider refactoring our code to allow us to remove this, and the
+  // CardboardInternalParams messages from this proto.
+  optional DaydreamInternalParams daydream_internal = 196883;
+}
+
+// TODO(b/27108179): CardboardInternalParams should be migrated into its own
+// file, and not live in this file.
+
+/**
+ * Message describing parameters that are used internally by Cardboard
+ * and VRToolkit. These parameters don't necessarily fit into the DeviceParams
+ * notion of a VR viewer combined with user's phone (e.g. case of viewer with
+ * dedicated display, etc.) and are not intended to be used by developers
+ * and may or may not be supported or changed without notice on new releases
+ * of the Cardboard SDK or VR Toolkit.
+ */
+message CardboardInternalParams {
+  // Used to specify a per-eye post-process transformation -- an optional
+  // rotation and x-axis reflection -- to be applied after distortion
+  // correction.
+  enum OrientationType {
+    CCW_0_DEGREES = 0;
+    CCW_90_DEGREES = 1;
+    CCW_180_DEGREES = 2;
+    CCW_270_DEGREES = 3;
+    CCW_0_DEGREES_MIRRORED = 4;
+    CCW_90_DEGREES_MIRRORED = 5;
+    CCW_180_DEGREES_MIRRORED = 6;
+    CCW_270_DEGREES_MIRRORED = 7;
+  }
+
+  // Specify a post-process transformation that is applied after the distortion
+  // function. This field is optional, if not specified, CCW_0_DEGREES is
+  // assumed. If repeated, the first orientation is for the left eye, the second
+  // is for the right eye.
+  //
+  // For example, if [CCW_90_DEGREES, CCW_270_DEGREES_MIRRORED] is specified,
+  //
+  // this input:
+  //
+  // ***************** *****************
+  // *1             2* *1             2*
+  // *      *        * *      ***      *
+  // *      *        * *      * *      *
+  // *      ****     * *      *  *     *
+  // *4             3* *4             3*
+  // ***************** *****************
+  //
+  // is rendered on the screen like this:
+  //
+  // ***************** *****************
+  // *2       *     3* *3     *       2*
+  // *        *      * *       **      *
+  // *        *      * *        *      *
+  // *      ***      * *      ***      *
+  // *1             4* *4             1*
+  // ***************** *****************
+  repeated OrientationType eye_orientations = 1 [packed = true];
+
+  // Specify a horizontal offset from the middle of the screen to the center of
+  // the lens, in meters. If one is not provided, half of the inter lens
+  // distance is used.
+  //
+  // This is only necessary if the HMD has some sort of periscope effect, where
+  // the position of the lenses, relative to the screen, is different than
+  // their position relative to the user.
+  //
+  // For example, in the HMD below, two mirrors reflect the image from the
+  // screen to the user, creating a larger inter lens distance than the screen
+  // can support.
+  //
+  // [In the diagram below, S = screen, L = lens]
+  //
+  // screen_center_to_lens_distance
+  //             |--|
+  //
+  // -------------------------
+  // |     SSSSSSSSSSSS      |
+  // |        |  |  |        |
+  // |   /----/  |  \----\   |
+  // |   |       |       |   |
+  // |  LLL             LLL  |
+  //
+  //     |---------------|
+  //    inter_lens_distance
+  //
+  optional float screen_center_to_lens_distance = 2;
+
+  // Optional x-dimension physical pixels per inch of the external display,
+  // assuming landscape orientation. If set, this will override OS-reported
+  // values.
+  optional float x_ppi_override = 3;
+
+  // Optional y-dimension physical pixels per inch of the external display,
+  // assuming landscape orientation.  If set, this will override OS-reported
+  // values.
+  optional float y_ppi_override = 4;
+
+  // Optional string identifying the device's accelerometer and gyroscope.
+  // If either field is filled out, the corresponding sensor (gyroscope or
+  // accelerometer) will be used for head tracking.
+  //
+  // Valid strings are usually found in:
+  // vendor/<vendorname>/<devicename>/xxx/sensors.cpp
+  //
+  // For dynamic sensors, this string will be provided in a separate way.
+  //
+  // NB: Vendors and manufacturers should make the name of the sensor as
+  // specific as possible, since if multiple sensors with the same name are
+  // connected, the first will be used.
+  optional string accelerometer = 5;
+  optional string gyroscope = 6;
+}
+
+/**
+ * Message describing the additional properties of a Daydream-ready headset that
+ * are not used for a normal cardboard viewer. These parameters are not intended
+ * to be used, or consumed, by developers and may or may not be supported or
+ * changed without notice on new releases of the Cardboard SDK or VR Toolkit.
+ */
+message DaydreamInternalParams {
+  // The version of the Daydream-ready specification to which this device
+  // conforms.
+  optional int32 version = 1;
+
+  // Optionally, specifies the collection of screen alignment markers in the
+  // headset.
+  repeated ScreenAlignmentMarker alignment_markers = 2;
+}
+
+/**
+ * Message describing a single screen alignment marker.
+ *
+ * A screen alignment marker is a capacitive touch point affixed to the headset
+ * which is capable of making contact with the screen. The location of the touch
+ * point is given in meters, measured along a horizontal axis which passes
+ * through the center of both lenses, and a vertical axis which is equidistant
+ * from the centers of the lenses. A positive vertical value indicates that the
+ * point lies above the horizontal axis, and a positive horizontal value
+ * indicates that the point lies to the right, as seen by a user of the headset,
+ * of the vertical axis. For example, if the following is a representation of a
+ * headset, viewed from the point of view of a user, with three points marked by
+ * the numbers 1, 2, and 3.
+ *
+ * *****************************************************************************
+ * *                                    ^                                      *
+ * *               _____                |                _____                 *
+ * *              /     \               1               /     \                *
+ * *             /       \              |              /       \               *
+ * *            /         \             |             /         \              *
+ * *           /           \            |            /           \             *
+ * *          /             \           |           /             \            *
+ * *---------|-------*-------|----------+------2---|-------*-------|---------->*
+ * *          \             /           |           \             /            *
+ * *           \           /            |            \           /             *
+ * *            \         /         3   |             \         /              *
+ * *             \       /              |              \       /               *
+ * *              \_____/               |               \_____/                *
+ * *                                    |                                      *
+ * *                                    |                                      *
+ * *****************************************************************************
+ *
+ * Then the coordinates of point 1 could be horizontal = 0.0, vertical = 0.035;
+ * point 2 could be horizontal = 0.02; and point 3 could be horizontal = -0.01
+ * vertical = -0.012
+ */
+message ScreenAlignmentMarker {
+  // The horizontal coordinate of the touch point.
+  optional float horizontal = 1;
+
+  // The vertical coordinate of the touch point.
+  optional float vertical = 2;
+}
diff --git a/libs/vr/libgvr/dummy_gvr_ext.cpp b/libs/vr/libgvr/dummy_gvr_ext.cpp
new file mode 100644
index 0000000..c507038
--- /dev/null
+++ b/libs/vr/libgvr/dummy_gvr_ext.cpp
@@ -0,0 +1,29 @@
+#include <base/logging.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/include/gvr_ext.h>
+#include <vr/gvr/capi/include/gvr_types.h>
+
+gvr_frame_schedule* gvr_frame_schedule_create() { return NULL; }
+
+void gvr_frame_schedule_destroy(gvr_frame_schedule** /* schedule */) {}
+
+uint32_t gvr_frame_schedule_get_vsync_count(
+    gvr_frame_schedule* /* schedule */) {
+  return 0;
+}
+
+gvr_mat4f gvr_get_6dof_head_pose_in_start_space(gvr_context* gvr,
+                                                uint32_t /* vsync_count */) {
+  LOG(FATAL) << "gvr_get_6dof_head_pose_in_start_space is not implemented. "
+             << "Use gvr_get_head_space_from_start_space_pose instead.";
+  return gvr_mat4f({{{1.0f, 0.0f, 0.0f, 0.0f},
+                     {0.0f, 1.0f, 0.0f, 0.0f},
+                     {0.0f, 0.0f, 1.0f, 0.0f},
+                     {0.0f, 0.0f, 0.0f, 1.0f}}});
+}
+
+void gvr_wait_next_frame(gvr_swap_chain* /* swap_chain */,
+                         int64_t /* sched_offset_nanos */,
+                         gvr_frame_schedule* /* out_next_frame_schedule */) {
+  LOG(FATAL) << "gvr_wait_next_frame is not implemented.";
+}
diff --git a/libs/vr/libgvr/exported_apis.lds b/libs/vr/libgvr/exported_apis.lds
new file mode 100644
index 0000000..2d19303
--- /dev/null
+++ b/libs/vr/libgvr/exported_apis.lds
@@ -0,0 +1,16 @@
+{
+  global:
+    # Export GVR APIs, both public and private.
+    gvr_*;
+
+    # Whitelist of DVR APIs required by VrCore for Ambrosia controller support.
+    dvrPoseCreate;
+    dvrPoseDestroy;
+    dvrPoseGet;
+    dvrPoseGetController;
+    dvrPoseGetVsyncCount;
+
+  local:
+    # Hide everything else.
+    *;
+};
diff --git a/libs/vr/libgvr/include/private/dvr/internal_types.h b/libs/vr/libgvr/include/private/dvr/internal_types.h
new file mode 100644
index 0000000..dafdb73
--- /dev/null
+++ b/libs/vr/libgvr/include/private/dvr/internal_types.h
@@ -0,0 +1,170 @@
+#ifndef ANDROID_DVR_INTERNAL_TYPES_H_
+#define ANDROID_DVR_INTERNAL_TYPES_H_
+
+#include <GLES2/gl2.h>
+#include <atomic>
+#include <memory>
+#include <vector>
+#include <unordered_map>
+
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gui/Surface.h>
+#include <private/dvr/buffer_hub_queue_core.h>
+#include <private/dvr/display_client.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/include/gvr_types.h>
+#include <vr/gvr/capi/src/gvr_types_experimental.h>
+
+typedef struct gvr_user_prefs_ {
+} gvr_user_prefs;
+
+typedef struct gvr_context_ {
+  int32_t last_error_;
+  JNIEnv* jni_env_;
+  DvrPose* pose_client_;
+  std::unique_ptr<android::dvr::DisplayClient> display_client_;
+  android::dvr::SystemDisplayMetrics display_metrics_;
+  gvr_mat4f left_eye_viewport_transform_;
+  gvr_mat4f right_eye_viewport_transform_;
+  gvr_mat4f next_frame_6dof_pose_;
+  gvr_mat4f next_frame_controller_pose_[2];
+  gvr_user_prefs user_prefs_;
+  bool force_6dof_;
+  std::vector<gvr_swap_chain*> swap_chains_;
+
+  gvr_context_() :
+      last_error_(GVR_ERROR_NONE),
+      jni_env_(nullptr),
+      pose_client_(nullptr),
+      force_6dof_(false) {}
+
+  ~gvr_context_();
+} gvr_context;
+
+typedef struct gvr_buffer_spec_ {
+  gvr_sizei size;
+  int32_t msaa_samples;
+  int32_t color_format;
+  int32_t depth_stencil_format;
+  bool blur_behind;
+  bool initially_visible;
+  int z_order;
+
+  // The default values are configured to match SVR defaults
+  gvr_buffer_spec_()
+      : size{0, 0},
+        msaa_samples(0),
+        color_format(GVR_COLOR_FORMAT_RGBA_8888),
+        depth_stencil_format(GVR_DEPTH_STENCIL_FORMAT_DEPTH_16),
+        blur_behind(true),
+        initially_visible(true),
+        z_order(0) {}
+} gvr_buffer_spec;
+
+// This isn't a public gvr type
+struct gvr_buffer {
+  gvr_buffer_spec spec;
+  GLuint frame_buffer;
+  GLuint color_render_buffer;
+  GLuint depth_stencil_render_buffer;
+
+  // requested_size is used for resizing. It will be {-1, -1} when no resize is
+  // requested. Any other value indicates the app changed the size.
+  gvr_sizei requested_size;
+
+  gvr_buffer();
+  // If creation fails frame_buffer will be 0
+  gvr_buffer(gvr_context* gvr, const gvr_buffer_spec& spec,
+             GLuint texture_id, GLenum texture_target);
+  ~gvr_buffer();
+
+  gvr_buffer(gvr_buffer&& other);
+  gvr_buffer& operator=(gvr_buffer&& other);
+  gvr_buffer(const gvr_buffer& other) = delete;
+  gvr_buffer& operator=(const gvr_buffer& other) = delete;
+
+  // Set default values. Doesn't free GL resources first.
+  void SetDefaults();
+
+  // Frees all GL resources associated with the buffer
+  void FreeGl();
+};
+
+typedef struct gvr_swap_chain_ {
+  gvr_context* context;
+  DvrGraphicsContext* graphics_context_;
+  std::vector<gvr_buffer> buffers_;
+  bool frame_acquired_;
+  bool wait_next_frame_called_by_app_;
+  std::atomic<int32_t> next_external_surface_id_;
+  std::unordered_map<int32_t, gvr_external_surface*> external_surfaces_;
+
+  explicit gvr_swap_chain_(gvr_context* context)
+      : context(context),
+        graphics_context_(nullptr),
+        frame_acquired_(false),
+        wait_next_frame_called_by_app_(false),
+        next_external_surface_id_(0) {}
+  ~gvr_swap_chain_();
+} gvr_swap_chain;
+
+typedef struct gvr_buffer_viewport_ {
+  int32_t buffer_index;
+  gvr_rectf uv;
+  gvr_mat4f transform;
+  int32_t eye;
+  int32_t external_surface_id;
+  gvr_reprojection reprojection;
+
+  gvr_buffer_viewport_()
+      : buffer_index(0),
+        uv{0, 0, 0, 0},
+        transform{{{1.f, 0.f, 0.f, 0.f},
+                   {0.f, 1.f, 0.f, 0.f},
+                   {0.f, 0.f, 1.f, 0.f},
+                   {0.f, 0.f, 0.f, 1.f}}},
+        eye(0),
+        external_surface_id(-1),
+        reprojection(GVR_REPROJECTION_FULL) {}
+
+  gvr_buffer_viewport_(int32_t /* buffer_index */, gvr_rectf uv,
+                       const gvr_mat4f& transform, int32_t eye,
+                       int32_t external_surface_id,
+                       gvr_reprojection reprojection)
+      : buffer_index(0),
+        uv(uv),
+        transform(transform),
+        eye(eye),
+        external_surface_id(external_surface_id),
+        reprojection(reprojection) {}
+
+  bool operator==(const gvr_buffer_viewport_& other) const;
+
+  bool operator!=(const gvr_buffer_viewport_& other) const {
+    return !operator==(other);
+  }
+} gvr_buffer_viewport;
+
+typedef struct gvr_buffer_viewport_list_ {
+  std::vector<gvr_buffer_viewport> viewports;
+} gvr_buffer_viewport_list;
+
+typedef struct gvr_frame_schedule_ {
+  uint32_t vsync_count;
+  gvr_clock_time_point scheduled_finish;
+
+  gvr_frame_schedule_() : vsync_count(0) {
+    scheduled_finish.monotonic_system_time_nanos = 0;
+  }
+} gvr_frame_schedule;
+
+typedef struct gvr_display_synchronizer_ {} gvr_display_synchronizer;
+
+typedef struct gvr_external_surface_ {
+  int32_t id;
+  gvr_swap_chain* swap_chain;
+  DvrVideoMeshSurface* video_surface;
+} gvr_external_surface;
+
+#endif  // ANDROID_DVR_INTERNAL_TYPES_H_
diff --git a/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_ext.h b/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_ext.h
new file mode 100644
index 0000000..8af434f
--- /dev/null
+++ b/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_ext.h
@@ -0,0 +1,119 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 VR_GVR_CAPI_INCLUDE_GVR_EXT_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_EXT_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Constants that represent GVR error codes.
+typedef enum {
+  // TODO(steventhomas): All errors should be switched to something more
+  // meaningful and this should eventually go away.
+  GVR_ERROR_INTERNAL = 9000,
+} gvr_error_ext;
+
+typedef struct gvr_frame_schedule_ gvr_frame_schedule;
+
+gvr_frame_schedule* gvr_frame_schedule_create();
+
+void gvr_frame_schedule_destroy(gvr_frame_schedule** schedule);
+
+uint32_t gvr_frame_schedule_get_vsync_count(gvr_frame_schedule* schedule);
+
+gvr_clock_time_point gvr_frame_schedule_get_scheduled_finish(
+    gvr_frame_schedule* schedule);
+
+/// Sleep until it's time to render the next frame.
+// |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.
+void gvr_wait_next_frame(gvr_swap_chain* swap_chain, int64_t start_delay_nanos,
+                         gvr_frame_schedule* out_next_frame_schedule);
+
+gvr_mat4f gvr_get_6dof_head_pose_in_start_space(gvr_context* gvr,
+                                                uint32_t vsync_count);
+
+gvr_mat4f gvr_get_head_space_from_start_space_pose(
+    gvr_context* gvr, const gvr_clock_time_point time);
+
+gvr_mat4f gvr_get_start_space_from_controller_space_pose(
+    gvr_context* gvr, int controller_id, const gvr_clock_time_point time);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+#include <utility>
+
+namespace gvr {
+
+/// Convenience C++ wrapper for gvr_frame_schedule. Frees the underlying
+/// gvr_frame_schedule on destruction.
+class FrameSchedule {
+ public:
+  FrameSchedule() { schedule_ = gvr_frame_schedule_create(); }
+
+  ~FrameSchedule() {
+    if (schedule_)
+      gvr_frame_schedule_destroy(&schedule_);
+  }
+
+  FrameSchedule(FrameSchedule&& other) {
+    std::swap(schedule_, other.schedule_);
+  }
+
+  FrameSchedule& operator=(FrameSchedule&& other) {
+    std::swap(schedule_, other.schedule_);
+    return *this;
+  }
+
+  gvr_frame_schedule* cobj() { return schedule_; }
+  const gvr_frame_schedule* cobj() const { return schedule_; }
+
+  uint32_t GetVsyncCount() const {
+    return gvr_frame_schedule_get_vsync_count(schedule_);
+  }
+
+  gvr_clock_time_point GetScheduledFinish() const {
+    return gvr_frame_schedule_get_scheduled_finish(schedule_);
+  }
+
+ private:
+  gvr_frame_schedule* schedule_;
+};
+
+}  // namespace gvr
+#endif  // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif  // VR_GVR_CAPI_INCLUDE_GVR_EXT_H_
diff --git a/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_util.h b/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_util.h
new file mode 100644
index 0000000..1fae611
--- /dev/null
+++ b/libs/vr/libgvr/include/vr/gvr/capi/include/gvr_util.h
@@ -0,0 +1,123 @@
+#ifndef VR_GVR_CAPI_INCLUDE_GVR_UTIL_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_UTIL_H_
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/numeric.h>
+#include <private/dvr/types.h>
+#include <vr/gvr/capi/include/gvr_types.h>
+
+namespace android {
+namespace dvr {
+
+inline gvr_rectf FovRadiansToDegrees(const gvr_rectf& fov) {
+  return gvr_rectf{ToDeg(fov.left), ToDeg(fov.right), ToDeg(fov.bottom),
+                   ToDeg(fov.top)};
+}
+
+inline gvr_rectf FovDegreesToRadians(const gvr_rectf& fov) {
+  return gvr_rectf{ToRad(fov.left), ToRad(fov.right), ToRad(fov.bottom),
+                   ToRad(fov.top)};
+}
+
+inline FieldOfView GvrToDvrFov(const gvr_rectf& fov) {
+  gvr_rectf fov_rad = FovDegreesToRadians(fov);
+  return FieldOfView(fov_rad.left, fov_rad.right, fov_rad.bottom, fov_rad.top);
+}
+
+inline gvr_rectf DvrToGvrFov(const FieldOfView& fov) {
+  return FovRadiansToDegrees(
+      gvr_rectf{fov.GetLeft(), fov.GetRight(), fov.GetBottom(), fov.GetTop()});
+}
+
+inline gvr_mat4f GvrIdentityMatrix() {
+  gvr_mat4f identity;
+  memset(&identity.m, 0, sizeof(identity.m));
+  for (int i = 0; i < 4; i++)
+    identity.m[i][i] = 1;
+  return identity;
+}
+
+inline gvr_mat4f GvrTranslationMatrix(float x, float y, float z) {
+  gvr_mat4f trans = GvrIdentityMatrix();
+  trans.m[0][3] = x;
+  trans.m[1][3] = y;
+  trans.m[2][3] = z;
+  return trans;
+}
+
+inline gvr_mat4f EigenToGvrMatrix(const mat4& m) {
+  gvr_mat4f ret;
+  for (int i = 0; i < 4; ++i)
+    for (int j = 0; j < 4; ++j)
+      ret.m[i][j] = m(i, j);
+  return ret;
+}
+
+inline mat4 GvrToEigenMatrix(const gvr::Mat4f& m) {
+  mat4 ret;
+  for (int i = 0; i < 4; ++i)
+    for (int j = 0; j < 4; ++j)
+      ret(i, j) = m.m[i][j];
+  return ret;
+}
+
+inline quat GvrToEigenRotation(const gvr_mat4f& m) {
+  mat3 ret;
+  for (int r = 0; r < 3; ++r)
+    for (int c = 0; c < 3; ++c)
+      ret(r, c) = m.m[r][c];
+  return quat(ret.matrix());
+}
+
+inline vec3 GvrToEigenTranslation(const gvr_mat4f& m) {
+  return vec3(m.m[0][3], m.m[1][3], m.m[2][3]);
+}
+
+// Converts a DVR pose to 6DOF head transform as a GVR matrix.
+inline gvr_mat4f PosefToGvrMatrix(const Posef& pose) {
+  return EigenToGvrMatrix(pose.GetObjectFromReferenceMatrix());
+}
+
+// Converts a DVR pose to 3DOF head transform as a GVR matrix by stripping out
+// position translation.
+inline gvr_mat4f PosefTo3DofGvrMatrix(const Posef& pose) {
+  gvr_mat4f ret = PosefToGvrMatrix(pose);
+  ret.m[0][3] = 0;
+  ret.m[1][3] = 0;
+  ret.m[2][3] = 0;
+  return ret;
+}
+
+// Converts a GVR matrix to a DVR pose.
+inline Posef GvrMatrixToPosef(const gvr_mat4f& m) {
+  return Posef(GvrToEigenRotation(m), GvrToEigenTranslation(m)).Inverse();
+}
+
+// Calculates an transform with only the yaw and position components of |pose|.
+// The inverse of this matrix cancels yaw and position without affecting roll or
+// pitch.
+inline mat4 CalculateRecenterTransform(const mat4& pose) {
+  const vec4 z_axis = pose * vec4::UnitZ();
+  const float yaw = std::atan2(z_axis[0], z_axis[2]);
+  const vec3 position = pose.translation();
+  return mat4(Eigen::AngleAxis<float>(yaw, vec3::UnitY())).translate(position);
+}
+
+// Calculates a transform that negates the position component of |pose| and
+// offsets the pose by |position|. The inverse of this matrix cancels the
+// position component of pose and translates by |position| without affecting
+// orientation.
+inline mat4 CalculateOffsetTransform(const mat4& pose, const vec3& position) {
+  // Transform the origin by the pose matrix to produce the offset that cancels
+  // only the position of the pose.
+  //          -1          T
+  // [ R | t ]  [ 0 ] = -R * t
+  // [ 0   1 ]  [ 1 ]
+  const vec3 position_offset = (pose.inverse() * vec4(0, 0, 0, 1)).head<3>();
+  return mat4(mat4::Identity()).translate(position - position_offset);
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_GVR_CAPI_INCLUDE_GVR_UTIL_H_
diff --git a/libs/vr/libgvr/java/com/google/vr/gvr/platform/Loader.java b/libs/vr/libgvr/java/com/google/vr/gvr/platform/Loader.java
new file mode 100644
index 0000000..5c5cc62
--- /dev/null
+++ b/libs/vr/libgvr/java/com/google/vr/gvr/platform/Loader.java
@@ -0,0 +1,32 @@
+package com.google.vr.gvr.platform;
+
+/**
+ * Auxiliary class to load the system implementation of the GVR API.
+ */
+public class Loader {
+
+  /**
+   * Opens a shared library containing the system implementation for the GVR
+   * API and returns the handle to it.
+   *
+   * @return A Long object describing the handle returned by dlopen.
+   */
+  public static Long loadLibrary() {
+    // Note: we cannot safely do caller verifications here, so instead we do
+    // them in the service side. This means that private API symbols will be
+    // visible to any app adding the appropriate <uses-library> in their
+    // manifest, but any requests to such APIs will fail if not done from a
+    // trusted package like VrCore.
+    //
+    // Trusted packages are defined by (package name, signature) pairs in within
+    // a system service, and both must match.
+
+    // Load a thin JNI library that runs dlopen on request.
+    System.loadLibrary("gvr_system_loader");
+
+    // Performs dlopen on the library and returns the handle.
+    return nativeLoadLibrary("libgvr_system.so");
+  }
+
+  private static native long nativeLoadLibrary(String library);
+}
diff --git a/libs/vr/libgvr/library_loader.cpp b/libs/vr/libgvr/library_loader.cpp
new file mode 100644
index 0000000..3cdc7d6
--- /dev/null
+++ b/libs/vr/libgvr/library_loader.cpp
@@ -0,0 +1,25 @@
+#include <dlfcn.h>
+#include <jni.h>
+
+#include <string>
+
+extern "C" {
+
+JNIEXPORT jlong JNICALL
+Java_com_google_vr_gvr_platform_Loader_nativeLoadLibrary(
+    JNIEnv* env, jclass, jstring java_library) {
+  if (!java_library)
+    return 0;
+
+  // Convert the Java String object to a C++ null-terminated string.
+  const char* data = env->GetStringUTFChars(java_library, NULL);
+  size_t size = env->GetStringUTFLength(java_library);
+  std::string library(data, size);
+  env->ReleaseStringUTFChars(java_library, data);
+
+  // Return the handle to the requested library.
+  return reinterpret_cast<jlong>(
+      dlopen(library.c_str(), RTLD_NOW | RTLD_LOCAL));
+}
+
+}  // extern "C"
diff --git a/libs/vr/libgvr/prebuilt/build_gvr_prebuilts.sh b/libs/vr/libgvr/prebuilt/build_gvr_prebuilts.sh
new file mode 100755
index 0000000..8e6ca2d
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/build_gvr_prebuilts.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+# Build and copy libgvr from Google3. Make sure that your local Google3 client
+# is up-to-date by running `p4 sync` before executing this script.
+#
+# Usage:
+# build_gvr_prebuilts.sh --google3_dir=<path_google3_client_root>
+
+source gbash.sh || exit
+
+DEFINE_string --required "google3_dir" "" \
+  "Path to the root directory of Google3 client"
+
+BLAZE_COMMON_OPTS=(
+  --compilation_mode=opt
+  --copt=-fdata-sections
+  --copt=-ffunction-sections
+  --define='prod=1'
+  --define='enable_experimental_sdk=1'
+  --linkopt=-Wl,--gc-sections
+)
+
+function copy_file() {
+  cp -v "${1}" ${CURRENT_DIR}/"${2}"
+}
+
+function copy_gvr_headers() {
+  echo "Copy GVR headers ..."
+
+  GVR_HEADER_DIR="include/vr/gvr/capi/include"
+  GVR_SOURCE_DIR="include/vr/gvr/capi/src"
+
+  # GVR public headers
+  copy_file "vr/gvr/capi/include/gvr.h" ${GVR_HEADER_DIR}
+  copy_file "vr/gvr/capi/include/gvr_audio.h" ${GVR_HEADER_DIR}
+  copy_file "vr/gvr/capi/include/gvr_controller.h" ${GVR_HEADER_DIR}
+  copy_file "vr/gvr/capi/include/gvr_types.h" ${GVR_HEADER_DIR}
+
+  # GVR private and experimental headers
+  copy_file "vr/gvr/capi/src/gvr_experimental.h" ${GVR_SOURCE_DIR}
+  copy_file "vr/gvr/capi/src/gvr_private.h" ${GVR_SOURCE_DIR}
+  copy_file "vr/gvr/capi/src/gvr_types_experimental.h" ${GVR_SOURCE_DIR}
+}
+
+function build_gvr_libs() {
+  echo "Build GVR libraries ..."
+
+  blaze build \
+    //java/com/google/vr/sdk/release:common_library.aar \
+    //vr/gvr/platform:libgvr.so \
+    //vr/gvr/platform:libgvr_audio.so \
+    ${BLAZE_COMMON_OPTS[@]} --config=android_arm --symlink_prefix blaze-arm-
+
+  blaze build \
+    //vr/gvr/platform:libgvr.so \
+    //vr/gvr/platform:libgvr_audio.so \
+    ${BLAZE_COMMON_OPTS[@]} --config=android_arm64 --symlink_prefix blaze-arm64-
+
+    blaze build \
+    //java/com/google/vr/sdk/release:common_library.aar \
+    //vr/gvr/platform:libgvr.so \
+    //vr/gvr/platform:libgvr_audio.so \
+    ${BLAZE_COMMON_OPTS[@]} --config=android_x86 --symlink_prefix blaze-x86-
+
+  blaze build \
+    //vr/gvr/platform:libgvr.so \
+    //vr/gvr/platform:libgvr_audio.so \
+    ${BLAZE_COMMON_OPTS[@]} --config=android_x86_64 --symlink_prefix blaze-x86_64-
+
+  copy_file "blaze-arm-genfiles/java/com/google/vr/sdk/release/common_library.aar" \
+    "lib/common_library.aar"
+  copy_file "blaze-arm-genfiles/vr/gvr/platform/libgvr.so" "lib/android_arm"
+  copy_file "blaze-arm-genfiles/vr/gvr/platform/libgvr_audio.so" "lib/android_arm"
+  copy_file "blaze-arm64-genfiles/vr/gvr/platform/libgvr.so" "lib/android_arm64"
+  copy_file "blaze-arm64-genfiles/vr/gvr/platform/libgvr_audio.so" "lib/android_arm64"
+  copy_file "blaze-x86-genfiles/vr/gvr/platform/libgvr.so" "lib/android_x86"
+  copy_file "blaze-x86-genfiles/vr/gvr/platform/libgvr_audio.so" "lib/android_x86"
+  copy_file "blaze-x86_64-genfiles/vr/gvr/platform/libgvr.so" "lib/android_x86_64"
+  copy_file "blaze-x86_64-genfiles/vr/gvr/platform/libgvr_audio.so" "lib/android_x86_64"
+}
+
+function main() {
+  set -ex
+
+  CURRENT_DIR=$(pwd)
+  GOOGLE3_DIR=${FLAGS_google3_dir}
+
+  cd ${GOOGLE3_DIR}
+  copy_gvr_headers
+  build_gvr_libs
+}
+
+gbash::init_google "$@"
+main "$@"
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h
new file mode 100644
index 0000000..c459eca
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr.h
@@ -0,0 +1,1751 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 VR_GVR_CAPI_INCLUDE_GVR_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_H_
+
+#ifdef __ANDROID__
+#include <jni.h>
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+#include <array>
+#include <memory>
+#include <vector>
+#endif
+
+#include "vr/gvr/capi/include/gvr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @defgroup base Google VR Base C API
+/// @brief This is the Google VR C API. It supports clients writing VR
+/// experiences for head mounted displays that consist of a mobile phone and a
+/// VR viewer.
+///
+/// Example API usage:
+///
+///     #ifdef __ANDROID__
+///     // On Android, the gvr_context should almost always be obtained from
+///     // the Java GvrLayout object via
+///     // GvrLayout.getGvrApi().getNativeGvrContext().
+///     gvr_context* gvr = ...;
+///     #else
+///     gvr_context* gvr = gvr_create();
+///     #endif
+///
+///     gvr_initialize_gl(gvr);
+///
+///     gvr_buffer_viewport_list* viewport_list =
+///         gvr_buffer_viewport_list_create(gvr);
+///     gvr_get_recommended_buffer_viewports(gvr, viewport_list);
+///     gvr_buffer_viewport* left_eye_vp = gvr_buffer_viewport_create(gvr);
+///     gvr_buffer_viewport* right_eye_vp = gvr_buffer_viewport_create(gvr);
+///     gvr_buffer_viewport_list_get_item(viewport_list, 0, left_eye_vp);
+///     gvr_buffer_viewport_list_get_item(viewport_list, 1, right_eye_vp);
+///
+///     while (client_app_should_render) {
+///       // A client app should be ready for the render target size to change
+///       // whenever a new QR code is scanned, or a new viewer is paired.
+///       gvr_sizei render_target_size =
+///           gvr_get_maximum_effective_render_target_size(gvr);
+///       // The maximum effective render target size can be very large, most
+///       // applications need to scale down to compensate.
+///       render_target_size.width /= 2;
+///       render_target_size.height /= 2;
+///       gvr_swap_chain_resize_buffer(swap_chain, 0, render_target_size);
+///
+///       // This function will depend on your render loop's implementation.
+///       gvr_clock_time_point next_vsync = AppGetNextVsyncTime();
+///
+///       const gvr_mat4f head_view =
+///           gvr_get_head_space_from_start_space_rotation(gvr, next_vsync);
+///       const gvr_mat4f left_eye_view = MatrixMultiply(
+///           gvr_get_eye_from_head_matrix(gvr, GVR_LEFT_EYE), head_view);
+///       const gvr::Mat4f right_eye_view = MatrixMultiply(
+///           gvr_get_eye_from_head_matrix(gvr, GVR_RIGHT_EYE), head_view);
+///
+///       // Insert client rendering code here.
+///
+///       AppSetRenderTarget(offscreen_texture_id);
+///
+///       AppDoSomeRenderingForEye(
+///           gvr_buffer_viewport_get_source_uv(left_eye_view),
+///           left_eye_matrix);
+///       AppDoSomeRenderingForEye(
+///           gvr_buffer_viewport_get_source_uv(right_eye_view),
+///           right_eye_matrix);
+///       AppSetRenderTarget(primary_display);
+///
+///       gvr_frame_submit(&frame, viewport_list, head_matrix);
+///     }
+///
+///     // Cleanup memory.
+///     gvr_buffer_viewport_list_destroy(&viewport_list);
+///     gvr_buffer_viewport_destroy(&left_eye_vp);
+///     gvr_buffer_viewport_destroy(&right_eye_vp);
+///
+///     #ifdef __ANDROID__
+///     // On Android, The Java GvrLayout owns the gvr_context.
+///     #else
+///     gvr_destroy(gvr);
+///     #endif
+///
+/// Head tracking is enabled by default, and will begin as soon as the
+/// gvr_context is created. The client should call gvr_pause_tracking() and
+/// gvr_resume_tracking() when the app is paused and resumed, respectively.
+///
+/// Note: Unless otherwise noted, the functions in this API may not be
+/// thread-safe with respect to the gvr_context, and it is up the caller to use
+/// the API in a thread-safe manner.
+///
+/// @{
+
+/// Creates a new gvr instance.
+///
+/// The instance must remain valid as long as any GVR object is in use. When
+/// the application no longer needs to use the GVR SDK, call gvr_destroy().
+///
+///
+/// On Android, the gvr_context should *almost always* be obtained from the Java
+/// GvrLayout object, rather than explicitly created here. The GvrLayout should
+/// live in the app's View hierarchy, and its use is required to ensure
+/// consistent behavior across all varieties of GVR-compatible viewers. See
+/// the Java GvrLayout and GvrApi documentation for more details.
+///
+#ifdef __ANDROID__
+/// @param env The JNIEnv associated with the current thread.
+/// @param app_context The Android application context. This must be the
+///     application context, NOT an Activity context (Note: from any Android
+///     Activity in your app, you can call getApplicationContext() to
+///     retrieve the application context).
+/// @param class_loader The class loader to use when loading Java classes.
+///     This must be your app's main class loader (usually accessible through
+///     activity.getClassLoader() on any of your Activities).
+///
+/// @return Pointer to the created gvr instance, NULL on failure.
+gvr_context* gvr_create(JNIEnv* env, jobject app_context, jobject class_loader);
+#else
+/// @return Pointer to the created gvr instance, NULL on failure.
+gvr_context* gvr_create();
+#endif  // #ifdef __ANDROID__
+
+/// Gets the current GVR runtime version.
+///
+/// Note: This runtime version may differ from the version against which the
+/// client app is compiled, as defined by the semantic version components in
+/// gvr_version.h.
+///
+/// @return The version as a gvr_version.
+gvr_version gvr_get_version();
+
+/// Gets a string representation of the current GVR runtime version. This is of
+/// the form "MAJOR.MINOR.PATCH".
+///
+/// Note: This runtime version may differ from the version against which the
+/// client app is compiled, as defined in gvr_version.h by
+/// GVR_SDK_VERSION_STRING.
+///
+/// @return The version as a static char pointer.
+const char* gvr_get_version_string();
+
+/// Gets the current GVR error code, or GVR_ERROR_NONE if there is no error.
+/// This function doesn't clear the error code; see gvr_clear_error().
+///
+/// @param gvr Pointer to the gvr instance.
+/// @return The current gvr_error code, or GVR_ERROR_NONE if no error has
+///    occurred.
+int32_t gvr_get_error(gvr_context* gvr);
+
+/// Clears the current GVR error code, and returns the error code that was
+/// cleared.
+///
+/// @param gvr Pointer to the gvr instance.
+/// @return The gvr_error code that was cleared by this function, or
+/// GVR_ERROR_NONE if no error has occurred.
+int32_t gvr_clear_error(gvr_context* gvr);
+
+/// Gets a human-readable string representing the given error code.
+///
+/// @param error_code The gvr_error code.
+/// @return A human-readable string representing the error code.
+const char* gvr_get_error_string(int32_t error_code);
+
+/// Returns an opaque struct containing information about user preferences.
+///
+/// The returned struct will remain valid as long as the context is valid.
+/// The returned struct may be updated when the user changes their preferences,
+/// so this function only needs to be called once, and calling it multiple
+/// times will return the same object each time.
+///
+/// @param gvr Pointer to the gvr instance.
+/// @return An opaque struct containing information about user preferences.
+const gvr_user_prefs* gvr_get_user_prefs(gvr_context* gvr);
+
+/// Returns the controller handedness of the given gvr_user_prefs struct.
+///
+/// @param user_prefs Pointer to the gvr_user_prefs object returned by
+///     gvr_get_user_prefs.
+/// @return Either GVR_CONTROLLER_RIGHT_HANDED or GVR_CONTROLLER_LEFT_HANDED
+///     depending on which hand the user holds the controller in.
+int32_t gvr_user_prefs_get_controller_handedness(
+    const gvr_user_prefs* user_prefs);
+
+/// Destroys a gvr_context instance.  The parameter will be nulled by this
+/// operation.  Once this function is called, the behavior of any subsequent
+/// call to a GVR SDK function that references objects created from this
+/// context is undefined.
+///
+/// @param gvr Pointer to a pointer to the gvr instance to be destroyed and
+///     nulled.
+void gvr_destroy(gvr_context** gvr);
+
+/// Initializes necessary GL-related objects and uses the current thread and
+/// GL context for rendering. Please make sure that a valid GL context is
+/// available when this function is called.  This should never be called more
+/// than once on the same GL context (doing so would cause resource leaks).
+///
+/// @param gvr Pointer to the gvr instance to be initialized.
+void gvr_initialize_gl(gvr_context* gvr);
+
+/// Gets whether asynchronous reprojection is currently enabled.
+///
+/// If enabled, frames will be collected by the rendering system and
+/// asynchronously re-projected in sync with the scanout of the display. This
+/// feature may not be available on every platform, and requires a
+/// high-priority render thread with special extensions to function properly.
+///
+/// Note: On Android, this feature can be enabled solely via the GvrLayout Java
+/// instance which (indirectly) owns this gvr_context. The corresponding
+/// method call is GvrLayout.setAsyncReprojectionEnabled().
+///
+/// Note: Because of the above requirements, asynchronous reprojection is only
+/// currently available on Daydream-ready Android devices.  This function will
+/// always return false on other devices.
+///
+/// @param gvr Pointer to the gvr instance.
+/// @return Whether async reprojection is enabled. Defaults to false.
+bool gvr_get_async_reprojection_enabled(const gvr_context* gvr);
+
+/// Gets the recommended buffer viewport configuration, populating a previously
+/// allocated gvr_buffer_viewport_list object. The updated values include the
+/// per-eye recommended viewport and field of view for the target.
+///
+/// When the recommended viewports are used for distortion rendering, this
+/// method should always be called after calling refresh_viewer_profile(). That
+/// will ensure that the populated viewports reflect the currently paired
+/// viewer.
+///
+/// @param gvr Pointer to the gvr instance from which to get the viewports.
+/// @param viewport_list Pointer to a previously allocated viewport list. This
+///     will be populated with the recommended buffer viewports and resized if
+///     necessary.
+void gvr_get_recommended_buffer_viewports(
+    const gvr_context* gvr, gvr_buffer_viewport_list* viewport_list);
+
+/// Gets the screen (non-distorted) buffer viewport configuration, populating a
+/// previously allocated gvr_buffer_viewport_list object. The updated values
+/// include the per-eye recommended viewport and field of view for the target.
+///
+/// @param gvr Pointer to the gvr instance from which to get the viewports.
+/// @param viewport_list Pointer to a previously allocated viewport list. This
+///     will be populated with the screen buffer viewports and resized if
+///     necessary.
+void gvr_get_screen_buffer_viewports(const gvr_context* gvr,
+                                     gvr_buffer_viewport_list* viewport_list);
+
+/// Returns the maximum effective size for the client's render target, given the
+/// parameters of the head mounted device selected. At this resolution, we have
+/// a 1:1 ratio between source pixels and screen pixels in the most magnified
+/// region of the screen. Applications should rarely, if ever, need to render
+/// to a larger target, as it will simply result in sampling artifacts.
+///
+/// Note that this is probably too large for most applications to use as a
+/// render target size. Applications should scale this value to be appropriate
+/// to their graphical load.
+///
+/// @param gvr Pointer to the gvr instance from which to get the size.
+///
+/// @return Maximum effective size for the target render target.
+gvr_sizei gvr_get_maximum_effective_render_target_size(const gvr_context* gvr);
+
+/// Returns a non-distorted size for the screen, given the parameters
+/// of the phone and/or the head mounted device selected.
+///
+/// @param gvr Pointer to the gvr instance from which to get the size.
+///
+/// @return Screen (non-distorted) size for the render target.
+gvr_sizei gvr_get_screen_target_size(const gvr_context* gvr);
+
+// Sets the size of the underlying render surface.
+//
+// By default, it is assumed that the display size matches the surface
+// size. If that is the case for the client app, this method need never be
+// called. However, in certain cases (e.g., hardware scaling), this will not
+// always hold, in which case the distortion pass must be informed of the
+// custom surface size.
+//
+// Note that the caller is responsible for resizing any BufferSpec objects
+// created before this function is called. Otherwise there will be rendering
+// artifacts, such as edges appearing pixelated. This function will change the
+// result of get_maximum_effective_render_target_size(), so that function can be
+// used to compute the appropriate size for buffers.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param surface_size_pixels The size in pixels of the display surface. If
+//     non-empty, this will be used in conjunction with the current display to
+//     perform properly scaled distortion. If empty, it is assumed that the
+//     rendering surface dimensions match that of the active display.
+void gvr_set_surface_size(gvr_context* gvr, gvr_sizei surface_size_pixels);
+
+/// Performs postprocessing, including lens distortion, on the contents of the
+/// passed texture and shows the result on the screen. Lens distortion is
+/// determined by the parameters of the viewer encoded in its QR code. The
+/// passed texture is not modified.
+///
+/// If the application does not call gvr_initialize_gl() before calling this
+/// function, the results are undefined.
+///
+/// @deprecated This function exists only to support legacy rendering pathways
+///     for Cardboard devices. It is incompatible with the low-latency
+///     experiences supported by async reprojection. Use the swap chain API
+///     instead.
+///
+/// @param gvr Pointer to the gvr instance which will do the distortion.
+/// @param texture_id The OpenGL ID of the texture that contains the next frame
+///     to be displayed.
+/// @param viewport_list Rendering parameters.
+/// @param head_space_from_start_space This parameter is ignored.
+/// @param target_presentation_time This parameter is ignored.
+void gvr_distort_to_screen(gvr_context* gvr, int32_t texture_id,
+                           const gvr_buffer_viewport_list* viewport_list,
+                           gvr_mat4f head_space_from_start_space,
+                           gvr_clock_time_point target_presentation_time);
+
+/// Queries whether a particular GVR feature is supported by the underlying
+/// platform.
+///
+/// @param gvr The context to query against.
+/// @param feature The gvr_feature type being queried.
+/// @return true if feature is supported, false otherwise.
+bool gvr_is_feature_supported(const gvr_context* gvr, int32_t feature);
+
+/// @}
+
+/////////////////////////////////////////////////////////////////////////////
+// Viewports and viewport lists
+/////////////////////////////////////////////////////////////////////////////
+/// @defgroup viewport Viewports and viewport lists
+/// @brief Objects to define the mapping between the application's rendering
+///     output and the user's field of view.
+/// @{
+
+/// Creates a gvr_buffer_viewport instance.
+gvr_buffer_viewport* gvr_buffer_viewport_create(gvr_context* gvr);
+
+/// Frees a gvr_buffer_viewport instance and clears the pointer.
+void gvr_buffer_viewport_destroy(gvr_buffer_viewport** viewport);
+
+/// Gets the UV coordinates specifying where the output buffer is sampled.
+///
+/// @param viewport The buffer viewport.
+/// @return UV coordinates as a rectangle.
+gvr_rectf gvr_buffer_viewport_get_source_uv(
+    const gvr_buffer_viewport* viewport);
+
+/// Sets the UV coordinates specifying where the output buffer should be
+/// sampled when compositing the final distorted image.
+///
+/// @param viewport The buffer viewport.
+/// @param uv The new UV coordinates for sampling. The coordinates must be
+///     valid, that is, left <= right and bottom <= top. Otherwise an empty
+///     source region is set, which will result in no output for this viewport.
+void gvr_buffer_viewport_set_source_uv(gvr_buffer_viewport* viewport,
+                                       gvr_rectf uv);
+
+/// Retrieves the field of view for the referenced buffer region.
+///
+/// This is a helper that converts the stored projection matrix to a field of
+/// view. Note that if the previously set projection matrix cannot be expressed
+/// as a view frustum aligned with the eye's optical axis, the result will be
+/// incorrect.
+///
+/// @param viewport The buffer viewport.
+/// @return The field of view of the rendered image, in degrees.
+gvr_rectf gvr_buffer_viewport_get_source_fov(
+    const gvr_buffer_viewport* viewport);
+
+/// Sets the field of view for the viewport contents.
+///
+/// This is a helper that sets the projection matrix in such a way that the
+/// viewport's contents fill the specified FOV around the eye's optical axis.
+///
+/// @param viewport The buffer viewport.
+/// @param fov The field of view to use when compositing the rendered image,
+///     in degrees.
+void gvr_buffer_viewport_set_source_fov(gvr_buffer_viewport* viewport,
+                                        gvr_rectf fov);
+
+/// Gets the matrix that positions the viewport in eye space.
+///
+/// @param viewport The buffer viewport.
+/// @return Matrix that transforms a quad with vertices (-1, -1, 0), (1, -1, 0),
+///     (-1, 1, 0), (1, 1, 0) representing the viewport contents to its desired
+///     eye space position for the target eye.
+gvr_mat4f gvr_buffer_viewport_get_transform(
+    const gvr_buffer_viewport* viewport);
+
+/// Sets the matrix that positions the viewport in eye space.
+///
+/// @param viewport The buffer viewport.
+/// @param transform Matrix that transforms a quad with vertices (-1, -1, 0),
+///     (1, -1, 0), (-1, 1, 0), (1, 1, 0) representing the viewport contents to
+///     its desired eye space position for the target eye.
+void gvr_buffer_viewport_set_transform(gvr_buffer_viewport* viewport,
+                                       gvr_mat4f transform);
+
+/// Gets the target logical eye for the specified viewport.
+///
+/// @param viewport The buffer viewport.
+/// @return Index of the target logical eye for this viewport.
+int32_t gvr_buffer_viewport_get_target_eye(const gvr_buffer_viewport* viewport);
+
+/// Sets the target logical eye for the specified viewport.
+///
+/// @param viewport The buffer viewport.
+/// @param index Index of the target logical eye.
+void gvr_buffer_viewport_set_target_eye(gvr_buffer_viewport* viewport,
+                                        int32_t index);
+
+/// Gets the index of the source buffer from which the viewport reads its
+/// undistorted pixels.
+///
+/// @param viewport The buffer viewport.
+/// @return Index of the source buffer. This corresponds to the index in the
+///     list of buffer specs that was passed to gvr_swap_chain_create().
+int32_t gvr_buffer_viewport_get_source_buffer_index(
+    const gvr_buffer_viewport* viewport);
+
+/// Sets the buffer from which the viewport reads its undistorted pixels.
+///
+/// To use the contents of the external surface as buffer contents, associate an
+/// external surface with the viewport by calling
+/// gvr_buffer_viewport_set_external_surface_id(), then call this function and
+/// pass GVR_BUFFER_INDEX_EXTERNAL_SURFACE.
+///
+/// @param viewport The buffer viewport.
+/// @param buffer_index The index of the source buffer. This is either an index
+///     in the list of buffer specs that was passed to
+///     gvr_swap_chain_create(), or GVR_BUFFER_INDEX_EXTERNAL_SURFACE.
+void gvr_buffer_viewport_set_source_buffer_index(
+    gvr_buffer_viewport* viewport, int32_t buffer_index);
+
+/// Gets the ID of the externally-managed Surface texture from which this
+/// viewport reads undistored pixels.
+///
+/// @param viewport The buffer viewport.
+/// @return ID of the externally-managed Surface of undistorted pixels.
+int32_t gvr_buffer_viewport_get_external_surface_id(
+    const gvr_buffer_viewport* viewport);
+
+/// Sets the ID of the externally-managed Surface texture from which this
+/// viewport reads. The ID is issued by GvrLayout.
+///
+/// @param viewport The buffer viewport.
+/// @param external_surface_id The ID of the surface to read from.
+void gvr_buffer_viewport_set_external_surface_id(
+    gvr_buffer_viewport* viewport, int32_t external_surface_id);
+
+/// Gets the type of reprojection to perform on the specified viewport.
+///
+/// @param viewport The buffer viewport.
+/// @return Type of reprojection that is applied to the viewport.
+int32_t gvr_buffer_viewport_get_reprojection(
+    const gvr_buffer_viewport* viewport);
+
+/// Sets the type of reprojection to perform on the specified viewport.
+/// Viewports that display world content should use full reprojection.
+/// Viewports that display head-locked UI should disable reprojection to avoid
+/// excessive judder. The default is to perform full reprojection.
+///
+/// @param viewport The buffer viewport.
+/// @param reprojection Type of reprojection that will be applied to the passed
+///     viewport.
+void gvr_buffer_viewport_set_reprojection(gvr_buffer_viewport* viewport,
+                                          int32_t reprojection);
+
+/// Compares two gvr_buffer_viewport instances and returns true if they specify
+/// the same view mapping.
+///
+/// @param a Instance of a buffer viewport.
+/// @param b Another instance of a buffer viewport.
+/// @return True if the passed viewports are the same.
+bool gvr_buffer_viewport_equal(const gvr_buffer_viewport* a,
+                               const gvr_buffer_viewport* b);
+
+/// Creates a new, empty list of viewports. The viewport list defines how the
+/// application's rendering output should be transformed into the stabilized,
+/// lens-distorted image that is sent to the screen.
+///
+/// The caller should populate the returned viewport using one of:
+///   - gvr_get_recommended_buffer_viewports()
+///   - gvr_get_screen_buffer_viewports()
+///   - gvr_buffer_viewport_list_set_item()
+///
+/// @param gvr Pointer the gvr instance from which to allocate the viewport
+/// list.
+/// @return Pointer to an allocated gvr_buffer_viewport_list object. The caller
+//      is responsible for calling gvr_buffer_viewport_list_destroy() on the
+///     returned object when it is no longer needed.
+gvr_buffer_viewport_list* gvr_buffer_viewport_list_create(
+    const gvr_context* gvr);
+
+/// Destroys a gvr_buffer_viewport_list instance. The parameter will be nulled
+/// by this operation.
+///
+/// @param viewport_list Pointer to a pointer to the viewport list instance to
+///     be destroyed and nulled.
+void gvr_buffer_viewport_list_destroy(gvr_buffer_viewport_list** viewport_list);
+
+/// Returns the size of the given viewport list.
+///
+/// @param viewport_list Pointer to a viewport list.
+/// @return The number of entries in the viewport list.
+size_t gvr_buffer_viewport_list_get_size(
+    const gvr_buffer_viewport_list* viewport_list);
+
+/// Retrieve a buffer viewport entry from a list.
+///
+/// @param viewport_list Pointer to the previously allocated viewport list.
+/// @param index Zero-based index of the viewport entry to query. Must be
+///    smaller than the list size.
+/// @param viewport The buffer viewport structure that will be populated with
+///    retrieved data.
+void gvr_buffer_viewport_list_get_item(
+    const gvr_buffer_viewport_list* viewport_list, size_t index,
+    gvr_buffer_viewport* viewport);
+
+/// Update an element of the viewport list or append a new one at the end.
+///
+/// @param viewport_list Pointer to a previously allocated viewport list.
+/// @param index Index of the buffer viewport entry to update. If the
+///     `viewport_list` size is equal to the index, a new viewport entry will be
+///     added. The `viewport_list` size must *not* be less than the index value.
+/// @param viewport A pointer to the buffer viewport object.
+void gvr_buffer_viewport_list_set_item(gvr_buffer_viewport_list* viewport_list,
+                                       size_t index,
+                                       const gvr_buffer_viewport* viewport);
+
+/// @}
+
+/////////////////////////////////////////////////////////////////////////////
+// Swapchains and frames
+/////////////////////////////////////////////////////////////////////////////
+/// @defgroup swap_chain Swap chains and frames
+/// @brief Functions to create a swap chain, manipulate it and submit frames
+///     for lens distortion and presentation on the screen.
+/// @{
+
+/// Creates a default buffer specification.
+gvr_buffer_spec* gvr_buffer_spec_create(gvr_context* gvr);
+
+/// Destroy the buffer specification and null the pointer.
+void gvr_buffer_spec_destroy(gvr_buffer_spec** spec);
+
+/// Gets the size of the buffer to be created.
+///
+/// @param spec Buffer specification.
+/// @return Size of the pixel buffer. The default is equal to the recommended
+///     render target size at the time when the specification was created.
+gvr_sizei gvr_buffer_spec_get_size(const gvr_buffer_spec* spec);
+
+/// Sets the size of the buffer to be created.
+///
+/// @param spec Buffer specification.
+/// @param size The size. Width and height must both be greater than zero.
+///     Otherwise, the application is aborted.
+void gvr_buffer_spec_set_size(gvr_buffer_spec* spec, gvr_sizei size);
+
+/// Gets the number of samples per pixel in the buffer to be created.
+///
+/// @param spec Buffer specification.
+/// @return Value >= 1 giving the number of samples. 1 means multisampling is
+///     disabled. Negative values and 0 are never returned.
+int32_t gvr_buffer_spec_get_samples(const gvr_buffer_spec* spec);
+
+/// Sets the number of samples per pixel in the buffer to be created.
+///
+/// @param spec Buffer specification.
+/// @param num_samples The number of samples. Negative values are an error.
+///     The values 0 and 1 are treated identically and indicate that
+//      multisampling should be disabled.
+void gvr_buffer_spec_set_samples(gvr_buffer_spec* spec, int32_t num_samples);
+
+/// Sets the color format for the buffer to be created. Default format is
+/// GVR_COLOR_FORMAT_RGBA_8888.
+///
+/// @param spec Buffer specification.
+/// @param color_format The color format for the buffer. Valid formats are in
+///     the gvr_color_format_type enum.
+void gvr_buffer_spec_set_color_format(gvr_buffer_spec* spec,
+                                      int32_t color_format);
+
+/// Sets the depth and stencil format for the buffer to be created. Currently,
+/// only packed stencil formats are supported. Default format is
+/// GVR_DEPTH_STENCIL_FORMAT_DEPTH_16.
+///
+/// @param spec Buffer specification.
+/// @param depth_stencil_format The depth and stencil format for the buffer.
+///     Valid formats are in the gvr_depth_stencil_format_type enum.
+void gvr_buffer_spec_set_depth_stencil_format(gvr_buffer_spec* spec,
+                                              int32_t depth_stencil_format);
+
+/// Creates a swap chain from the given buffer specifications.
+/// This is a potentially time-consuming operation. All frames within the
+/// swapchain will be allocated. Once rendering is stopped, call
+/// gvr_swap_chain_destroy() to free GPU resources. The passed gvr_context must
+/// not be destroyed until then.
+///
+/// Swap chains can have no buffers. This is useful when only displaying
+/// external surfaces. When `count` is zero, `buffers` must be null.
+///
+/// @param gvr GVR instance for which a swap chain will be created.
+/// @param buffers Array of pixel buffer specifications. Each frame in the
+///     swap chain will be composed of these buffers.
+/// @param count Number of buffer specifications in the array.
+/// @return Opaque handle to the newly created swap chain.
+gvr_swap_chain* gvr_swap_chain_create(gvr_context* gvr,
+                                      const gvr_buffer_spec** buffers,
+                                      int32_t count);
+
+/// Destroys the swap chain and nulls the pointer.
+void gvr_swap_chain_destroy(gvr_swap_chain** swap_chain);
+
+/// Gets the number of buffers in each frame of the swap chain.
+int32_t gvr_swap_chain_get_buffer_count(const gvr_swap_chain* swap_chain);
+
+/// Retrieves the size of the specified pixel buffer. Note that if the buffer
+/// was resized while the current frame was acquired, the return value will be
+/// different than the value obtained from the equivalent function for the
+/// current frame.
+///
+/// @param swap_chain The swap chain.
+/// @param index Index of the pixel buffer.
+/// @return Size of the specified pixel buffer in frames that will be returned
+///     from gvr_swap_chain_acquire_frame().
+gvr_sizei gvr_swap_chain_get_buffer_size(gvr_swap_chain* swap_chain,
+                                         int32_t index);
+
+/// Resizes the specified pixel buffer to the given size. The frames are resized
+/// when they are unused, so the currently acquired frame will not be resized
+/// immediately.
+///
+/// @param swap_chain The swap chain.
+/// @param index Index of the pixel buffer to resize.
+/// @param size New size for the specified pixel buffer.
+void gvr_swap_chain_resize_buffer(gvr_swap_chain* swap_chain, int32_t index,
+                                  gvr_sizei size);
+
+/// Acquires a frame from the swap chain for rendering. Buffers that are part of
+/// the frame can then be bound with gvr_frame_bind_buffer(). Once the frame
+/// is finished and all its constituent buffers are ready, call
+/// gvr_frame_submit() to display it while applying lens distortion.
+///
+/// @param swap_chain The swap chain.
+/// @return Handle to the acquired frame. NULL if the swap chain is invalid,
+///     or if acquire has already been called on this swap chain.
+gvr_frame* gvr_swap_chain_acquire_frame(gvr_swap_chain* swap_chain);
+
+/// Binds a pixel buffer that is part of the frame to the OpenGL framebuffer.
+///
+/// @param frame Frame handle acquired from the swap chain.
+/// @param index Index of the pixel buffer to bind. This corresponds to the
+///     index in the buffer spec list that was passed to
+///     gvr_swap_chain_create().
+void gvr_frame_bind_buffer(gvr_frame* frame, int32_t index);
+
+/// Unbinds any buffers bound from this frame and binds the default OpenGL
+/// framebuffer.
+void gvr_frame_unbind(gvr_frame* frame);
+
+/// Returns the dimensions of the pixel buffer with the specified index. Note
+/// that a frame that was acquired before resizing a swap chain buffer will not
+/// be resized until it is submitted to the swap chain.
+///
+/// @param frame Frame handle.
+/// @param index Index of the pixel buffer to inspect.
+/// @return Dimensions of the specified pixel buffer.
+gvr_sizei gvr_frame_get_buffer_size(const gvr_frame* frame, int32_t index);
+
+/// Gets the name (ID) of the framebuffer object associated with the specified
+/// buffer. The OpenGL state is not modified.
+///
+/// @param frame Frame handle.
+/// @param index Index of a pixel buffer.
+/// @return OpenGL object name (ID) of a framebuffer object which can be used
+///     to render into the buffer. The ID is valid only until the frame is
+///     submitted.
+int32_t gvr_frame_get_framebuffer_object(const gvr_frame* frame, int32_t index);
+
+/// Submits the frame for distortion and display on the screen. The passed
+/// pointer is nulled to prevent reuse.
+///
+/// @param frame The frame to submit.
+/// @param list Buffer view configuration to be used for this frame.
+/// @param head_space_from_start_space Transform from start space (space with
+///     head at the origin at last tracking reset) to head space (space with
+///     head at the origin and axes aligned to the view vector).
+void gvr_frame_submit(gvr_frame** frame, const gvr_buffer_viewport_list* list,
+                      gvr_mat4f head_space_from_start_space);
+
+/// Resets the OpenGL framebuffer binding to what it was at the time the
+/// passed gvr_context was created.
+void gvr_bind_default_framebuffer(gvr_context* gvr);
+
+/// @}
+
+/////////////////////////////////////////////////////////////////////////////
+// Head tracking
+/////////////////////////////////////////////////////////////////////////////
+/// @defgroup Headtracking Head tracking
+/// @brief Functions for managing head tracking.
+/// @{
+
+/// Gets the current monotonic system time.
+///
+/// @return The current monotonic system time.
+gvr_clock_time_point gvr_get_time_point_now();
+
+/// Gets the rotation from start space to head space.  The head space is a
+/// space where the head is at the origin and faces the -Z direction.
+///
+/// @param gvr Pointer to the gvr instance from which to get the pose.
+/// @param time The time at which to get the head pose. The time should be in
+///     the future. If the time is not in the future, it will be clamped to now.
+/// @return A matrix representation of the rotation from start space (the space
+///     where the head was last reset) to head space (the space with the head
+///     at the origin, and the axes aligned to the view vector).
+gvr_mat4f gvr_get_head_space_from_start_space_rotation(
+    const gvr_context* gvr, const gvr_clock_time_point time);
+
+/// Applies a simple neck model translation based on the rotation of the
+/// provided head pose.
+///
+/// Note: Neck model application may not be appropriate for all tracking
+/// scenarios, e.g., when tracking is non-biological.
+///
+/// @param gvr Pointer to the context instance from which the pose was obtained.
+/// @param head_space_from_start_space_rotation The head rotation as returned by
+///     gvr_get_head_space_from_start_space_rotation().
+/// @param factor A scaling factor for the neck model offset, clamped from 0 to
+///     1. This should be 1 for most scenarios, while 0 will effectively disable
+///     neck model application. This value can be animated to smoothly
+///     interpolate between alternative (client-defined) neck models.
+/// @return The new head pose with the neck model applied.
+gvr_mat4f gvr_apply_neck_model(const gvr_context* gvr,
+                               gvr_mat4f head_space_from_start_space_rotation,
+                               float factor);
+
+/// Pauses head tracking, disables all sensors (to save power).
+///
+/// @param gvr Pointer to the gvr instance for which tracking will be paused and
+///     sensors disabled.
+void gvr_pause_tracking(gvr_context* gvr);
+
+/// Resumes head tracking, re-enables all sensors.
+///
+/// @param gvr Pointer to the gvr instance for which tracking will be resumed.
+void gvr_resume_tracking(gvr_context* gvr);
+
+/// Resets head tracking.
+///
+/// This API call is deprecated. Use gvr_recenter_tracking instead, which
+/// accomplishes the same effects but avoids the undesirable side-effects of
+/// a full reset (temporary loss of tracking quality).
+///
+/// Only to be used by Cardboard apps. Daydream apps must not call this. On the
+/// Daydream platform, recentering is handled automatically and should never
+/// be triggered programatically by applications. Hybrid apps that support both
+/// Cardboard and Daydream must only call this function when in Cardboard mode
+/// (that is, when the phone is paired with a Cardboard viewer), never in
+/// Daydream mode.
+///
+/// @param gvr Pointer to the gvr instance for which tracking will be reseted.
+/// @deprecated Calls to this method can be safely replaced by calls to
+//    gvr_recenter_tracking.
+void gvr_reset_tracking(gvr_context* gvr);
+
+/// Recenters the head orientation (resets the yaw to zero, leaving pitch and
+/// roll unmodified).
+///
+/// Only to be used by Cardboard apps. Daydream apps must not call this. On the
+/// Daydream platform, recentering is handled automatically and should never
+/// be triggered programatically by applications. Hybrid apps that support both
+/// Cardboard and Daydream must only call this function when in Cardboard mode
+/// (that is, when the phone is paired with a Cardboard viewer), never in
+/// Daydream mode.
+///
+/// @param gvr Pointer to the gvr instance for which tracking will be
+///     recentered.
+void gvr_recenter_tracking(gvr_context* gvr);
+
+/// @}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Head mounted display.
+/////////////////////////////////////////////////////////////////////////////
+/// @defgroup HMD Head Mounted Display
+/// @brief Functions for managing viewer information.
+/// @{
+
+/// Sets the default viewer profile specified by viewer_profile_uri.
+/// The viewer_profile_uri that is passed in will be ignored if a valid
+/// viewer profile has already been stored on the device that the app
+/// is running on.
+///
+/// Note: This function has the potential of blocking for up to 30 seconds for
+/// each redirect if a shortened URI is passed in as argument. It will try to
+/// unroll the shortened URI for a maximum number of 5 times if the redirect
+/// continues. In that case, it is recommended to create a separate thread to
+/// call this function so that other tasks like rendering will not be blocked
+/// on this. The blocking can be avoided if a standard URI is passed in.
+///
+/// @param gvr Pointer to the gvr instance which to set the profile on.
+/// @param viewer_profile_uri A string that contains either the shortened URI or
+///     the standard URI representing the viewer profile that the app should be
+///     using. If the valid viewer profile can be found on the device, the URI
+///     that is passed in will be ignored and nothing will happen. Otherwise,
+///     gvr will look for the viewer profile specified by viewer_profile_uri,
+///     and it will be stored if found. Also, the values will be applied to gvr.
+///     A valid standard URI can be generated from this page:
+///     https://www.google.com/get/cardboard/viewerprofilegenerator/
+/// @return True if the viewer profile specified by viewer_profile_uri was
+///     successfully stored and applied, false otherwise.
+bool gvr_set_default_viewer_profile(gvr_context* gvr,
+                                    const char* viewer_profile_uri);
+
+/// Refreshes gvr_context with the viewer profile that is stored on the device.
+/// If it can not find the viewer profile, nothing will happen.
+///
+/// @param gvr Pointer to the gvr instance to refresh the profile on.
+void gvr_refresh_viewer_profile(gvr_context* gvr);
+
+/// Gets the name of the viewer vendor.
+///
+/// @param gvr Pointer to the gvr instance from which to get the vendor.
+/// @return A pointer to the vendor name. May be NULL if no viewer is paired.
+///     WARNING: This method guarantees the validity of the returned pointer
+///     only until the next use of the `gvr` context. The string should be
+///     copied immediately if persistence is required.
+const char* gvr_get_viewer_vendor(const gvr_context* gvr);
+
+/// Gets the name of the viewer model.
+///
+/// @param gvr Pointer to the gvr instance from which to get the name.
+/// @return A pointer to the model name. May be NULL if no viewer is paired.
+///     WARNING: This method guarantees the validity of the returned pointer
+///     only until the next use of the `gvr` context. The string should be
+///     copied immediately if persistence is required.
+const char* gvr_get_viewer_model(const gvr_context* gvr);
+
+/// Gets the type of the viewer, as defined by gvr_viewer_type.
+///
+/// @param gvr Pointer to the gvr instance from which to get the viewer type.
+/// @return The gvr_viewer_type of the currently paired viewer.
+int32_t gvr_get_viewer_type(const gvr_context* gvr);
+
+/// Gets the transformation matrix to convert from Head Space to Eye Space for
+/// the given eye.
+///
+/// @param gvr Pointer to the gvr instance from which to get the matrix.
+/// @param eye Selected gvr_eye type.
+/// @return Transformation matrix from Head Space to selected Eye Space.
+gvr_mat4f gvr_get_eye_from_head_matrix(const gvr_context* gvr,
+                                       const int32_t eye);
+
+/// Gets the window bounds.
+///
+/// @param gvr Pointer to the gvr instance from which to get the bounds.
+///
+/// @return Window bounds in physical pixels.
+gvr_recti gvr_get_window_bounds(const gvr_context* gvr);
+
+/// Computes the distorted point for a given point in a given eye.  The
+/// distortion inverts the optical distortion caused by the lens for the eye.
+/// Due to chromatic aberration, the distortion is different for each
+/// color channel.
+///
+/// @param gvr Pointer to the gvr instance which will do the computing.
+/// @param eye The gvr_eye type (left or right).
+/// @param uv_in A point in screen eye Viewport Space in [0,1]^2 with (0, 0)
+///     in the lower left corner of the eye's viewport and (1, 1) in the
+///     upper right corner of the eye's viewport.
+/// @param uv_out A pointer to an array of (at least) 3 elements, with each
+///     element being a Point2f representing a point in render texture eye
+///     Viewport Space in [0,1]^2 with (0, 0) in the lower left corner of the
+///     eye's viewport and (1, 1) in the upper right corner of the eye's
+///     viewport.
+///     `uv_out[0]` is the corrected position of `uv_in` for the red channel
+///     `uv_out[1]` is the corrected position of `uv_in` for the green channel
+///     `uv_out[2]` is the corrected position of `uv_in` for the blue channel
+void gvr_compute_distorted_point(const gvr_context* gvr, const int32_t eye,
+                                 const gvr_vec2f uv_in, gvr_vec2f uv_out[3]);
+
+/// @}
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+namespace gvr {
+
+/// Convenience C++ wrapper for gvr_user_prefs.
+class UserPrefs {
+ public:
+  /// Creates a C++ wrapper for a gvr_user_prefs object. Note that unlike most
+  /// of the C++ wrappers in the API, this does not take ownership, as the
+  /// gvr_user_prefs will remain valid for the lifetime of the GVR context.
+  explicit UserPrefs(const gvr_user_prefs* user_prefs)
+      : user_prefs_(user_prefs) {}
+
+  UserPrefs(UserPrefs&& other) : user_prefs_(nullptr) {
+    std::swap(user_prefs_, other.user_prefs_);
+  }
+
+  UserPrefs& operator=(UserPrefs&& other) {
+    std::swap(user_prefs_, other.user_prefs_);
+    return *this;
+  }
+
+  /// For more information, see gvr_user_prefs_get_controller_handedness().
+  ControllerHandedness GetControllerHandedness() const {
+    return static_cast<ControllerHandedness>(
+        gvr_user_prefs_get_controller_handedness(user_prefs_));
+  }
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  const gvr_user_prefs* cobj() const { return user_prefs_; }
+
+ private:
+  const gvr_user_prefs* user_prefs_;
+
+  // Disallow copy and assign.
+  UserPrefs(const UserPrefs&);
+  void operator=(const UserPrefs&);
+};
+
+/// Convenience C++ wrapper for the opaque gvr_buffer_viewport type.
+/// The constructor allocates memory, so when used in tight loops, instances
+/// should be reused.
+class BufferViewport {
+ public:
+  BufferViewport(BufferViewport&& other)
+      : viewport_(nullptr) {
+    std::swap(viewport_, other.viewport_);
+  }
+
+  BufferViewport& operator=(BufferViewport&& other) {
+    std::swap(viewport_, other.viewport_);
+    return *this;
+  }
+
+  ~BufferViewport() {
+    if (viewport_) gvr_buffer_viewport_destroy(&viewport_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_get_source_fov().
+  Rectf GetSourceFov() const {
+    return gvr_buffer_viewport_get_source_fov(viewport_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_set_source_fov().
+  void SetSourceFov(const Rectf& fov) {
+    gvr_buffer_viewport_set_source_fov(viewport_, fov);
+  }
+
+  /// For more information, see gvr_buffer_viewport_get_transform().
+  Mat4f GetTransform() const {
+    return gvr_buffer_viewport_get_transform(viewport_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_set_transform().
+  void SetTransform(const Mat4f& transform) {
+    gvr_buffer_viewport_set_transform(viewport_, transform);
+  }
+
+  /// For more information, see gvr_buffer_viewport_get_source_uv().
+  Rectf GetSourceUv() const {
+    return gvr_buffer_viewport_get_source_uv(viewport_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_set_source_uv().
+  void SetSourceUv(const Rectf& uv) {
+    gvr_buffer_viewport_set_source_uv(viewport_, uv);
+  }
+
+  /// For more information, see gvr_buffer_viewport_get_target_eye().
+  Eye GetTargetEye() const {
+    return static_cast<Eye>(gvr_buffer_viewport_get_target_eye(viewport_));
+  }
+
+  /// For more information, see gvr_buffer_viewport_set_target_eye().
+  void SetTargetEye(Eye eye) {
+    gvr_buffer_viewport_set_target_eye(viewport_, eye);
+  }
+
+  /// For more information, see gvr_buffer_viewport_get_source_buffer_index().
+  int32_t GetSourceBufferIndex() const {
+    return gvr_buffer_viewport_get_source_buffer_index(viewport_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_set_source_buffer_index().
+  void SetSourceBufferIndex(int32_t buffer_index) {
+    gvr_buffer_viewport_set_source_buffer_index(viewport_, buffer_index);
+  }
+
+  /// For more information, see gvr_buffer_viewport_get_external_surface_id().
+  int32_t GetExternalSurfaceId() const {
+    return gvr_buffer_viewport_get_external_surface_id(viewport_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_set_external_surface_id().
+  void SetExternalSurfaceId(const int32_t external_surface_id) {
+    gvr_buffer_viewport_set_external_surface_id(viewport_, external_surface_id);
+  }
+
+  /// For more information, see gvr_buffer_viewport_get_reprojection().
+  gvr_reprojection GetReprojection() const {
+    return static_cast<gvr_reprojection>(
+        gvr_buffer_viewport_get_reprojection(viewport_));
+  }
+  /// For more information, see gvr_buffer_viewport_set_reprojection().
+  void SetReprojection(gvr_reprojection reprojection) {
+    gvr_buffer_viewport_set_reprojection(viewport_, reprojection);
+  }
+
+  /// For more information, see gvr_buffer_viewport_equal().
+  bool operator==(const BufferViewport& other) const {
+    return gvr_buffer_viewport_equal(viewport_, other.viewport_) ? true : false;
+  }
+  bool operator!=(const BufferViewport& other) const {
+    return !(*this == other);
+  }
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and takes ownership.
+  explicit BufferViewport(gvr_buffer_viewport* viewport)
+      : viewport_(viewport) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_buffer_viewport* cobj() { return viewport_; }
+  const gvr_buffer_viewport* cobj() const { return viewport_; }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_buffer_viewport* release() {
+    auto result = viewport_;
+    viewport_ = nullptr;
+    return result;
+  }
+  /// @}
+
+ private:
+  friend class GvrApi;
+  friend class BufferViewportList;
+
+  explicit BufferViewport(gvr_context* gvr)
+      : viewport_(gvr_buffer_viewport_create(gvr)) {}
+
+  gvr_buffer_viewport* viewport_;
+};
+
+/// Convenience C++ wrapper for the opaque gvr_buffer_viewport_list type. This
+/// class will automatically release the wrapped gvr_buffer_viewport_list upon
+/// destruction. It can only be created via a `GvrApi` instance, and its
+/// validity is tied to the lifetime of that instance.
+class BufferViewportList {
+ public:
+  BufferViewportList(BufferViewportList&& other)
+      : context_(nullptr), viewport_list_(nullptr) {
+    std::swap(context_, other.context_);
+    std::swap(viewport_list_, other.viewport_list_);
+  }
+
+  BufferViewportList& operator=(BufferViewportList&& other) {
+    std::swap(context_, other.context_);
+    std::swap(viewport_list_, other.viewport_list_);
+    return *this;
+  }
+
+  ~BufferViewportList() {
+    if (viewport_list_) {
+      gvr_buffer_viewport_list_destroy(&viewport_list_);
+    }
+  }
+
+  /// For more information, see gvr_get_recommended_buffer_viewports().
+  void SetToRecommendedBufferViewports() {
+    gvr_get_recommended_buffer_viewports(context_, viewport_list_);
+  }
+
+  /// For more information, see gvr_get_screen_buffer_viewports().
+  void SetToScreenBufferViewports() {
+    gvr_get_screen_buffer_viewports(context_, viewport_list_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_list_set_item().
+  void SetBufferViewport(size_t index, const BufferViewport& viewport) {
+    gvr_buffer_viewport_list_set_item(viewport_list_, index,
+                                      viewport.viewport_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_list_get_item().
+  void GetBufferViewport(size_t index, BufferViewport* viewport) const {
+    gvr_buffer_viewport_list_get_item(viewport_list_, index,
+                                      viewport->viewport_);
+  }
+
+  /// For more information, see gvr_buffer_viewport_list_get_size().
+  size_t GetSize() const {
+    return gvr_buffer_viewport_list_get_size(viewport_list_);
+  }
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and takes ownership.
+  BufferViewportList(gvr_buffer_viewport_list* viewport_list,
+                     gvr_context* context)
+      : context_(context),
+        viewport_list_(viewport_list) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_buffer_viewport_list* cobj() { return viewport_list_; }
+  const gvr_buffer_viewport_list* cobj() const { return viewport_list_; }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_buffer_viewport_list* release() {
+    auto result = viewport_list_;
+    viewport_list_ = nullptr;
+    return result;
+  }
+  /// @}
+
+ private:
+  friend class Frame;
+  friend class GvrApi;
+  friend class SwapChain;
+
+  explicit BufferViewportList(gvr_context* context)
+      : context_(context),
+        viewport_list_(gvr_buffer_viewport_list_create(context)) {}
+
+  const gvr_context* context_;
+  gvr_buffer_viewport_list* viewport_list_;
+
+  // Disallow copy and assign.
+  BufferViewportList(const BufferViewportList&) = delete;
+  void operator=(const BufferViewportList&) = delete;
+};
+
+/// Convenience C++ wrapper for gvr_buffer_spec, an opaque pixel buffer
+/// specification. Frees the underlying gvr_buffer_spec on destruction.
+class BufferSpec {
+ public:
+  BufferSpec(BufferSpec&& other)
+      : spec_(nullptr) {
+    std::swap(spec_, other.spec_);
+  }
+
+  BufferSpec& operator=(BufferSpec&& other) {
+    std::swap(spec_, other.spec_);
+    return *this;
+  }
+
+  ~BufferSpec() {
+    if (spec_) gvr_buffer_spec_destroy(&spec_);
+  }
+
+  /// Gets the buffer's size. The default value is the recommended render
+  /// target size. For more information, see gvr_buffer_spec_get_size().
+  Sizei GetSize() const {
+    return gvr_buffer_spec_get_size(spec_);
+  }
+
+  /// Sets the buffer's size. For more information, see
+  /// gvr_buffer_spec_set_size().
+  void SetSize(const Sizei& size) {
+    gvr_buffer_spec_set_size(spec_, size);
+  }
+
+  /// Sets the buffer's size to the passed width and height. For more
+  /// information, see gvr_buffer_spec_set_size().
+  ///
+  /// @param width The width in pixels. Must be greater than 0.
+  /// @param height The height in pixels. Must be greater than 0.
+  void SetSize(int32_t width, int32_t height) {
+    gvr_sizei size{width, height};
+    gvr_buffer_spec_set_size(spec_, size);
+  }
+
+  /// Gets the number of samples per pixel in the buffer. For more
+  /// information, see gvr_buffer_spec_get_samples().
+  int32_t GetSamples() const { return gvr_buffer_spec_get_samples(spec_); }
+
+  /// Sets the number of samples per pixel. For more information, see
+  /// gvr_buffer_spec_set_samples().
+  void SetSamples(int32_t num_samples) {
+    gvr_buffer_spec_set_samples(spec_, num_samples);
+  }
+
+  /// Sets the color format for this buffer. For more information, see
+  /// gvr_buffer_spec_set_color_format().
+  void SetColorFormat(ColorFormat color_format) {
+    gvr_buffer_spec_set_color_format(spec_, color_format);
+  }
+
+  /// Sets the depth and stencil format for this buffer. For more
+  /// information, see gvr_buffer_spec_set_depth_stencil_format().
+  void SetDepthStencilFormat(DepthStencilFormat depth_stencil_format) {
+    gvr_buffer_spec_set_depth_stencil_format(spec_, depth_stencil_format);
+  }
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and takes ownership.
+  explicit BufferSpec(gvr_buffer_spec* spec) : spec_(spec) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_buffer_spec* cobj() { return spec_; }
+  const gvr_buffer_spec* cobj() const { return spec_; }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_buffer_spec* release() {
+    auto result = spec_;
+    spec_ = nullptr;
+    return result;
+  }
+  /// @}
+
+ private:
+  friend class GvrApi;
+  friend class SwapChain;
+
+  explicit BufferSpec(gvr_context* gvr) {
+    spec_ = gvr_buffer_spec_create(gvr);
+  }
+
+  gvr_buffer_spec* spec_;
+};
+
+/// Convenience C++ wrapper for gvr_frame, which represents a single frame
+/// acquired for rendering from the swap chain.
+class Frame {
+ public:
+  Frame(Frame&& other) : frame_(nullptr) {
+    std::swap(frame_, other.frame_);
+  }
+
+  Frame& operator=(Frame&& other) {
+    std::swap(frame_, other.frame_);
+    return *this;
+  }
+
+  ~Frame() {
+    // The swap chain owns the frame, so no clean-up is required.
+  }
+
+  /// For more information, see gvr_frame_get_buffer_size().
+  Sizei GetBufferSize(int32_t index) const {
+    return gvr_frame_get_buffer_size(frame_, index);
+  }
+
+  /// For more information, see gvr_frame_bind_buffer().
+  void BindBuffer(int32_t index) {
+    gvr_frame_bind_buffer(frame_, index);
+  }
+
+  /// For more information, see gvr_frame_unbind().
+  void Unbind() {
+    gvr_frame_unbind(frame_);
+  }
+
+  /// For more information, see gvr_frame_get_framebuffer_object().
+  int32_t GetFramebufferObject(int32_t index) {
+    return gvr_frame_get_framebuffer_object(frame_, index);
+  }
+
+  /// For more information, see gvr_frame_submit().
+  void Submit(const BufferViewportList& viewport_list,
+              const Mat4f& head_space_from_start_space) {
+    gvr_frame_submit(&frame_, viewport_list.viewport_list_,
+                     head_space_from_start_space);
+  }
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and takes ownership.
+  explicit Frame(gvr_frame* frame) : frame_(frame) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_frame* cobj() { return frame_; }
+  const gvr_frame* cobj() const { return frame_; }
+
+  /// Returns whether the wrapped gvr_frame reference is valid.
+  bool is_valid() const { return frame_ != nullptr; }
+  explicit operator bool const() { return is_valid(); }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_frame* release() {
+    auto result = frame_;
+    frame_ = nullptr;
+    return result;
+  }
+  /// @}
+
+ private:
+  friend class SwapChain;
+
+  gvr_frame* frame_;
+};
+
+/// Convenience C++ wrapper for gvr_swap_chain, which represents a queue of
+/// frames. The GvrApi object must outlive any SwapChain objects created from
+/// it.
+class SwapChain {
+ public:
+  SwapChain(SwapChain&& other)
+      : swap_chain_(nullptr) {
+    std::swap(swap_chain_, other.swap_chain_);
+  }
+
+  SwapChain& operator=(SwapChain&& other) {
+    std::swap(swap_chain_, other.swap_chain_);
+    return *this;
+  }
+
+  ~SwapChain() {
+    if (swap_chain_) gvr_swap_chain_destroy(&swap_chain_);
+  }
+
+  /// For more information, see gvr_swap_chain_get_buffer_count().
+  int32_t GetBufferCount() const {
+    return gvr_swap_chain_get_buffer_count(swap_chain_);
+  }
+
+  /// For more information, see gvr_swap_chain_get_buffer_size().
+  Sizei GetBufferSize(int32_t index) const {
+    return gvr_swap_chain_get_buffer_size(swap_chain_, index);
+  }
+
+  /// For more information, see gvr_swap_chain_resize_buffer().
+  void ResizeBuffer(int32_t index, Sizei size) {
+    gvr_swap_chain_resize_buffer(swap_chain_, index, size);
+  }
+
+  /// For more information, see gvr_swap_chain_acquire_frame().
+  /// Note that if Frame acquisition fails, the returned Frame may not be valid.
+  /// The caller should inspect the returned Frame's validity before using,
+  /// and reschedule frame acquisition upon failure.
+  Frame AcquireFrame() {
+    Frame result(gvr_swap_chain_acquire_frame(swap_chain_));
+    return result;
+  }
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and takes ownership.
+  explicit SwapChain(gvr_swap_chain* swap_chain) : swap_chain_(swap_chain) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_swap_chain* cobj() { return swap_chain_; }
+  const gvr_swap_chain* cobj() const { return swap_chain_; }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_swap_chain* release() {
+    auto result = swap_chain_;
+    swap_chain_ = nullptr;
+    return result;
+  }
+  /// @}
+
+ private:
+  friend class GvrApi;
+
+  SwapChain(gvr_context* gvr, const std::vector<BufferSpec>& specs) {
+    std::vector<const gvr_buffer_spec*> c_specs;
+    for (const auto& spec : specs)
+      c_specs.push_back(spec.spec_);
+    swap_chain_ = gvr_swap_chain_create(gvr, c_specs.data(),
+                                        static_cast<int32_t>(c_specs.size()));
+  }
+
+  gvr_swap_chain* swap_chain_;
+
+  // Disallow copy and assign.
+  SwapChain(const SwapChain&);
+  void operator=(const SwapChain&);
+};
+
+/// This is a convenience C++ wrapper for the Google VR C API.
+///
+/// This wrapper strategy prevents ABI compatibility issues between compilers
+/// by ensuring that the interface between client code and the implementation
+/// code in libgvr.so is a pure C interface. The translation from C++ calls
+/// to C calls provided by this wrapper runs entirely in the client's binary
+/// and is compiled by the client's compiler.
+///
+/// Methods in this class are only documented insofar as the C++ wrapping logic
+/// is concerned; for information about the method itself, please refer to the
+/// corresponding function in the C API.
+///
+/// Example API usage:
+///
+///     // Functionality supplied by the application in the example below has
+///     // the "app-" prefix.
+///     #ifdef __ANDROID__
+///     // On Android, the gvr_context should almost always be obtained from the
+///     // Java GvrLayout object via
+///     // GvrLayout.getGvrApi().getNativeGvrContext().
+///     std::unique_ptr<GvrApi> gvr = GvrApi::WrapNonOwned(gvr_context);
+///     #else
+///     std::unique_ptr<GvrApi> gvr = GvrApi::Create();
+///     #endif
+///
+///     gvr->InitializeGl();
+///
+///     gvr::BufferViewportList viewport_list =
+///         gvr->CreateEmptyBufferViewportList();
+///     gvr->GetRecommendedBufferViewports(&viewport_list);
+///     gvr::BufferViewport left_eye_viewport = gvr->CreateBufferViewport();
+///     gvr::BufferViewport right_eye_viewport = gvr->CreateBufferViewport();
+///     viewport_list.Get(0, &left_eye_view);
+///     viewport_list.Get(1, &right_eye_view);
+///
+///     std::vector<gvr::BufferSpec> specs;
+///     specs.push_back(gvr->CreateBufferSpec());
+///     specs[0].SetSamples(app_samples);
+///     gvr::SwapChain swap_chain = gvr->CreateSwapChain(specs);
+///
+///     while (client_app_should_render) {
+///       // A client app should be ready for the render target size to change
+///       // whenever a new QR code is scanned, or a new viewer is paired.
+///       gvr::Sizei render_target_size =
+///           gvr->GetRecommendedRenderTargetSize();
+///       swap_chain.ResizeBuffer(0, render_target_size);
+///       gvr::Frame frame = swap_chain.AcquireFrame();
+///       while (!frame) {
+///         std::this_thread::sleep_for(2ms);
+///         frame = swap_chain.AcquireFrame();
+///       }
+///
+///       // This function will depend on your render loop's implementation.
+///       gvr::ClockTimePoint next_vsync = AppGetNextVsyncTime();
+///
+///       const gvr::Mat4f head_view =
+///           gvr->GetHeadSpaceFromStartSpaceRotation(next_vsync);
+///       const gvr::Mat4f left_eye_view = MatrixMultiply(
+///           gvr->GetEyeFromHeadMatrix(kLeftEye), head_view);
+///       const gvr::Mat4f right_eye_view = MatrixMultiply(
+///           gvr->GetEyeFromHeadMatrix(kRightEye), head_view);
+///
+///       frame.BindBuffer(0);
+///       // App does its rendering to the current framebuffer here.
+///       AppDoSomeRenderingForEye(
+///           left_eye_viewport.GetSourceUv(), left_eye_view);
+///       AppDoSomeRenderingForEye(
+///           right_eye_viewport.GetSourceUv(), right_eye_view);
+///       frame.Unbind();
+///       frame.Submit(viewport_list, head_matrix);
+///     }
+///
+class GvrApi {
+ public:
+#ifdef __ANDROID__
+  /// Instantiates and returns a GvrApi instance that owns a gvr_context.
+  ///
+  /// @param env The JNIEnv associated with the current thread.
+  /// @param app_context The Android application context. This must be the
+  ///     application context, NOT an Activity context (Note: from any Android
+  ///     Activity in your app, you can call getApplicationContext() to
+  ///     retrieve the application context).
+  /// @param class_loader The class loader to use when loading Java classes.
+  ///     This must be your app's main class loader (usually accessible through
+  ///     activity.getClassLoader() on any of your Activities).
+  /// @return unique_ptr to the created GvrApi instance, nullptr on failure.
+  static std::unique_ptr<GvrApi> Create(JNIEnv* env, jobject app_context,
+                                        jobject class_loader) {
+    gvr_context* context = gvr_create(env, app_context, class_loader);
+    if (!context) {
+      return nullptr;
+    }
+    return std::unique_ptr<GvrApi>(new GvrApi(context, true /* owned */));
+  }
+#else
+  /// Instantiates and returns a GvrApi instance that owns a gvr_context.
+  ///
+  /// @return unique_ptr to the created GvrApi instance, nullptr on failure.
+  static std::unique_ptr<GvrApi> Create() {
+    gvr_context* context = gvr_create();
+    if (!context) {
+      return nullptr;
+    }
+    return std::unique_ptr<GvrApi>(new GvrApi(context, true /* owned */));
+  }
+#endif  // #ifdef __ANDROID__
+
+  ~GvrApi() {
+    if (context_ && owned_) {
+      gvr_destroy(&context_);
+    }
+  }
+
+  /// @name Error handling
+  /// @{
+
+  /// For more information, see gvr_get_error().
+  Error GetError() { return static_cast<Error>(gvr_get_error(context_)); }
+
+  /// For more information, see gvr_clear_error().
+  Error ClearError() { return static_cast<Error>(gvr_clear_error(context_)); }
+
+  /// For more information, see gvr_get_error_string().
+  static const char* GetErrorString(Error error_code) {
+    return gvr_get_error_string(error_code);
+  }
+
+  /// For more information, see gvr_get_user_prefs().
+  UserPrefs GetUserPrefs() { return UserPrefs(gvr_get_user_prefs(context_)); }
+
+  /// @}
+
+  /// @name Rendering
+  /// @{
+
+  /// For more information, see gvr_initialize_gl().
+  void InitializeGl() { gvr_initialize_gl(context_); }
+
+  /// For more information, see gvr_get_async_reprojection_enabled().
+  bool GetAsyncReprojectionEnabled() const {
+    return gvr_get_async_reprojection_enabled(context_);
+  }
+
+  /// Constructs a C++ wrapper for a gvr_buffer_viewport object.  For more
+  /// information, see gvr_buffer_viewport_create().
+  ///
+  /// @return A new BufferViewport instance with memory allocated for an
+  ///     underlying gvr_buffer_viewport.
+  BufferViewport CreateBufferViewport() const {
+    return BufferViewport(context_);
+  }
+
+  /// Constructs a C++ wrapper for a gvr_buffer_viewport_list object.
+  /// For more information, see gvr_buffer_viewport_list_create().
+  ///
+  /// @return A new, empty BufferViewportList instance.
+  ///     Note: The validity of the returned object is closely tied to the
+  ///     lifetime of the member gvr_context. The caller is responsible for
+  ///     ensuring correct usage accordingly.
+  BufferViewportList CreateEmptyBufferViewportList() const {
+    return BufferViewportList(context_);
+  }
+
+  /// For more information, see gvr_get_maximum_effective_render_target_size().
+  Sizei GetMaximumEffectiveRenderTargetSize() const {
+    return gvr_get_maximum_effective_render_target_size(context_);
+  }
+
+  /// For more information, see gvr_get_screen_target_size().
+  Sizei GetScreenTargetSize() const {
+    return gvr_get_screen_target_size(context_);
+  }
+
+  /// For more information, see gvr_set_surface_size().
+  void SetSurfaceSize(Sizei surface_size_pixels) {
+    gvr_set_surface_size(context_, surface_size_pixels);
+  }
+
+  /// For more information, see gvr_distort_to_screen().
+  void DistortToScreen(int32_t texture_id,
+                       const BufferViewportList& viewport_list,
+                       const Mat4f& rendered_head_pose_in_start_space_matrix,
+                       const ClockTimePoint& texture_presentation_time) {
+    gvr_distort_to_screen(context_, texture_id, viewport_list.viewport_list_,
+                          rendered_head_pose_in_start_space_matrix,
+                          texture_presentation_time);
+  }
+
+  /// For more information, see gvr_is_feature_supported().
+  bool IsFeatureSupported(int32_t feature) {
+    return gvr_is_feature_supported(context_, feature);
+  }
+
+  /// For more information, see gvr_buffer_spec_create().
+  BufferSpec CreateBufferSpec() {
+    return BufferSpec(context_);
+  }
+
+  /// For more information, see gvr_swap_chain_create().
+  SwapChain CreateSwapChain(const std::vector<BufferSpec>& specs) {
+    return SwapChain(context_, specs);
+  }
+
+  /// For more information, see gvr_bind_default_framebuffer().
+  void BindDefaultFramebuffer() {
+    gvr_bind_default_framebuffer(context_);
+  }
+  /// @}
+
+  /// @name Head tracking
+  /// @{
+
+  /// For more information see gvr_get_head_space_from_start_space_rotation.
+  ///
+  /// @param time_point The time at which to calculate the head pose in start
+  ///     space.
+  /// @return The matrix representation of the rotation from start space
+  ///     (the space with the head pose at the last tracking reset at origin) to
+  ///     head space (the space with the head at origin and axes aligned to the
+  ///     view vector).
+  Mat4f GetHeadSpaceFromStartSpaceRotation(const ClockTimePoint& time_point) {
+    return gvr_get_head_space_from_start_space_rotation(context_, time_point);
+  }
+
+  /// For more information, see gvr_apply_neck_model().
+  Mat4f ApplyNeckModel(const Mat4f& head_pose_in_start_space, float factor) {
+    return gvr_apply_neck_model(context_, head_pose_in_start_space, factor);
+  }
+
+  /// For more information, see gvr_pause_tracking().
+  void PauseTracking() { gvr_pause_tracking(context_); }
+
+  /// For more information, see gvr_resume_tracking().
+  void ResumeTracking() { gvr_resume_tracking(context_); }
+
+  /// For more information, see gvr_reset_tracking().
+  void ResetTracking() { gvr_reset_tracking(context_); }
+
+  // For more information, see gvr_recenter_tracking().
+  void RecenterTracking() { gvr_recenter_tracking(context_); }
+
+  /// For more information, see gvr_get_time_point_now().
+  static ClockTimePoint GetTimePointNow() { return gvr_get_time_point_now(); }
+  /// @}
+
+  /// @name Viewer parameters
+  /// @{
+
+  /// For more information, see gvr_set_default_viewer_profile().
+  bool SetDefaultViewerProfile(const char* viewer_profile_uri) {
+    return gvr_set_default_viewer_profile(context_, viewer_profile_uri);
+  }
+
+  /// For more information, see gvr_refresh_viewer_profile().
+  void RefreshViewerProfile() { gvr_refresh_viewer_profile(context_); }
+
+  /// For more information, see gvr_get_viewer_vendor().
+  const char* GetViewerVendor() const {
+    return gvr_get_viewer_vendor(context_);
+  }
+
+  /// For more information, see gvr_get_viewer_model().
+  const char* GetViewerModel() const { return gvr_get_viewer_model(context_); }
+
+  /// For more information, see gvr_get_viewer_type().
+  ViewerType GetViewerType() const {
+    return static_cast<ViewerType>(gvr_get_viewer_type(context_));
+  }
+
+  /// For more information, see gvr_get_eye_from_head_matrix().
+  Mat4f GetEyeFromHeadMatrix(Eye eye) const {
+    return gvr_get_eye_from_head_matrix(context_, eye);
+  }
+
+  /// For more information, see gvr_get_window_bounds().
+  Recti GetWindowBounds() const { return gvr_get_window_bounds(context_); }
+
+  /// For more information, see gvr_compute_distorted_point().
+  std::array<Vec2f, 3> ComputeDistortedPoint(Eye eye, const Vec2f& uv_in) {
+    std::array<Vec2f, 3> uv_out = {{{}}};
+    gvr_compute_distorted_point(context_, eye, uv_in, uv_out.data());
+    return uv_out;
+  }
+  /// @}
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and optionally takes ownership.
+  ///
+  /// @param context C object to wrap.
+  /// @param owned Whether the wrapper will own the underlying C object.
+  explicit GvrApi(gvr_context* context, bool owned = true)
+      : context_(context), owned_(owned) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_context* cobj() { return context_; }
+  const gvr_context* cobj() const { return context_; }
+
+  /// @deprecated Use cobj() instead.
+  gvr_context* GetContext() { return context_; }
+  /// @deprecated Use cobj() instead.
+  const gvr_context* GetContext() const { return context_; }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_context* release() {
+    auto result = context_;
+    context_ = nullptr;
+    return result;
+  }
+
+  /// Instantiates a GvrApi instance that wraps a *non-owned* gvr_context.
+  ///
+  /// Ownership of the provided `context` remains with the caller, and they
+  /// are responsible for ensuring proper disposal of the context.
+  ///
+  /// @param context Pointer to a non-null, non-owned gvr_context instance.
+  /// @return unique_ptr to the created GvrApi instance. Never null.
+  static std::unique_ptr<GvrApi> WrapNonOwned(gvr_context* context) {
+    return std::unique_ptr<GvrApi>(new GvrApi(context, false /* owned */));
+  }
+  /// @}
+
+ private:
+  gvr_context* context_;
+
+  // Whether context_ is owned by the GvrApi instance. If owned, the context
+  // will be released upon destruction.
+  const bool owned_;
+
+  // Disallow copy and assign.
+  GvrApi(const GvrApi&);
+  void operator=(const GvrApi&);
+};
+
+}  // namespace gvr
+#endif  // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif  // VR_GVR_CAPI_INCLUDE_GVR_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h
new file mode 100644
index 0000000..d34c84d
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_audio.h
@@ -0,0 +1,852 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 VR_GVR_CAPI_INCLUDE_GVR_AUDIO_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_AUDIO_H_
+
+#if __ANDROID__
+#include <jni.h>
+#endif  // __ANDROID__
+
+#include <stdint.h>
+
+#include "vr/gvr/capi/include/gvr.h"
+#include "vr/gvr/capi/include/gvr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+/// @defgroup Audio Spatial Audio API
+/// @brief This is the GVR Audio C API, a spatial audio rendering engine,
+/// optimized for mobile VR.
+///
+/// It allows the user to spatialize sound sources in 3D space, including
+/// distance and elevation cues. Specifically, the API is capable of playing
+/// back spatial sound in three ways:
+///
+/// - **Sound object rendering**: This allows the user to create a virtual sound
+///   source in 3D space. These sources, while spatialized, are fed with mono
+///   audio data.
+///
+/// - **Ambisonic soundfields**: Ambisonic recordings are multi-channel audio
+///   files which are spatialized all around the listener in 360 degrees. These
+///   can be thought of as recorded or pre-baked soundfields. They can be of
+///   great use for background effects which sound perfectly spatial.  Examples
+///   include rain noise, crowd noise or even the sound of the ocean off to one
+///   side.
+///
+/// - **Stereo Sounds**: This allows the user to directly play back
+///   non-spatialized mono or stereo audio files. This is useful for music and
+///   other such audio.
+///
+/// **Initialization**
+///
+///     gvr_audio_context* gvr_audio_create(int32_t rendering_mode);
+///
+/// The rendering_mode argument corresponds to a `gvr_audio_rendering_mode` enum
+/// value, which specifies a rendering configuration setting:
+///
+/// - `GVR_AUDIO_RENDERING_STEREO_PANNING`:
+///   Stereo panning of all sound objects. This disables HRTF-based rendering.
+/// - `GVR_AUDIO_RENDERING_BINAURAL_LOW_QUALITY`:
+///   This renders sound objects over a virtual array of 8 loudspeakers arranged
+///   in a cube configuration around the listener’s head. HRTF-based rendering
+///   is enabled.
+/// - `GVR_AUDIO_RENDERING_BINAURAL_HIGH_QUALITY`:
+///   This renders sound objects over a virtual array of 16 loudspeakers
+///   arranged in an approximate equidistribution about the listener’s
+///   head. HRTF-based rendering is enabled.
+///
+/// For most modern phones, the high quality mode offers a good balance between
+/// performance and audio quality. To optimize the rendering performance for
+/// headphones *and* speaker playback, the stereo speaker mode can be enabled
+/// which automatically switches to stereo panning when no headphone is plugin.
+/// Note that this can lead to varying CPU usage based on headphone and speaker
+/// playback.
+///
+/// **Sound engine control**
+///
+/// Audio playback on the default audio device can be started and stopped by
+/// calling the following two methods:
+///
+///     void gvr_audio_pause(gvr_audio_context* api);
+///     void gvr_audio_resume(gvr_audio_context* api);
+///
+/// Note that:
+///
+///     void gvr_audio_update(gvr_audio_context* api);
+///
+/// must be called from the main thread at a regular rate. It is used to execute
+/// background operations outside of the audio thread.
+///
+/// **Listener position and rotation**
+///
+/// To ensure that the audio in your application reacts to listener head
+/// movement, it is important to update the listener's head orientation in the
+/// graphics callback using the head orientation matrix.
+///
+/// The following methods can be used to control the listener’s head position
+/// and orientation with the audio engine:
+///
+///     void gvr_audio_set_head_position(gvr_audio_context* api, float x,
+///                                      float y, float z);
+/// or
+///
+///     void gvr_audio_set_head_position_gvr(gvr_audio_context* api,
+///                                          const gvr_vec3f& position);
+///
+/// and
+///
+///     void gvr_audio_set_head_rotation(gvr_audio_context* api,
+///                                      float x, float y, float z, float w);
+/// or
+///
+///     void gvr_audio_set_head_rotation_gvr(gvr_audio_context* api,
+///                                          const gvr_quatf& rotation);
+///
+/// **Preloading Sounds**
+///
+/// Both mono sound files for use with Sound Objects and multi-channel Ambisonic
+/// soundfield files can be preloaded into memory before playback or
+/// alternatively streamed during playback. Preloading can be useful to reduce
+/// CPU usage especially if the same audio clip is likely to be played back many
+/// times. In this case playback latency is also reduced.
+///
+/// Sound files can be preloaded into memory by calling:
+///
+///     bool gvr_audio_preload_soundfile(gvr_audio_context* api,
+///                                      const char* filename);
+///
+/// Unused sound files can be unloaded with a call to:
+///
+///     void gvr_audio_unload_soundfile(gvr_audio_context* api,
+///                                     const char* filename);
+///
+/// NOTE: If a sound object, soundfield or stereo sound is created with a file
+/// that has not been preloaded, that audio will be streamed.
+///
+/// **Spatializtion of sound objects**
+///
+/// The GVR Audio System allows the user to create virtual sound objects which
+/// can be placed anywhere in space around the listener.
+///
+/// To create a new sound object, call:
+///
+///     gvr_audio_source_id
+///     gvr_audio_create_sound_object(gvr_audio_context* api,
+///                                   const char* filename);
+///
+/// This returns a handle that can be used to set properties such as the
+/// position and the volume of the sound object via calls to the following two
+/// functions:
+///
+///     void
+///     gvr_audio_set_sound_object_position(gvr_audio_context* api,
+///                                         gvr_audio_source_id sound_object_id,
+///                                         float x, float y, float z);
+///
+///     void
+///     gvr_audio_set_sound_volume(gvr_audio_context* api,
+///                                gvr_audio_source_id source_id, float volume);
+///
+/// The behavior of Sound Objects with respect to their distance from the
+/// listener can be controlled via calls to the following method:
+///
+///     void gvr_audio_set_sound_object_distance_rolloff_model(
+///         gvr_audio_context* api, gvr_audio_source_id sound_object_id,
+///         int32_t rolloff_model, float min_distance, float max_distance);
+///
+/// This enables a user to choose between logarithmic and linear distance
+/// rolloff methods, or to completely disable distance rolloff effects.
+///
+///
+/// The spatialized playback of a sound object can be triggered with a call to:
+///
+///     void gvr_audio_play_sound(gvr_audio_context* api,
+///                               gvr_audio_source_id source_id,
+///                               bool looping_enabled);
+///
+/// and stopped with a call to:
+///
+///     void gvr_audio_stop_sound(gvr_audio_context* api,
+///                               gvr_audio_source_id source_id);
+///
+/// Note that the sound object handle destroys itself at the moment the sound
+/// playback has stopped. This way, no clean up of sound object handles is
+/// needed. On subsequent calls to this function the corresponding
+/// gvr_audio_source_id no longer refers to a valid sound object.
+///
+/// The following function can be used to check if a sound object is currently
+/// active:
+///
+///     bool gvr_audio_is_sound_playing(const gvr_audio_context* api,
+///                                     gvr_audio_source_id source_id);
+///
+/// **Rendering of ambisonic soundfields**
+///
+/// The GVR Audio System also provides the user with the ability to play back
+/// ambisonic soundfields. Ambisonic soundfields are captured or pre-rendered
+/// 360 degree recordings. It is best to think of them as equivalent to 360
+/// degree video. While they envelop and surround the listener, they only react
+/// to the listener's rotational movement. That is, one cannot walk towards
+/// features in the soundfield. Soundfields are ideal for accompanying 360
+/// degree video playback, for introducing background and environmental effects
+/// such as rain or crowd noise, or even for pre baking 3D audio to reduce
+/// rendering costs.  The GVR Audio System supports full 3D First Order
+/// Ambisonic recordings using ACN channel ordering and SN3D normalization. For
+/// more information please see our Spatial Audio specification at:
+/// https://github.com/google/spatial-media/blob/master/docs/spatial-audio-rfc.md#semantics
+///
+/// Note that Soundfield playback is directly streamed from the sound file and
+/// no sound file preloading is needed.
+///
+/// To obtain a soundfield handler, call:
+///
+///     gvr_audio_source_id gvr_audio_create_soundfield(gvr_audio_context* api,
+///                                                    const char* filename);
+///
+/// This returns a gvr_audio_source_id handle that allows the user to begin
+/// playback of the soundfield, to alter the soundfield’s volume or to stop
+/// soundfield playback and as such destroy the object. These actions can be
+/// achieved with calls to the following functions:
+///
+///     void gvr_audio_play_sound(gvr_audio_context* api,
+///                               gvr_audio_source_id source_id,
+///                               bool looping_enabled);
+///
+///     void gvr_audio_set_sound_volume(gvr_audio_context* api,
+///                                     gvr_audio_source_id source_id,
+///                                     float volume);
+///
+///     void gvr_audio_stop_sound(gvr_audio_context* api,
+///                               gvr_audio_source_id source_id);
+///
+/// Ambisonic soundfields can also be rotated about the listener's head in order
+/// to align the components of the soundfield with the visuals of the game/app.
+///
+/// void gvr_audio_set_soundfield_rotation(gvr_audio_context* api,
+///                                        gvr_audio_source_id soundfield_id,
+///                                        const gvr_quatf&
+///                                        soundfield_rotation);
+///
+/// **Direct Playback of Stereo or Mono Sounds**
+///
+/// The GVR Audio System allows the direct non-spatialized playback of both
+/// stereo and mono audio. Such audio is often used for music or sound effects
+/// that should not be spatialized.
+///
+/// A stereo sound can be created with a call to:
+///
+/// gvr_audio_source_id gvr_audio_create_stereo_sound(gvr_audio_context* api,
+///                                                  const char* filename);
+///
+/// **Paused Sounds and Stopped Sounds**
+///
+/// When using sound sources of any of the above types, the user can ensure that
+/// the given source is currently playing before calling.
+///
+/// bool gvr_audio_is_sound_playing(gvr_audio_source_id source_id);
+///
+/// This method will return false if the source has been either paused or
+/// stopped, and true if the source is currently playing.
+///
+/// Once one is finished with a Sound Object and wish to remove it, a call can
+/// be placed to:
+///
+/// void gvr_audio_stop_sound(gvr_audio_source_id source_id);
+///
+/// Once a source has been stopped it is destroyed and the corresponding
+/// gvr_audio_source_id will be invalid. Sources which have been played with the
+/// |looping_enabled| parameter disabled will also be destroyed once playback
+/// of the full audio clip has completed.
+///
+/// To check whether a given gvr_audio_source_id corresponds to a valid source
+/// which exists and is in a playable state, a call can be made to:
+///
+/// bool gvr_audio_is_source_id_valid(gvr_audio_source_id source_id);
+///
+/// By using this pair of methods a user can differentiate between sources which
+/// have been paused and those which have ceased.
+///
+/// **Room effects**
+///
+/// The GVR Audio System provides a powerful reverb engine which can be used to
+/// create customized room effects by specifying the size of a room and a
+/// material for each surface of the room from the gvr_audio_material_name enum.
+/// Each of these surface materials has unique absorption properties which
+/// differ with frequency. The room created will be centered around the
+/// listener. Note that the Google VR Audio System uses meters as the unit of
+/// distance throughout.
+///
+/// The following methods are used to control room effects:
+///
+///     void gvr_audio_enable_room(gvr_audio_context* api, bool enable);
+///
+/// enables or disables room effects with smooth transitions.
+///
+/// and
+///
+///     void
+///     gvr_audio_set_room_properties(gvr_audio_context* api, float size_x,
+///                                   float size_y, float size_z,
+///                                   gvr_audio_material_name wall_material,
+///                                   gvr_audio_material_name ceiling_material,
+///                                   gvr_audio_material_name floor_material);
+///
+/// allows the user to describe the room based on its dimensions and its surface
+/// properties. For example, one can expect very large rooms to be more
+/// reverberant than smaller rooms, and a room with with hard surface materials
+/// such as brick to be more reverberant than one with soft absorbent materials
+/// such as heavy curtains on every surface.
+///
+/// Note that when a sound source is located outside of the listener's room,
+/// it will sound different from sources located within the room due to
+/// attenuation of both the direct sound and the reverb on that source. Sources
+/// located far outside of the listener's room will not be audible to the
+/// listener.
+///
+/// The following method can be used to subtly adjust the reverb in a room by
+/// changing the gain/attenuation on the reverb, setting a multiplier on the
+/// reverberation time to control the reverb's length, or adjusting the balance
+/// between the low and high frequency components of the reverb.
+///
+/// void gvr_audio_set_room_reverb_adjustments(gvr_audio_context* api,
+///                                            float gain,
+///                                            float time_adjust,
+///                                            float brightness_adjust);
+///
+/// If you are writing C++ code, you might prefer to use the C++ wrapper
+/// rather than implement this C API directly.
+///
+/// **Example usage (C++ API)**
+///
+/// Construction:
+///
+///     std::unique_ptr<gvr::AudioApi> gvr_audio_api(new gvr::AudioApi);
+///     gvr_audio_api->Init(GVR_AUDIO_RENDERING_BINAURAL_HIGH_QUALITY);
+///
+/// Update head rotation in DrawFrame():
+///
+///     head_pose_ = gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time);
+///     gvr_audio_api_->SetHeadPose(head_pose_);
+///     gvr_audio_api_->Update();
+///
+/// Preload sound file, create sound handle and start playback:
+///
+///     gvr_audio_api->PreloadSoundfile(kSoundFile);
+///     AudioSourceId source_id =
+///                   gvr_audio_api_->CreateSoundObject("sound.wav");
+///     gvr_audio_api->SetSoundObjectPosition(source_id,
+///                                           position_x,
+///                                           position_y,
+///                                           position_z);
+///     gvr_audio_api->PlaySound(source_id, true /* looped playback */);
+///
+
+/// @{
+
+typedef struct gvr_audio_context_ gvr_audio_context;
+
+/// Creates and initializes a gvr_audio_context. This call also initializes
+/// the audio interface and starts the audio engine. Note that the returned
+/// instance must be deleted with gvr_audio_destroy.
+///
+#ifdef __ANDROID__
+/// @param env The JNI Env associated with the current thread.
+/// @param android_context The Android application context. This must be the
+///     application context, NOT an Activity context (Note: from any Android
+///     Activity in your app, you can call getApplicationContext() to
+///     retrieve the application context).
+/// @param class_loader The class loader to use when loading Java
+///     classes. This must be your app's main class loader (usually
+///     accessible through activity.getClassLoader() on any of your Activities).
+/// @param rendering_mode The gvr_audio_rendering_mode value which determines
+///     the rendering configuration preset. This is passed as an int32_t to
+///     ensure API compatibility.
+/// @return gvr_audio_context instance.
+gvr_audio_context* gvr_audio_create(JNIEnv* env, jobject android_context,
+                                    jobject class_loader,
+                                    int32_t rendering_mode);
+#else
+/// @param rendering_mode The gvr_audio_rendering_mode value which determines
+///     the rendering configuration preset. This is passed as an int32_t to
+///     ensure API compatibility.
+/// @return gvr_audio_context instance.
+gvr_audio_context* gvr_audio_create(int32_t rendering_mode);
+#endif  // #ifdef __ANDROID__
+
+/// Destroys a gvr_audio_context that was previously created with
+/// gvr_audio_create or gvr_audio_create_android.
+///
+/// @param api Pointer to a pointer to a gvr_audio_context. The pointer
+///     will be set to NULL after destruction.
+void gvr_audio_destroy(gvr_audio_context** api);
+
+/// Resumes the VR Audio system.
+/// Call this when your app/game loses focus.
+/// Calling this when not paused is a no-op.
+/// Thread-safe (call from any thread).
+///
+/// @param api Pointer to a gvr_audio_context.
+void gvr_audio_resume(gvr_audio_context* api);
+
+/// Pauses the VR Audio system.
+/// Calling this when already paused is a no-op.
+/// Thread-safe (call from any thread).
+///
+/// @param api Pointer to a gvr_audio_context.
+void gvr_audio_pause(gvr_audio_context* api);
+
+/// This method must be called from the main thread at a regular rate. It is
+/// used to execute background operations outside of the audio thread.
+///
+/// @param api Pointer to a gvr_audio_context.
+void gvr_audio_update(gvr_audio_context* api);
+
+/// Preloads a local sound file. Note that the local file access method
+/// depends on the target platform.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename Name of the file, used as identifier.
+/// @return True on success or if file has already been preloaded.
+bool gvr_audio_preload_soundfile(gvr_audio_context* api, const char* filename);
+
+/// Unloads a previously preloaded sample from memory. Note that if the sample
+/// is currently used, the memory is freed at the moment playback stops.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename Name of the file, used as identifier.
+void gvr_audio_unload_soundfile(gvr_audio_context* api, const char* filename);
+
+/// Returns a new sound object. Note that the sample should only contain a
+/// single audio channel (stereo sources are automatically downmixed to mono).
+/// The handle automatically destroys itself at the moment the sound playback
+/// has stopped.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename The path/name of the file to be played.
+/// @return Id of new sound object. Returns kInvalidId if the sound file has not
+///     been preloaded or if the number of input channels is > 1.
+gvr_audio_source_id gvr_audio_create_sound_object(gvr_audio_context* api,
+                                                  const char* filename);
+
+/// Returns a new ambisonic sound field. Note that the sample needs to be
+/// preloaded and must have 4 separate audio channels. The handle automatically
+/// destroys itself at the moment the sound playback has stopped.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename The path/name of the file to be played.
+/// @return Id of new soundfield. Returns kInvalidId if the sound file has not
+///     been preloaded or if the number of input channels does not match that
+///     required.
+gvr_audio_source_id gvr_audio_create_soundfield(gvr_audio_context* api,
+                                                const char* filename);
+
+/// Returns a new stereo non-spatialized source, which directly plays back mono
+/// or stereo audio. Note the sample needs to be preloaded and may contain only
+/// one (mono) or two (stereo) audio channels.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param filename The path/name of the file to be played..
+/// @return Id of new stereo non-spatialized source. Returns kInvalidId if the
+///     sound file has not been preloaded or if the number of input channels is
+///     > 2;
+gvr_audio_source_id gvr_audio_create_stereo_sound(gvr_audio_context* api,
+                                                  const char* filename);
+
+/// Starts the playback of a sound.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be stopped.
+/// @param looping_enabled Enables looped audio playback.
+void gvr_audio_play_sound(gvr_audio_context* api, gvr_audio_source_id source_id,
+                          bool looping_enabled);
+
+/// Pauses the playback of a sound.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be paused.
+void gvr_audio_pause_sound(gvr_audio_context* api,
+                           gvr_audio_source_id source_id);
+
+/// Resumes the playback of a sound.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be resumed.
+void gvr_audio_resume_sound(gvr_audio_context* api,
+                            gvr_audio_source_id source_id);
+
+/// Stops the playback of a sound and destroys the corresponding sound object
+/// or Soundfield.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be stopped.
+void gvr_audio_stop_sound(gvr_audio_context* api,
+                          gvr_audio_source_id source_id);
+
+/// Checks if a sound is playing.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be checked.
+/// @return True if the sound is being played.
+bool gvr_audio_is_sound_playing(const gvr_audio_context* api,
+                                gvr_audio_source_id source_id);
+
+/// Checks if a |source_id| is valid, and that the corresponding source is in a
+/// playable state. Sources that have been stopped will be reported as invalid.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be checked.
+/// @return True if the source exists and is in a playable state.
+bool gvr_audio_is_source_id_valid(const gvr_audio_context* api,
+                                  gvr_audio_source_id source_id);
+
+/// Repositions an existing sound object.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param sound_object_id Id of the sound object to be moved.
+/// @param x X coordinate the sound will be placed at.
+/// @param y Y coordinate the sound will be placed at.
+/// @param z Z coordinate the sound will be placed at.
+void gvr_audio_set_sound_object_position(gvr_audio_context* api,
+                                         gvr_audio_source_id sound_object_id,
+                                         float x, float y, float z);
+
+/// Sets the given ambisonic soundfields's rotation.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param soundfield_id Id of the soundfield source to be rotated.
+/// @param soundfield_rotation Quaternion representing the soundfield rotation.
+void gvr_audio_set_soundfield_rotation(gvr_audio_context* api,
+                                       gvr_audio_source_id soundfield_id,
+                                       const gvr_quatf& soundfield_rotation);
+
+/// Sets the given sound object source's distance attenuation method with
+/// minimum and maximum distances. Maximum distance must be greater than the
+/// minimum distance for the method to be set.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param sound_object_id Id of sound object source.
+/// @param rolloff_model Linear or logarithmic distance rolloff models. Note
+///     setting the rolloff model to |GVR_AUDIO_ROLLOFF_NONE| will allow
+///     distance attenuation values to be set manually.
+/// @param min_distance Minimum distance to apply distance attenuation method.
+/// @param max_distance Maximum distance to apply distance attenuation method.
+void gvr_audio_set_sound_object_distance_rolloff_model(
+    gvr_audio_context* api, gvr_audio_source_id sound_object_id,
+    int32_t rolloff_model, float min_distance, float max_distance);
+
+/// Changes the master volume.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param volume Volume value. Should range from 0 (mute) to 1 (max).
+void gvr_audio_set_master_volume(gvr_audio_context* api, float volume);
+
+/// Changes the volume of an existing sound.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param source_id Id of the audio source to be modified.
+/// @param volume Volume value. Should range from 0 (mute) to 1 (max).
+void gvr_audio_set_sound_volume(gvr_audio_context* api,
+                                gvr_audio_source_id source_id, float volume);
+
+/// Sets the head pose from a matrix representation of the same.
+///
+/// @param api Pointer to a gvr_audio_context on which to set the pose.
+/// @param head_pose_matrix Matrix representing the head transform to be set.
+void gvr_audio_set_head_pose(gvr_audio_context* api,
+                             const gvr_mat4f& head_pose_matrix);
+
+/// Turns on/off the room reverberation effect.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param enable True to enable room effect.
+void gvr_audio_enable_room(gvr_audio_context* api, bool enable);
+
+/// Sets the room properties describing the dimensions and surface materials of
+/// a given room.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param size_x Dimension along X axis.
+/// @param size_y Dimension along Y axis.
+/// @param size_z Dimension along Z axis.
+/// @param wall_material Surface gvr_audio_material_type for the four walls.
+/// @param ceiling_material Surface gvr_audio_material_type for the ceiling.
+/// @param floor_material Surface gvr_audio_material_type for the floor.
+void gvr_audio_set_room_properties(gvr_audio_context* api, float size_x,
+                                   float size_y, float size_z,
+                                   int32_t wall_material,
+                                   int32_t ceiling_material,
+                                   int32_t floor_material);
+
+/// Adjusts the properties of the current reverb, allowing changes to the
+/// reverb's gain, duration and low/high frequency balance.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param gain Reverb volume (linear) adjustment in range [0, 1] for
+///     attenuation, range [1, inf) for gain boost.
+/// @param time_adjust Reverb time adjustment multiplier to scale the
+///     reverberation tail length. This value should be >= 0.
+/// @param brightness_adjust Reverb brightness adjustment that controls the
+///     reverberation ratio across low and high frequency bands.
+void gvr_audio_set_room_reverb_adjustments(gvr_audio_context* api, float gain,
+                                           float time_adjust,
+                                           float brightness_adjust);
+
+/// Enables the stereo speaker mode. It enforces stereo-panning when headphones
+/// are *not* plugged into the phone. This helps to avoid HRTF-based coloring
+/// effects and reduces computational complexity when speaker playback is
+/// active. By default the stereo speaker mode optimization is disabled.
+///
+/// @param api Pointer to a gvr_audio_context.
+/// @param enable True to enable the stereo speaker mode.
+void gvr_audio_enable_stereo_speaker_mode(gvr_audio_context* api, bool enable);
+
+/// @}
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+// Convenience C++ wrapper.
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#include <memory>
+#include <string>
+
+namespace gvr {
+/// This is a convenience C++ wrapper for the Audio C API.
+///
+/// This wrapper strategy prevents ABI compatibility issues between compilers
+/// by ensuring that the interface between client code and the implementation
+/// code in libgvr.so is a pure C interface. The translation from C++ calls
+/// to C calls provided by this wrapper runs entirely in the client's binary
+/// and is compiled by the client's compiler.
+///
+/// Methods in this class are only documented insofar as the C++ wrapping logic
+/// is concerned; for information about the method itself, please refer to the
+/// corresponding function in the C API.
+///
+///
+/// THREADING: this class is thread-safe and reentrant after initialized
+/// with Init().
+class AudioApi {
+ public:
+  /// Creates an (uninitialized) ControllerApi object. You must initialize
+  /// it by calling Init() before interacting with it.
+  AudioApi() : context_(nullptr) {}
+
+  ~AudioApi() {
+    if (context_) {
+      gvr_audio_destroy(&context_);
+    }
+  }
+
+/// Creates and initializes a gvr_audio_context.
+/// For more information, see gvr_audio_create().
+#ifdef __ANDROID__
+  bool Init(JNIEnv* env, jobject android_context, jobject class_loader,
+            AudioRenderingMode rendering_mode) {
+    context_ =
+        gvr_audio_create(env, android_context, class_loader, rendering_mode);
+    return context_ != nullptr;
+  }
+#else
+  bool Init(AudioRenderingMode rendering_mode) {
+    context_ = gvr_audio_create(rendering_mode);
+    return context_ != nullptr;
+  }
+#endif  // #ifdef __ANDROID__
+
+  /// Pauses the audio engine.
+  /// For more information, see gvr_audio_pause().
+  void Pause() { gvr_audio_pause(context_); }
+
+  /// Resumes the audio engine.
+  /// For more information, see gvr_audio_resume().
+  void Resume() { gvr_audio_resume(context_); }
+
+  /// For more information, see gvr_audio_update().
+  void Update() { gvr_audio_update(context_); }
+
+  /// Preloads a local sound file.
+  /// For more information, see gvr_audio_preload_soundfile().
+  bool PreloadSoundfile(const std::string& filename) {
+    return gvr_audio_preload_soundfile(context_, filename.c_str());
+  }
+
+  /// Unloads a previously preloaded sample from memory.
+  /// For more information, see gvr_audio_preload_soundfile().
+  void UnloadSoundfile(const std::string& filename) {
+    gvr_audio_unload_soundfile(context_, filename.c_str());
+  }
+
+  /// Returns a new sound object.
+  /// For more information, see gvr_audio_create_sound_object().
+  AudioSourceId CreateSoundObject(const std::string& filename) {
+    return gvr_audio_create_sound_object(context_, filename.c_str());
+  }
+
+  /// Returns a new sound field.
+  /// For more information, see gvr_audio_create_soundfield().
+  AudioSourceId CreateSoundfield(const std::string& filename) {
+    return gvr_audio_create_soundfield(context_, filename.c_str());
+  }
+
+  /// Returns a new stereo sound.
+  /// For more information, see gvr_audio_create_stereo_sound().
+  AudioSourceId CreateStereoSound(const std::string& filename) {
+    return gvr_audio_create_stereo_sound(context_, filename.c_str());
+  }
+
+  /// Starts the playback of a sound.
+  /// For more information, see gvr_audio_play_sound().
+  void PlaySound(AudioSourceId source_id, bool looping_enabled) {
+    gvr_audio_play_sound(context_, source_id, looping_enabled);
+  }
+
+  /// Pauses the playback of a sound.
+  /// For more information, see gvr_audio_pause_sound().
+  void PauseSound(AudioSourceId source_id) {
+    gvr_audio_pause_sound(context_, source_id);
+  }
+
+  /// Resumes the playback of a sound.
+  /// For more information, see gvr_audio_resume_sound().
+  void ResumeSound(AudioSourceId source_id) {
+    gvr_audio_resume_sound(context_, source_id);
+  }
+
+  /// Stops the playback of a sound.
+  /// For more information, see gvr_audio_stop_sound().
+  void StopSound(AudioSourceId source_id) {
+    gvr_audio_stop_sound(context_, source_id);
+  }
+
+  /// Checks if a sound is playing.
+  /// For more information, see gvr_audio_is_sound_playing().
+  bool IsSoundPlaying(AudioSourceId source_id) const {
+    return gvr_audio_is_sound_playing(context_, source_id);
+  }
+
+  /// Checks if a source is in a valid playable state.
+  /// For more information, see gvr_audio_is_source_id_valid().
+  bool IsSourceIdValid(AudioSourceId source_id) {
+    return gvr_audio_is_source_id_valid(context_, source_id);
+  }
+
+  /// Repositions an existing sound object.
+  /// For more information, see gvr_audio_set_sound_object_position().
+  void SetSoundObjectPosition(AudioSourceId sound_object_id, float x, float y,
+                              float z) {
+    gvr_audio_set_sound_object_position(context_, sound_object_id, x, y, z);
+  }
+
+  void SetSoundObjectDistanceRolloffModel(
+      AudioSourceId sound_object_id,
+      gvr_audio_distance_rolloff_type rolloff_model, float min_distance,
+      float max_distance) {
+    gvr_audio_set_sound_object_distance_rolloff_model(
+        context_, sound_object_id, rolloff_model, min_distance, max_distance);
+  }
+
+  /// Rotates an existing soundfield.
+  /// For more information, see gvr_audio_set_soundfield_rotation().
+  void SetSoundfieldRotation(AudioSourceId soundfield_id,
+                             const Quatf& soundfield_rotation) {
+    gvr_audio_set_soundfield_rotation(context_, soundfield_id,
+                                      soundfield_rotation);
+  }
+
+  /// Changes the master volume.
+  /// For more information, see gvr_audio_set_master_volume().
+  void SetMasterVolume(float volume) {
+    gvr_audio_set_master_volume(context_, volume);
+  }
+
+  /// Changes the volume of an existing sound.
+  /// For more information, see gvr_audio_set_sound_volume().
+  void SetSoundVolume(AudioSourceId source_id, float volume) {
+    gvr_audio_set_sound_volume(context_, source_id, volume);
+  }
+
+  /// Sets the head position from a matrix representation.
+  /// For more information, see gvr_audio_set_head_pose().
+  void SetHeadPose(const Mat4f& head_pose_matrix) {
+    gvr_audio_set_head_pose(context_, head_pose_matrix);
+  }
+
+  /// Turns on/off the room reverberation effect.
+  /// For more information, see gvr_audio_enable_room().
+  void EnableRoom(bool enable) { gvr_audio_enable_room(context_, enable); }
+
+  /// Sets the room properties describing the dimensions and surface materials
+  /// of a given room. For more information, see
+  /// gvr_audio_set_room_properties().
+  void SetRoomProperties(float size_x, float size_y, float size_z,
+                         gvr_audio_material_type wall_material,
+                         gvr_audio_material_type ceiling_material,
+                         gvr_audio_material_type floor_material) {
+    gvr_audio_set_room_properties(context_, size_x, size_y, size_z,
+                                  wall_material, ceiling_material,
+                                  floor_material);
+  }
+
+  /// Adjusts the properties of the current reverb, allowing changes to the
+  /// reverb's gain, duration and low/high frequency balance. For more
+  /// information see gvr_audio_set_room_reverb_adjustments().
+  void SetRoomReverbAdjustments(float gain, float time_adjust,
+                                float brightness_adjust) {
+    gvr_audio_set_room_reverb_adjustments(context_, gain, time_adjust,
+                                          brightness_adjust);
+  }
+
+  /// Enables the stereo speaker mode. For more information see
+  /// gvr_audio_enable_stereo_speaker_mode().
+  void EnableStereoSpeakerMode(bool enable) {
+    gvr_audio_enable_stereo_speaker_mode(context_, enable);
+  }
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and takes ownership.
+  explicit AudioApi(gvr_audio_context* context) : context_(context) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_audio_context* cobj() { return context_; }
+  const gvr_audio_context* cobj() const { return context_; }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_audio_context* Release() {
+    auto result = context_;
+    context_ = nullptr;
+    return result;
+  }
+  /// @}
+
+ private:
+  gvr_audio_context* context_;
+
+  // Disallow copy and assign:
+  AudioApi(const AudioApi&);
+  void operator=(const AudioApi&);
+};
+
+}  // namespace gvr
+#endif  // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif  // VR_GVR_CAPI_INCLUDE_GVR_AUDIO_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h
new file mode 100644
index 0000000..8347393
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_controller.h
@@ -0,0 +1,793 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 VR_GVR_CAPI_INCLUDE_GVR_CONTROLLER_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_CONTROLLER_H_
+
+#ifdef __ANDROID__
+#include <jni.h>
+#endif
+
+#include <stdint.h>
+
+#include "vr/gvr/capi/include/gvr.h"
+#include "vr/gvr/capi/include/gvr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @defgroup Controller Controller API
+/// @brief This is the Controller C API, which allows access to a VR controller.
+///
+/// If you are writing C++ code, you might prefer to use the C++ wrapper rather
+/// than implement this C API directly.
+///
+/// Typical initialization example:
+///
+///     // Get your gvr_context* pointer from GvrLayout:
+///     gvr_context* gvr = ......;  // (get from GvrLayout in Java)
+///
+///     // Set up the API features:
+///     int32_t options = gvr_controller_get_default_options();
+///
+///     // Enable non-default options, if needed:
+///     options |= GVR_CONTROLLER_ENABLE_GYRO | GVR_CONTROLLER_ENABLE_ACCEL;
+///
+///     // Create and init:
+///     gvr_controller_context* context =
+///         gvr_controller_create_and_init(options, gvr);
+///
+///     // Check if init was successful.
+///     if (!context) {
+///       // Handle error.
+///       return;
+///     }
+///
+///     gvr_controller_state* state = gvr_controller_state_create();
+///
+///     // Resume:
+///     gvr_controller_resume(api);
+///
+/// Usage:
+///
+///     void DrawFrame() {
+///       gvr_controller_state_update(context, 0, state);
+///       // ... process controller state ...
+///     }
+///
+///     // When your application gets paused:
+///     void OnPause() {
+///       gvr_controller_pause(context);
+///     }
+///
+///     // When your application gets resumed:
+///     void OnResume() {
+///       gvr_controller_resume(context);
+///     }
+///
+/// To conserve battery, be sure to call gvr_controller_pause and
+/// gvr_controller_resume when your app gets paused and resumed, respectively.
+///
+/// THREADING: unless otherwise noted, all functions are thread-safe, so
+/// you can operate on the same gvr_controller_context object from multiple
+/// threads.
+/// @{
+
+/// Represents a Daydream Controller API object, used to invoke the
+/// Daydream Controller API.
+typedef struct gvr_controller_context_ gvr_controller_context;
+
+/// Returns the default features for the controller API.
+///
+/// @return The set of default features, as bit flags (an OR'ed combination of
+///     the GVR_CONTROLLER_ENABLE_* feature flags).
+int32_t gvr_controller_get_default_options();
+
+/// Creates and initializes a gvr_controller_context instance which can be used
+/// to invoke the Daydream Controller API functions. Important: after creation
+/// the API will be in the paused state (the controller will be inactive).
+/// You must call gvr_controller_resume() explicitly (typically, in your Android
+/// app's onResume() callback).
+///
+/// @param options The API options. To get the defaults, use
+///     gvr_controller_get_default_options().
+/// @param context The GVR Context object to sync with (optional).
+///     This can be nullptr. If provided, the context's state will
+///     be synchronized with the controller's state where possible. For
+///     example, when the user recenters the controller, this will
+///     automatically recenter head tracking as well.
+///     WARNING: the caller is responsible for making sure the pointer
+///     remains valid for the lifetime of this object.
+/// @return A pointer to the initialized API, or NULL if an error occurs.
+gvr_controller_context* gvr_controller_create_and_init(
+    int32_t options, gvr_context* context);
+
+#ifdef __ANDROID__
+/// Creates and initializes a gvr_controller_context instance with an explicit
+/// Android context and class loader.
+///
+/// @param env The JNI Env associated with the current thread.
+/// @param android_context The Android application context. This must be the
+///     application context, NOT an Activity context (Note: from any Android
+///     Activity in your app, you can call getApplicationContext() to
+///     retrieve the application context).
+/// @param class_loader The class loader to use when loading Java
+///     classes. This must be your app's main class loader (usually
+///     accessible through activity.getClassLoader() on any of your Activities).
+/// @param options The API options. To get the defaults, use
+///     gvr_controller_get_default_options().
+/// @param context The GVR Context object to sync with (optional).
+///     This can be nullptr. If provided, the context's state will
+///     be synchronized with the controller's state where possible. For
+///     example, when the user recenters the controller, this will
+///     automatically recenter head tracking as well.
+///     WARNING: the caller is responsible for making sure the pointer
+///     remains valid for the lifetime of this object.
+/// @return A pointer to the initialized API, or NULL if an error occurs.
+gvr_controller_context* gvr_controller_create_and_init_android(
+    JNIEnv *env, jobject android_context, jobject class_loader,
+    int32_t options, gvr_context* context);
+#endif  // #ifdef __ANDROID__
+
+/// Destroys a gvr_controller_context that was previously created with
+/// gvr_controller_init.
+///
+/// @param api Pointer to a pointer to a gvr_controller_context. The pointer
+///     will be set to NULL after destruction.
+void gvr_controller_destroy(gvr_controller_context** api);
+
+/// Pauses the controller, possibly releasing resources.
+/// Call this when your app/game loses focus.
+/// Calling this when already paused is a no-op.
+/// Thread-safe (call from any thread).
+///
+/// @param api Pointer to a pointer to a gvr_controller_context.
+void gvr_controller_pause(gvr_controller_context* api);
+
+/// Resumes the controller. Call this when your app/game regains focus.
+/// Calling this when already resumed is a no-op.
+/// Thread-safe (call from any thread).
+///
+/// @param api Pointer to a pointer to a gvr_controller_context.
+void gvr_controller_resume(gvr_controller_context* api);
+
+/// Convenience to convert an API status code to string. The returned pointer
+/// is static and valid throughout the lifetime of the application.
+///
+/// @param status The gvr_controller_api_status to convert to string.
+/// @return A pointer to a string that describes the value.
+const char* gvr_controller_api_status_to_string(int32_t status);
+
+/// Convenience to convert an connection state to string. The returned pointer
+/// is static and valid throughout the lifetime of the application.
+///
+/// @param state The state to convert to string.
+/// @return A pointer to a string that describes the value.
+const char* gvr_controller_connection_state_to_string(int32_t state);
+
+/// Convenience to convert an connection state to string. The returned pointer
+/// is static and valid throughout the lifetime of the application.
+///
+/// @param button The gvr_controller_button to convert to string.
+/// @return A pointer to a string that describes the value.
+const char* gvr_controller_button_to_string(int32_t button);
+
+/// Creates a gvr_controller_state.
+gvr_controller_state* gvr_controller_state_create();
+
+/// Destroys a a gvr_controller_state that was previously created with
+/// gvr_controller_state_create.
+void gvr_controller_state_destroy(gvr_controller_state** state);
+
+/// Updates the controller state. Reading the controller state is not a
+/// const getter: it has side-effects. In particular, some of the
+/// gvr_controller_state fields (the ones documented as "transient") represent
+/// one-time events and will be true for only one read operation, and false
+/// in subsequente reads.
+///
+/// @param api Pointer to a pointer to a gvr_controller_context.
+/// @param flags Optional flags reserved for future use. A value of 0 should be
+///     used until corresponding flag attributes are defined and documented.
+/// @param out_state A pointer where the controller's state
+///     is to be written. This must have been allocated with
+///     gvr_controller_state_create().
+void gvr_controller_state_update(gvr_controller_context* api, int32_t flags,
+                                 gvr_controller_state* out_state);
+
+/// Gets the API status of the controller state. Returns one of the
+/// gvr_controller_api_status variants, but returned as an int32_t for ABI
+/// compatibility.
+int32_t gvr_controller_state_get_api_status(const gvr_controller_state* state);
+
+/// Gets the connection state of the controller. Returns one of the
+/// gvr_controller_connection_state variants, but returned as an int32_t for ABI
+/// compatibility.
+int32_t gvr_controller_state_get_connection_state(
+    const gvr_controller_state* state);
+
+/// Returns the current controller orientation, in Start Space. The Start Space
+/// is the same space as the headset space and has these three axes
+/// (right-handed):
+///
+/// * The positive X axis points to the right.
+/// * The positive Y axis points upwards.
+/// * The positive Z axis points backwards.
+///
+/// The definition of "backwards" and "to the right" are based on the position
+/// of the controller when tracking started. For Daydream, this is when the
+/// controller was first connected in the "Connect your Controller" screen
+/// which is shown when the user enters VR.
+///
+/// The definition of "upwards" is given by gravity (away from the pull of
+/// gravity). This API may not work in environments without gravity, such
+/// as space stations or near the center of the Earth.
+///
+/// Since the coordinate system is right-handed, rotations are given by the
+/// right-hand rule. For example, rotating the controller counter-clockwise
+/// on a table top as seen from above means a positive rotation about the
+/// Y axis, while clockwise would mean negative.
+///
+/// Note that this is the Start Space for the *controller*, which initially
+/// coincides with the Start Space for the headset, but they may diverge over
+/// time due to controller/headset drift. A recentering operation will bring
+/// the two spaces back into sync.
+///
+/// Remember that a quaternion expresses a rotation. Given a rotation of theta
+/// radians about the (x, y, z) axis, the corresponding quaternion (in
+/// xyzw order) is:
+///
+///     (x * sin(theta/2), y * sin(theta/2), z * sin(theta/2), cos(theta/2))
+///
+/// Here are some examples of orientations of the controller and their
+/// corresponding quaternions, all given in xyzw order:
+///
+///   * Initial pose, pointing forward and lying flat on a surface: identity
+///     quaternion (0, 0, 0, 1). Corresponds to "no rotation".
+///
+///   * Flat on table, rotated 90 degrees counter-clockwise: (0, 0.7071, 0,
+///     0.7071). Corresponds to a +90 degree rotation about the Y axis.
+///
+///   * Flat on table, rotated 90 degrees clockwise: (0, -0.7071, 0, 0.7071).
+///     Corresponds to a -90 degree rotation about the Y axis.
+///
+///   * Flat on table, rotated 180 degrees (pointing backwards): (0, 1, 0, 0).
+///     Corresponds to a 180 degree rotation about the Y axis.
+///
+///   * Pointing straight up towards the sky: (0.7071, 0, 0, 0.7071).
+///     Corresponds to a +90 degree rotation about the X axis.
+///
+///   * Pointing straight down towards the ground: (-0.7071, 0, 0, 0.7071).
+///     Corresponds to a -90 degree rotation about the X axis.
+///
+///   * Banked 90 degrees to the left: (0, 0, 0.7071, 0.7071). Corresponds
+///     to a +90 degree rotation about the Z axis.
+///
+///   * Banked 90 degrees to the right: (0, 0, -0.7071, 0.7071). Corresponds
+///     to a -90 degree rotation about the Z axis.
+gvr_quatf gvr_controller_state_get_orientation(
+    const gvr_controller_state* state);
+
+/// Returns the current controller gyro reading, in Start Space.
+///
+/// The gyro measures the controller's angular speed in radians per second.
+/// Note that this is an angular *speed*, so it reflects how fast the
+/// controller's orientation is changing with time.
+/// In particular, if the controller is not being rotated, the angular speed
+/// will be zero on all axes, regardless of the current pose.
+///
+/// The axes are in the controller's device space. Specifically:
+///
+///    * The X axis points to the right of the controller.
+///    * The Y axis points upwards perpendicular to the top surface of the
+///      controller.
+///    * The Z axis points backwards along the body of the controller,
+///      towards its rear, where the charging port is.
+///
+/// As usual in a right-handed coordinate system, the sign of the angular
+/// velocity is given by the right-hand rule. So, for example:
+///
+///    * If the controller is flat on a table top spinning counter-clockwise
+///      as seen from above, you will read a positive angular velocity
+///      about the Y axis. Clockwise would be negative.
+///    * If the controller is initially pointing forward and lying flat and
+///      is then gradually angled up so that its tip points towards the sky,
+///      it will report a positive angular velocity about the X axis during
+///      that motion. Likewise, angling it down will report a negative angular
+///      velocity about the X axis.
+///    * If the controller is banked (rolled) to the right, this will
+///      report a negative angular velocity about the Z axis during the
+///      motion (remember the Z axis points backwards along the controller).
+///      Banking to the left will report a positive angular velocity about
+///      the Z axis.
+gvr_vec3f gvr_controller_state_get_gyro(const gvr_controller_state* state);
+
+/// Current (latest) controller accelerometer reading, in Start Space.
+///
+/// The accelerometer indicates the direction in which the controller feels
+/// an acceleration, including gravity. The reading is given in meters
+/// per second squared (m/s^2). The axes are the same as for the gyro.
+/// To have an intuition for the signs used in the accelerometer, it is useful
+/// to imagine that, when at rest, the controller is being "pushed" by a
+/// force opposite to gravity. It is as if, by the equivalency princle, it were
+/// on a frame of reference that is accelerating in the opposite direction to
+/// gravity. For example:
+///
+///   * If the controller is lying flat on a table top, it will read a positive
+///     acceleration of about 9.8 m/s^2 along the Y axis, corresponding to
+///     the acceleration of gravity (as if the table were pushing the controller
+///     upwards at 9.8 m/s^2 to counteract gravity).
+///   * If, in that situation, the controller is now accelerated upwards at
+///     3.0 m/s^2, then the reading will be 12.8 m/s^2 along the Y axis,
+///     since the controller will now feel a stronger acceleration corresponding
+///     to the 9.8 m/s^2 plus the upwards push of 3.0 m/s^2.
+///   * If, the controller is accelerated downwards at 5.0 m/s^2, then the
+///     reading will now be 4.8 m/s^2 along the Y axis, since the controller
+///     will now feel a weaker acceleration (as the acceleration is giving in
+///     to gravity).
+///   * If you were to give in to gravity completely, letting the controller
+///     free fall towards the ground, it will read 0 on all axes, as there
+///     will be no force acting on the controller. (Please do not put your
+///     controller in a free-fall situation. This is just a theoretical
+///     example.)
+gvr_vec3f gvr_controller_state_get_accel(const gvr_controller_state* state);
+
+/// Returns whether the user is touching the touchpad.
+bool gvr_controller_state_is_touching(const gvr_controller_state* state);
+
+/// If the user is touching the touchpad, this returns the touch position in
+/// normalized coordinates, where (0,0) is the top-left of the touchpad
+/// and (1,1) is the bottom right. If the user is not touching the touchpad,
+/// then this is the position of the last touch.
+gvr_vec2f gvr_controller_state_get_touch_pos(const gvr_controller_state* state);
+
+/// Returns true if user just started touching touchpad (this is a transient
+/// event:
+/// it is true for only one frame after the event).
+bool gvr_controller_state_get_touch_down(const gvr_controller_state* state);
+
+/// Returns true if user just stopped touching touchpad (this is a transient
+/// event:
+/// it is true for only one frame after the event).
+bool gvr_controller_state_get_touch_up(const gvr_controller_state* state);
+
+/// Returns true if a recenter operation just ended (this is a transient event:
+/// it is true only for one frame after the recenter ended). If this is
+/// true then the `orientation` field is already relative to the new center.
+bool gvr_controller_state_get_recentered(const gvr_controller_state* state);
+
+/// Returns whether the recenter flow is currently in progress.
+///
+/// @deprecated Use gvr_controller_state_get_recentered instead.
+bool gvr_controller_state_get_recentering(const gvr_controller_state* state);
+
+/// Returns whether the given button is currently pressed.
+bool gvr_controller_state_get_button_state(const gvr_controller_state* state,
+
+                                           int32_t button);
+
+/// Returns whether the given button was just pressed (transient).
+bool gvr_controller_state_get_button_down(const gvr_controller_state* state,
+                                          int32_t button);
+
+/// Returns whether the given button was just released (transient).
+bool gvr_controller_state_get_button_up(const gvr_controller_state* state,
+                                        int32_t button);
+
+/// Returns the timestamp (nanos) when the last orientation event was received.
+int64_t gvr_controller_state_get_last_orientation_timestamp(
+    const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last gyro event was received.
+int64_t gvr_controller_state_get_last_gyro_timestamp(
+    const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last accelerometer event was
+/// received.
+int64_t gvr_controller_state_get_last_accel_timestamp(
+    const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last touch event was received.
+int64_t gvr_controller_state_get_last_touch_timestamp(
+    const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last button event was received.
+int64_t gvr_controller_state_get_last_button_timestamp(
+    const gvr_controller_state* state);
+
+// Current (latest) controller simulated position for use with an elbow model.
+gvr_vec3f gvr_controller_state_get_position(const gvr_controller_state* state);
+
+// Returns the timestamp (nanos) when the last position event was received.
+int64_t gvr_controller_state_get_last_position_timestamp(
+    const gvr_controller_state* state);
+
+/// Returns whether the controller battery is currently charging.
+/// This may not be real time information and may be slow to be updated.
+bool gvr_controller_state_get_battery_charging(
+    const gvr_controller_state* state);
+
+/// Returns the bucketed controller battery level.
+/// Note this is a gvr_controller_battery_level and not a percent.
+int32_t gvr_controller_state_get_battery_level(
+    const gvr_controller_state* state);
+
+/// Returns the timestamp (nanos) when the last battery event was received.
+int64_t gvr_controller_state_get_last_battery_timestamp(
+    const gvr_controller_state* state);
+
+/// Convenience to convert a battery level to string. The returned pointer
+/// is static and valid throughout the lifetime of the application.
+///
+/// @param level The gvr_controller_battery_level to convert to string.
+/// @return A pointer to a string that describes the value.
+const char* gvr_controller_battery_level_to_string(int32_t level);
+
+/// @}
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+
+// Convenience C++ wrapper.
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#include <memory>
+
+namespace gvr {
+/// This is a convenience C++ wrapper for the Controller C API.
+///
+/// This wrapper strategy prevents ABI compatibility issues between compilers
+/// by ensuring that the interface between client code and the implementation
+/// code in libgvr.so is a pure C interface. The translation from C++ calls
+/// to C calls provided by this wrapper runs entirely in the client's binary
+/// and is compiled by the client's compiler.
+///
+/// Methods in this class are only documented insofar as the C++ wrapping logic
+/// is concerned; for information about the method itself, please refer to the
+/// corresponding function in the C API.
+///
+/// Typical C++ initialization example:
+///
+///     std::unique_ptr<ControllerApi> controller_api(new ControllerApi);
+///
+///     // Your GVR context pointer (which can be obtained from GvrLayout)
+///     gvr_context* context = .....;  // (get it from GvrLayout)
+///
+///     // Set up the options:
+///     int32_t options = ControllerApi::DefaultOptions();
+///
+///     // Enable non-default options, if you need them:
+///     options |= GVR_CONTROLLER_ENABLE_GYRO;
+///
+///     // Init the ControllerApi object:
+///     bool success = controller_api->Init(options, context);
+///     if (!success) {
+///       // Handle failure.
+///       // Do NOT call other methods (like Resume, etc) if init failed.
+///       return;
+///     }
+///
+///     // Resume the ControllerApi (if your app is on the foreground).
+///     controller_api->Resume();
+///
+///     ControllerState state;
+///
+/// Usage example:
+///
+///     void DrawFrame() {
+///       state.Update(*controller_api);
+///       // ... process controller state ...
+///     }
+///
+///     // When your application gets paused:
+///     void OnPause() {
+///       controller_api->Pause();
+///     }
+///
+///     // When your application gets resumed:
+///     void OnResume() {
+///       controller_api->Resume();
+///     }
+///
+/// To conserve battery, be sure to call Pause() and Resume() when your app
+/// gets paused and resumed, respectively. This will allow the underlying
+/// logic to unbind from the VR service and let the controller sleep when
+/// no apps are using it.
+///
+/// THREADING: this class is thread-safe and reentrant after initialized
+/// with Init().
+class ControllerApi {
+ public:
+  /// Creates an (uninitialized) ControllerApi object. You must initialize
+  /// it by calling Init() before interacting with it.
+  ControllerApi() : context_(nullptr) {}
+
+  /// Returns the default controller options.
+  static int32_t DefaultOptions() {
+    return gvr_controller_get_default_options();
+  }
+
+  // Deprecated factory-style create method.
+  // TODO(btco): remove this once no one is using it.
+  static std::unique_ptr<ControllerApi> Create() {
+    return std::unique_ptr<ControllerApi>(new ControllerApi);
+  }
+
+  /// Initializes the controller API.
+  ///
+  /// This method must be called exactly once in the lifetime of this object.
+  /// Other methods in this class may only be called after Init() returns true.
+  /// Note: this does not cause the ControllerApi to be resumed. You must call
+  /// Resume() explicitly in order to start using the controller.
+  ///
+  /// For more information see gvr_controller_create_and_init().
+  ///
+  /// @return True if initialization was successful, false if it failed.
+  ///     Initialization may fail, for example, because invalid options were
+  ///     supplied.
+  bool Init(int32_t options, gvr_context* context) {
+    context_ = gvr_controller_create_and_init(options, context);
+    return context_ != nullptr;
+  }
+
+#ifdef __ANDROID__
+  /// Overload of Init() with explicit Android context and class loader
+  /// (for Android only). For more information, see:
+  /// gvr_controller_create_and_init_android().
+  bool Init(JNIEnv *env, jobject android_context, jobject class_loader,
+            int32_t options, gvr_context* context) {
+    context_ = gvr_controller_create_and_init_android(
+        env, android_context, class_loader, options, context);
+    return context_ != nullptr;
+  }
+#endif  // #ifdef __ANDROID__
+
+  /// Convenience overload that calls Init without a gvr_context.
+  // TODO(btco): remove once it is no longer being used.
+  bool Init(int32_t options) {
+    return Init(options, nullptr);
+  }
+
+  /// Pauses the controller.
+  /// For more information, see gvr_controller_pause().
+  void Pause() {
+    gvr_controller_pause(context_);
+  }
+
+  /// Resumes the controller.
+  /// For more information, see gvr_controller_resume().
+  void Resume() {
+    gvr_controller_resume(context_);
+  }
+
+  /// Destroys this ControllerApi instance.
+  ~ControllerApi() {
+    if (context_) gvr_controller_destroy(&context_);
+  }
+
+  /// Convenience functions to convert enums to strings.
+  /// For more information, see the corresponding functions in the C API.
+  static const char* ToString(ControllerApiStatus status) {
+    return gvr_controller_api_status_to_string(status);
+  }
+
+  static const char* ToString(ControllerConnectionState state) {
+    return gvr_controller_connection_state_to_string(state);
+  }
+
+  static const char* ToString(ControllerButton button) {
+    return gvr_controller_button_to_string(button);
+  }
+
+  static const char* ToString(ControllerBatteryLevel level) {
+    return gvr_controller_battery_level_to_string(level);
+  }
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and takes ownership.
+  explicit ControllerApi(gvr_controller_context* context)
+      : context_(context) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_controller_context* cobj() { return context_; }
+  const gvr_controller_context* cobj() const { return context_; }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_controller_context* release() {
+    auto result = context_;
+    context_ = nullptr;
+    return result;
+  }
+  /// @}
+
+ protected:
+  gvr_controller_context* context_;
+
+ private:
+  friend class ControllerState;
+
+  // Disallow copy and assign:
+  ControllerApi(const ControllerApi&);
+  void operator=(const ControllerApi&);
+};
+
+/// Convenience C++ wrapper for the opaque gvr_controller_state type. See the
+/// gvr_controller_state functions for more information.
+class ControllerState {
+ public:
+  ControllerState() : state_(gvr_controller_state_create()) {}
+
+  ~ControllerState() {
+    if (state_) gvr_controller_state_destroy(&state_);
+  }
+
+  /// For more information, see gvr_controller_state_update().
+  void Update(const ControllerApi& api) {
+    gvr_controller_state_update(api.context_, 0, state_);
+  }
+
+  /// For more information, see gvr_controller_state_update().
+  void Update(const ControllerApi& api, int32_t flags) {
+    gvr_controller_state_update(api.context_, flags, state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_api_status().
+  ControllerApiStatus GetApiStatus() const {
+    return static_cast<ControllerApiStatus>(
+        gvr_controller_state_get_api_status(state_));
+  }
+
+  /// For more information, see gvr_controller_state_get_connection_state().
+  ControllerConnectionState GetConnectionState() const {
+    return static_cast<ControllerConnectionState>(
+        gvr_controller_state_get_connection_state(state_));
+  }
+
+  /// For more information, see gvr_controller_state_get_orientation().
+  gvr_quatf GetOrientation() const {
+    return gvr_controller_state_get_orientation(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_gyro().
+  gvr_vec3f GetGyro() const { return gvr_controller_state_get_gyro(state_); }
+
+  /// For more information, see gvr_controller_state_get_accel().
+  gvr_vec3f GetAccel() const { return gvr_controller_state_get_accel(state_); }
+
+  /// For more information, see gvr_controller_state_is_touching().
+  bool IsTouching() const { return gvr_controller_state_is_touching(state_); }
+
+  /// For more information, see gvr_controller_state_get_touch_pos().
+  gvr_vec2f GetTouchPos() const {
+    return gvr_controller_state_get_touch_pos(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_touch_down().
+  bool GetTouchDown() const {
+    return gvr_controller_state_get_touch_down(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_touch_up().
+  bool GetTouchUp() const { return gvr_controller_state_get_touch_up(state_); }
+
+  /// For more information, see gvr_controller_state_get_recentered().
+  bool GetRecentered() const {
+    return gvr_controller_state_get_recentered(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_recentering().
+  bool GetRecentering() const {
+    return gvr_controller_state_get_recentering(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_button_state().
+  bool GetButtonState(ControllerButton button) const {
+    return gvr_controller_state_get_button_state(state_, button);
+  }
+
+  /// For more information, see gvr_controller_state_get_button_down().
+  bool GetButtonDown(ControllerButton button) const {
+    return gvr_controller_state_get_button_down(state_, button);
+  }
+
+  /// For more information, see gvr_controller_state_get_button_up().
+  bool GetButtonUp(ControllerButton button) const {
+    return gvr_controller_state_get_button_up(state_, button);
+  }
+
+  /// For more information, see
+  /// gvr_controller_state_get_last_orientation_timestamp().
+  int64_t GetLastOrientationTimestamp() const {
+    return gvr_controller_state_get_last_orientation_timestamp(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_last_gyro_timestamp().
+  int64_t GetLastGyroTimestamp() const {
+    return gvr_controller_state_get_last_gyro_timestamp(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_last_accel_timestamp().
+  int64_t GetLastAccelTimestamp() const {
+    return gvr_controller_state_get_last_accel_timestamp(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_last_touch_timestamp().
+  int64_t GetLastTouchTimestamp() const {
+    return gvr_controller_state_get_last_touch_timestamp(state_);
+  }
+
+  /// For more information, see
+  /// gvr_controller_state_get_last_button_timestamp().
+  int64_t GetLastButtonTimestamp() const {
+    return gvr_controller_state_get_last_button_timestamp(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_position().
+  gvr_vec3f GetPosition() const {
+    return gvr_controller_state_get_position(state_);
+  }
+
+  /// For more information, see
+  /// gvr_controller_state_get_last_position_timestamp().
+  int64_t GetLastPositionTimestamp() const {
+    return gvr_controller_state_get_last_position_timestamp(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_battery_charging
+  bool GetBatteryCharging() const {
+    return gvr_controller_state_get_battery_charging(state_);
+  }
+
+  /// For more information, see gvr_controller_state_get_battery_level
+  ControllerBatteryLevel GetBatteryLevel() const {
+    return static_cast<ControllerBatteryLevel>(
+        gvr_controller_state_get_battery_level(state_));
+  }
+
+  /// For more information, see gvr_controller_state_get_last_battery_timestamp
+  int64_t GetLastBatteryTimestamp() const {
+    return gvr_controller_state_get_last_battery_timestamp(state_);
+  }
+
+  /// @name Wrapper manipulation
+  /// @{
+  /// Creates a C++ wrapper for a C object and takes ownership.
+  explicit ControllerState(gvr_controller_state* state) : state_(state) {}
+
+  /// Returns the wrapped C object. Does not affect ownership.
+  gvr_controller_state* cobj() { return state_; }
+  const gvr_controller_state* cobj() const { return state_; }
+
+  /// Returns the wrapped C object and transfers its ownership to the caller.
+  /// The wrapper becomes invalid and should not be used.
+  gvr_controller_state* release() {
+    auto result = state_;
+    state_ = nullptr;
+    return result;
+  }
+  /// @}
+
+ private:
+  gvr_controller_state* state_;
+};
+
+}  // namespace gvr
+#endif  // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif  // VR_GVR_CAPI_INCLUDE_GVR_CONTROLLER_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h
new file mode 100644
index 0000000..cf81b51
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/include/gvr_types.h
@@ -0,0 +1,658 @@
+/* Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 VR_GVR_CAPI_INCLUDE_GVR_TYPES_H_
+#define VR_GVR_CAPI_INCLUDE_GVR_TYPES_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// @defgroup types Google VR Types
+/// @brief Various types used in the Google VR NDK.
+/// @{
+
+/// Primary context for invoking Google VR APIs.
+typedef struct gvr_context_ gvr_context;
+
+/// An enum for the left and right eye.
+typedef enum {
+  GVR_LEFT_EYE = 0,
+  GVR_RIGHT_EYE,
+  GVR_NUM_EYES
+} gvr_eye;
+
+/// The type of VR viewer.
+typedef enum {
+  /// A Cardboard-compatible viewer. A typical Cardboard viewer supports a
+  /// simple touchscreen-based trigger input mechanism. On most platforms, this
+  // is the default viewer type if no viewer has been explicitly paired.
+  GVR_VIEWER_TYPE_CARDBOARD = 0,
+  /// A Daydream-compatible viewer. A typical Daydream viewer supports 3DOF
+  /// controller input (as defined in gvr_controller.h), and is intended only
+  /// for Daydream-ready platforms. It does *not* support touchscreen-based
+  /// input unless Cardboard emulation is explicitly enabled.
+  GVR_VIEWER_TYPE_DAYDREAM = 1,
+} gvr_viewer_type;
+
+// Types of VR-specific features which may or may not be supported on the
+// underlying platform.
+typedef enum {
+  // Asynchronous reprojection warps the app's rendered frame using the most
+  // recent head pose just before pushing the frame to the display.
+  GVR_FEATURE_ASYNC_REPROJECTION = 0,
+} gvr_feature;
+
+/// @}
+
+/// Version information for the Google VR API.
+typedef struct gvr_version_ {
+  int32_t major;
+  int32_t minor;
+  int32_t patch;
+} gvr_version;
+
+/// An integral 2D size. Used for render target sizes.
+typedef struct gvr_sizei {
+  int32_t width;
+  int32_t height;
+} gvr_sizei;
+
+/// An integral 2D rect. Used for window bounds in pixels.
+typedef struct gvr_recti {
+  int32_t left;
+  int32_t right;
+  int32_t bottom;
+  int32_t top;
+} gvr_recti;
+
+/// A floating point 2D rect. Used for field of view, and also for ranges
+/// in texture space. When used for a field of view, all angles are in positive
+/// degrees from the optical axis.
+typedef struct gvr_rectf {
+  float left;
+  float right;
+  float bottom;
+  float top;
+} gvr_rectf;
+
+/// A floating point 2D vector.
+typedef struct gvr_vec2f {
+  float x;
+  float y;
+} gvr_vec2f;
+
+/// A floating point 3D vector.
+typedef struct gvr_vec3f {
+  float x;
+  float y;
+  float z;
+} gvr_vec3f;
+
+/// A floating point 4x4 matrix.
+typedef struct gvr_mat4f { float m[4][4]; } gvr_mat4f;
+
+/// A floating point quaternion, in JPL format.
+/// We use this simple struct in order not to impose a dependency on a
+/// particular math library. The user of this API is free to encapsulate this
+/// into any math library they want.
+typedef struct gvr_quatf {
+  /// qx, qy, qz are the vector component.
+  float qx;
+  float qy;
+  float qz;
+  /// qw is the linelar component.
+  float qw;
+} gvr_quatf;
+
+/// A *monotonic system time* representation. On Android, this is equivalent to
+/// System.nanoTime(), or clock_gettime(CLOCK_MONOTONIC). If there is any doubt
+/// about how to get the current time for the current platform, simply use
+/// gvr_get_time_point_now().
+typedef struct gvr_clock_time_point {
+  int64_t monotonic_system_time_nanos;
+} gvr_clock_time_point;
+
+/// A structure that ties together a region of a buffer, the field of view
+/// rendered into that region and a target eye index to define part of the
+/// user's field of view. The SDK implementation uses this information to
+/// transform the images generated by the app output into the final display that
+/// is sent to the screen.
+///
+/// A set of these structures will most often be generated by the API, via
+/// gvr_get_recommended_buffer_viewports() or
+/// gvr_get_screen_buffer_viewports(). However, the client may also customize
+/// these values via gvr_buffer_viewport_list_set(), constructing a custom
+/// gvr_buffer_viewport_list for use in the distortion pass.
+typedef struct gvr_buffer_viewport_ gvr_buffer_viewport;
+
+/// List of buffer viewports that completely specifies how to transform the
+/// frame's buffers into the image displayed on the screen.
+typedef struct gvr_buffer_viewport_list_ gvr_buffer_viewport_list;
+
+/// Specification of a pixel buffer. A pixel buffer can have color, depth and
+/// stencil attachments and mostly corresponds to the OpenGL concept of a
+/// framebuffer object. However, since there can be multiple such objects for
+/// each frame, we avoid calling them "framebuffers". Pixel buffers which are
+/// part of the currently acquired frame are immutable, i.e., they cannot be
+/// resized or otherwise reconfigured.
+typedef struct gvr_buffer_spec_ gvr_buffer_spec;
+
+/// Swap chain that contains some number of frames. Frames in the swap chain
+/// can be unused, in the process of being distorted and presented on the
+/// screen, or acquired and being rendered to by the application. The swap chain
+/// ensures that the most recent available frame is always shown and that the
+/// application never has to wait to render the next frame.
+typedef struct gvr_swap_chain_ gvr_swap_chain;
+
+/// A single frame acquired from the swap chain. Each frame is composed of one
+/// or more buffers, which are then lens distorted and composited into the final
+/// output. Buffers are identified by indices that correspond to the position
+/// of their gvr_buffer_spec in the list passed when constructing the swap
+/// chain.
+typedef struct gvr_frame_ gvr_frame;
+
+/// @addtogroup types
+/// @{
+
+/// Constants that represent GVR error codes.
+typedef enum {
+  GVR_ERROR_NONE = 0,
+  GVR_ERROR_CONTROLLER_CREATE_FAILED = 2,
+  GVR_ERROR_NO_FRAME_AVAILABLE = 3,
+} gvr_error;
+
+/// Controller API options (bit flags).
+enum {
+  /// Indicates that controller orientation data should be reported.
+  GVR_CONTROLLER_ENABLE_ORIENTATION = 1 << 0,
+  /// Indicates that controller touchpad data should be reported.
+  GVR_CONTROLLER_ENABLE_TOUCH = 1 << 1,
+  /// Indicates that controller gyroscope data should be reported.
+  GVR_CONTROLLER_ENABLE_GYRO = 1 << 2,
+  /// Indicates that controller accelerometer data should be reported.
+  GVR_CONTROLLER_ENABLE_ACCEL = 1 << 3,
+  /// Indicates that controller gestures should be reported.
+  GVR_CONTROLLER_ENABLE_GESTURES = 1 << 4,
+  /// Indicates that controller pose prediction should be enabled.
+  GVR_CONTROLLER_ENABLE_POSE_PREDICTION = 1 << 5,
+  /// Indicates that controller position data should be reported.
+  GVR_CONTROLLER_ENABLE_POSITION = 1 << 6,
+  /// Indicates that controller battery data should be reported.
+  GVR_CONTROLLER_ENABLE_BATTERY = 1 << 7,
+};
+
+/// Constants that represent the status of the controller API.
+typedef enum {
+  /// API is happy and healthy. This doesn't mean the controller itself
+  /// is connected, it just means that the underlying service is working
+  /// properly.
+  GVR_CONTROLLER_API_OK = 0,
+
+  /// Any other status represents a permanent failure that requires
+  /// external action to fix:
+
+  /// API failed because this device does not support controllers (API is too
+  /// low, or other required feature not present).
+  GVR_CONTROLLER_API_UNSUPPORTED = 1,
+  /// This app was not authorized to use the service (e.g., missing permissions,
+  /// the app is blacklisted by the underlying service, etc).
+  GVR_CONTROLLER_API_NOT_AUTHORIZED = 2,
+  /// The underlying VR service is not present.
+  GVR_CONTROLLER_API_UNAVAILABLE = 3,
+  /// The underlying VR service is too old, needs upgrade.
+  GVR_CONTROLLER_API_SERVICE_OBSOLETE = 4,
+  /// The underlying VR service is too new, is incompatible with current client.
+  GVR_CONTROLLER_API_CLIENT_OBSOLETE = 5,
+  /// The underlying VR service is malfunctioning. Try again later.
+  GVR_CONTROLLER_API_MALFUNCTION = 6,
+} gvr_controller_api_status;
+
+/// Constants that represent the state of the controller.
+typedef enum {
+  /// Controller is disconnected.
+  GVR_CONTROLLER_DISCONNECTED = 0,
+  /// Controller is scanning.
+  GVR_CONTROLLER_SCANNING = 1,
+  /// Controller is connecting.
+  GVR_CONTROLLER_CONNECTING = 2,
+  /// Controller is connected.
+  GVR_CONTROLLER_CONNECTED = 3,
+} gvr_controller_connection_state;
+
+/// Controller buttons.
+typedef enum {
+  GVR_CONTROLLER_BUTTON_NONE = 0,
+  GVR_CONTROLLER_BUTTON_CLICK = 1,  ///< Touchpad Click.
+  GVR_CONTROLLER_BUTTON_HOME = 2,
+  GVR_CONTROLLER_BUTTON_APP = 3,
+  GVR_CONTROLLER_BUTTON_VOLUME_UP = 4,
+  GVR_CONTROLLER_BUTTON_VOLUME_DOWN = 5,
+
+  /// Note: there are 5 buttons on the controller, but the state arrays have
+  /// this many elements due to the inclusion of a dummy "none" button.
+  GVR_CONTROLLER_BUTTON_COUNT = 6,
+} gvr_controller_button;
+
+/// Controller battery states.
+typedef enum {
+  GVR_CONTROLLER_BATTERY_LEVEL_UNKNOWN = 0,
+  GVR_CONTROLLER_BATTERY_LEVEL_CRITICAL_LOW = 1,
+  GVR_CONTROLLER_BATTERY_LEVEL_LOW = 2,
+  GVR_CONTROLLER_BATTERY_LEVEL_MEDIUM = 3,
+  GVR_CONTROLLER_BATTERY_LEVEL_ALMOST_FULL = 4,
+  GVR_CONTROLLER_BATTERY_LEVEL_FULL = 5,
+
+  /// Note: there are 5 distinct levels, but there are 6 due to the inclusion
+  /// of an UNKNOWN state before any battery information is collected, etc.
+  GVR_CONTROLLER_BATTERY_LEVEL_COUNT = 6,
+} gvr_controller_battery_level;
+
+
+/// @}
+
+/// Opaque handle to controller state.
+typedef struct gvr_controller_state_ gvr_controller_state;
+
+/// @addtogroup types
+/// @{
+
+/// Rendering modes define CPU load / rendering quality balances.
+typedef enum {
+  /// Stereo panning of all Sound Objects. This disables HRTF-based rendering.
+  GVR_AUDIO_RENDERING_STEREO_PANNING = 0,
+  /// HRTF-based rendering over a virtual array of 8 loudspeakers arranged in
+  /// a cube configuration around the listener’s head.
+  GVR_AUDIO_RENDERING_BINAURAL_LOW_QUALITY = 1,
+  /// HRTF-based rendering over a virtual array of 16 loudspeakers arranged in
+  /// an approximate equidistribution about the around the listener’s head.
+  GVR_AUDIO_RENDERING_BINAURAL_HIGH_QUALITY = 2,
+} gvr_audio_rendering_mode;
+
+/// Room surface material names, used to set room properties.
+typedef enum {
+  /// Acoustically transparent material, reflects no sound.
+  GVR_AUDIO_MATERIAL_TRANSPARENT = 0,
+  /// Acoustic ceiling tiles, absorbs most frequencies.
+  GVR_AUDIO_MATERIAL_ACOUSTIC_CEILING_TILES = 1,
+  /// Bare brick, relatively reflective.
+  GVR_AUDIO_MATERIAL_BRICK_BARE = 2,
+  /// Painted brick
+  GVR_AUDIO_MATERIAL_BRICK_PAINTED = 3,
+  /// Coarse surface concrete block.
+  GVR_AUDIO_MATERIAL_CONCRETE_BLOCK_COARSE = 4,
+  /// Painted concrete block.
+  GVR_AUDIO_MATERIAL_CONCRETE_BLOCK_PAINTED = 5,
+  /// Heavy curtains.
+  GVR_AUDIO_MATERIAL_CURTAIN_HEAVY = 6,
+  /// Fiber glass insulation.
+  GVR_AUDIO_MATERIAL_FIBER_GLASS_INSULATION = 7,
+  /// Thin glass.
+  GVR_AUDIO_MATERIAL_GLASS_THIN = 8,
+  /// Thick glass.
+  GVR_AUDIO_MATERIAL_GLASS_THICK = 9,
+  /// Grass.
+  GVR_AUDIO_MATERIAL_GRASS = 10,
+  /// Linoleum on concrete.
+  GVR_AUDIO_MATERIAL_LINOLEUM_ON_CONCRETE = 11,
+  /// Marble.
+  GVR_AUDIO_MATERIAL_MARBLE = 12,
+  /// Galvanized sheet metal.
+  GVR_AUDIO_MATERIAL_METAL = 13,
+  /// Wooden parquet on concrete.
+  GVR_AUDIO_MATERIAL_PARQUET_ON_CONCRETE = 14,
+  /// Rough plaster surface.
+  GVR_AUDIO_MATERIAL_PLASTER_ROUGH = 15,
+  /// Smooth plaster surface.
+  GVR_AUDIO_MATERIAL_PLASTER_SMOOTH = 16,
+  /// Plywood panel.
+  GVR_AUDIO_MATERIAL_PLYWOOD_PANEL = 17,
+  /// Polished concrete OR tile surface.
+  GVR_AUDIO_MATERIAL_POLISHED_CONCRETE_OR_TILE = 18,
+  /// Sheet rock.
+  GVR_AUDIO_MATERIAL_SHEET_ROCK = 19,
+  /// Surface of water or ice.
+  GVR_AUDIO_MATERIAL_WATER_OR_ICE_SURFACE = 20,
+  /// Wooden ceiling.
+  GVR_AUDIO_MATERIAL_WOOD_CEILING = 21,
+  /// Wood paneling.
+  GVR_AUDIO_MATERIAL_WOOD_PANEL = 22,
+} gvr_audio_material_type;
+
+/// Distance rolloff models used for distance attenuation.
+typedef enum {
+  /// Logarithmic distance rolloff model.
+  GVR_AUDIO_ROLLOFF_LOGARITHMIC = 0,
+  /// Linear distance rolloff model.
+  GVR_AUDIO_ROLLOFF_LINEAR = 1,
+  /// No distance attenuation will be applied.
+  GVR_AUDIO_ROLLOFF_NONE = 2,
+} gvr_audio_distance_rolloff_type;
+
+/// Sound object and sound field identifier.
+typedef int32_t gvr_audio_source_id;
+
+/// Supported surround sound formats.
+typedef enum {
+  // Enables to initialize a yet undefined rendering mode.
+  GVR_AUDIO_SURROUND_FORMAT_INVALID = 0,
+
+  // Virtual stereo speakers at -30 degrees and +30 degrees.
+  GVR_AUDIO_SURROUND_FORMAT_SURROUND_STEREO = 1,
+
+  // 5.1 surround sound according to the ITU-R BS 775 speaker configuration
+  // recommendation:
+  //   - Front left (FL) at 30 degrees.
+  //   - Front right (FR) at -30 degrees.
+  //   - Front center (FC) at 0 degrees.
+  //   - Low frequency effects (LFE) at front center at 0 degrees.
+  //   - Left side (LS) at 110 degrees.
+  //   - Right side (RS) at -110 degrees.
+  //
+  // The 5.1 channel input layout must matches AAC: FL, FR, FC, LFE, LS, RS.
+  // Note that this differs from the Vorbis/Opus 5.1 channel layout, which
+  // is: FL, FC, FR, LS, RS, LFE.
+  GVR_AUDIO_SURROUND_FORMAT_SURROUND_FIVE_DOT_ONE = 2,
+
+  // First-order ambisonics (AmbiX format: 4 channels, ACN channel ordering,
+  // SN3D normalization).
+  GVR_AUDIO_SURROUND_FORMAT_FIRST_ORDER_AMBISONICS = 3,
+
+  // Second-order ambisonics (AmbiX format: 9 channels, ACN channel ordering,
+  // SN3D normalization).
+  GVR_AUDIO_SURROUND_FORMAT_SECOND_ORDER_AMBISONICS = 4,
+
+  // Third-order ambisonics (AmbiX format: 16 channels, ACN channel ordering,
+  // SN3D normalization).
+  GVR_AUDIO_SURROUND_FORMAT_THIRD_ORDER_AMBISONICS = 5,
+} gvr_audio_surround_format_type;
+
+/// Valid color formats for swap chain buffers.
+typedef enum {
+  /// Equivalent to GL_RGBA8
+  GVR_COLOR_FORMAT_RGBA_8888 = 0,
+  /// Equivalent to GL_RGB565
+  GVR_COLOR_FORMAT_RGB_565 = 1,
+} gvr_color_format_type;
+
+typedef enum {
+  /// No depth or stencil buffer.
+  GVR_DEPTH_STENCIL_FORMAT_NONE = 255,
+  /// Equivalent to GL_DEPTH_COMPONENT16.
+  GVR_DEPTH_STENCIL_FORMAT_DEPTH_16 = 0,
+  /// Equivalent to GL_DEPTH_COMPONENT24.
+  GVR_DEPTH_STENCIL_FORMAT_DEPTH_24 = 1,
+  /// Equivlaent to GL_DEPTH24_STENCIL8.
+  GVR_DEPTH_STENCIL_FORMAT_DEPTH_24_STENCIL_8 = 2,
+  /// Equivalent to GL_DEPTH_COMPONENT32F.
+  GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F = 3,
+  /// Equivalent to GL_DEPTH_32F_STENCIL8.
+  GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F_STENCIL_8 = 4,
+  /// Equivalent to GL_STENCIL8.
+  GVR_DEPTH_STENCIL_FORMAT_STENCIL_8 = 5,
+} gvr_depth_stencil_format_type;
+
+/// Types of asynchronous reprojection.
+typedef enum {
+  /// Do not reproject.
+  GVR_REPROJECTION_NONE = 0,
+  /// Reproject in all dimensions.
+  GVR_REPROJECTION_FULL = 1,
+} gvr_reprojection;
+
+typedef enum {
+  GVR_CONTROLLER_RIGHT_HANDED = 0,
+  GVR_CONTROLLER_LEFT_HANDED = 1,
+} gvr_controller_handedness;
+
+typedef struct gvr_user_prefs_ gvr_user_prefs;
+
+// Anonymous enum for miscellaneous integer constants.
+enum {
+  /// Constant that represents a nonexistent external surface. Pass to
+  /// gvr_buffer_viewport_set_external_surface_id() to disable sampling from
+  /// an external surface.
+  GVR_EXTERNAL_SURFACE_ID_NONE = -1,
+  /// Special index for a source buffer that has the same contents as the
+  /// external surface attached to the given viewport. Pass this to
+  /// gvr_buffer_viewport_set_source_buffer_index() to use the external surface
+  /// as the buffer contents.
+  GVR_BUFFER_INDEX_EXTERNAL_SURFACE = -1,
+};
+
+/// @}
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+// These typedefs convert the C-style names to C++-style names.
+
+namespace gvr {
+
+const int32_t kControllerEnableOrientation =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_ORIENTATION);
+const int32_t kControllerEnableTouch =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_TOUCH);
+const int32_t kControllerEnableGyro =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_GYRO);
+const int32_t kControllerEnableAccel =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_ACCEL);
+const int32_t kControllerEnableGestures =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_GESTURES);
+const int32_t kControllerEnablePosePrediction =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_POSE_PREDICTION);
+const int32_t kControllerEnablePosition =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_POSITION);
+const int32_t kControllerEnableBattery =
+    static_cast<int32_t>(GVR_CONTROLLER_ENABLE_BATTERY);
+
+typedef gvr_controller_api_status ControllerApiStatus;
+const ControllerApiStatus kControllerApiOk =
+    static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_OK);
+const ControllerApiStatus kControllerApiUnsupported =
+    static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_UNSUPPORTED);
+const ControllerApiStatus kControllerApiNotAuthorized =
+    static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_NOT_AUTHORIZED);
+const ControllerApiStatus kControllerApiUnavailable =
+    static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_UNAVAILABLE);
+const ControllerApiStatus kControllerApiServiceObsolete =
+    static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_SERVICE_OBSOLETE);
+const ControllerApiStatus kControllerApiClientObsolete =
+    static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_CLIENT_OBSOLETE);
+const ControllerApiStatus kControllerApiMalfunction =
+    static_cast<ControllerApiStatus>(GVR_CONTROLLER_API_MALFUNCTION);
+
+typedef gvr_controller_connection_state ControllerConnectionState;
+const ControllerConnectionState kControllerDisconnected =
+    static_cast<ControllerConnectionState>(GVR_CONTROLLER_DISCONNECTED);
+const ControllerConnectionState kControllerScanning =
+    static_cast<ControllerConnectionState>(GVR_CONTROLLER_SCANNING);
+const ControllerConnectionState kControllerConnecting =
+    static_cast<ControllerConnectionState>(GVR_CONTROLLER_CONNECTING);
+const ControllerConnectionState kControllerConnected =
+    static_cast<ControllerConnectionState>(GVR_CONTROLLER_CONNECTED);
+
+typedef gvr_controller_button ControllerButton;
+const ControllerButton kControllerButtonNone =
+    static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_NONE);
+const ControllerButton kControllerButtonClick =
+    static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_CLICK);
+const ControllerButton kControllerButtonHome =
+    static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_HOME);
+const ControllerButton kControllerButtonApp =
+    static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_APP);
+const ControllerButton kControllerButtonVolumeUp =
+    static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_VOLUME_UP);
+const ControllerButton kControllerButtonVolumeDown =
+    static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_VOLUME_DOWN);
+const ControllerButton kControllerButtonCount =
+    static_cast<ControllerButton>(GVR_CONTROLLER_BUTTON_COUNT);
+
+typedef gvr_controller_battery_level ControllerBatteryLevel;
+const ControllerBatteryLevel kControllerBatteryLevelUnknown =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_UNKNOWN);
+const ControllerBatteryLevel kControllerBatteryLevelCriticalLow =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_CRITICAL_LOW);
+const ControllerBatteryLevel kControllerBatteryLevelLow =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_LOW);
+const ControllerBatteryLevel kControllerBatteryLevelMedium =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_MEDIUM);
+const ControllerBatteryLevel kControllerBatteryLevelAlmostFull =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_ALMOST_FULL);
+const ControllerBatteryLevel kControllerBatteryLevelFull =
+    static_cast<ControllerBatteryLevel>(
+        GVR_CONTROLLER_BATTERY_LEVEL_FULL);
+
+/// An uninitialized external surface ID.
+const int32_t kUninitializedExternalSurface = GVR_BUFFER_INDEX_EXTERNAL_SURFACE;
+/// The default source buffer index for viewports.
+const int32_t kDefaultBufferIndex = 0;
+
+typedef gvr_eye Eye;
+
+typedef gvr_viewer_type ViewerType;
+const ViewerType kViewerTypeCardboard =
+    static_cast<ViewerType>(GVR_VIEWER_TYPE_CARDBOARD);
+const ViewerType kViewerTypeDaydream =
+    static_cast<ViewerType>(GVR_VIEWER_TYPE_DAYDREAM);
+
+typedef gvr_version Version;
+typedef gvr_sizei Sizei;
+typedef gvr_recti Recti;
+typedef gvr_rectf Rectf;
+typedef gvr_vec2f Vec2f;
+typedef gvr_vec3f Vec3f;
+typedef gvr_mat4f Mat4f;
+typedef gvr_quatf Quatf;
+typedef gvr_clock_time_point ClockTimePoint;
+
+typedef gvr_vec2f ControllerVec2;
+typedef gvr_vec3f ControllerVec3;
+typedef gvr_quatf ControllerQuat;
+
+typedef gvr_audio_rendering_mode AudioRenderingMode;
+typedef gvr_audio_material_type AudioMaterialName;
+typedef gvr_audio_distance_rolloff_type AudioRolloffMethod;
+typedef gvr_audio_source_id AudioSourceId;
+typedef gvr_audio_surround_format_type AudioSurroundFormat;
+
+typedef gvr_color_format_type ColorFormat;
+const ColorFormat kColorFormatRgba8888 =
+    static_cast<ColorFormat>(GVR_COLOR_FORMAT_RGBA_8888);
+const ColorFormat kColorFormatRgb565 =
+    static_cast<ColorFormat>(GVR_COLOR_FORMAT_RGB_565);
+
+typedef gvr_depth_stencil_format_type DepthStencilFormat;
+const DepthStencilFormat kDepthStencilFormatNone =
+    static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_NONE);
+const DepthStencilFormat kDepthStencilFormatDepth16 =
+    static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_DEPTH_16);
+const DepthStencilFormat kDepthStencilFormatDepth24 =
+    static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_DEPTH_24);
+const DepthStencilFormat kDepthStencilFormatDepth24Stencil8 =
+    static_cast<DepthStencilFormat>(
+        GVR_DEPTH_STENCIL_FORMAT_DEPTH_24_STENCIL_8);
+const DepthStencilFormat kDepthStencilFormatDepth32f =
+    static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F);
+const DepthStencilFormat kDepthStencilFormatDepth32fStencil8 =
+    static_cast<DepthStencilFormat>(
+        GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F_STENCIL_8);
+const DepthStencilFormat kDepthStencilFormatStencil8 =
+    static_cast<DepthStencilFormat>(GVR_DEPTH_STENCIL_FORMAT_STENCIL_8);
+
+typedef gvr_controller_handedness ControllerHandedness;
+const ControllerHandedness kControllerRightHanded =
+    static_cast<ControllerHandedness>(GVR_CONTROLLER_RIGHT_HANDED);
+const ControllerHandedness kControllerLeftHanded =
+    static_cast<ControllerHandedness>(GVR_CONTROLLER_LEFT_HANDED);
+
+typedef gvr_error Error;
+const Error kErrorNone = static_cast<Error>(GVR_ERROR_NONE);
+const Error kErrorControllerCreateFailed =
+    static_cast<Error>(GVR_ERROR_CONTROLLER_CREATE_FAILED);
+const Error kErrorNoFrameAvailable =
+    static_cast<Error>(GVR_ERROR_NO_FRAME_AVAILABLE);
+
+class AudioApi;
+class BufferSpec;
+class ControllerApi;
+class ControllerState;
+class Frame;
+class GvrApi;
+class BufferViewport;
+class BufferViewportList;
+class SwapChain;
+class UserPrefs;
+
+}  // namespace gvr
+
+// Non-member equality operators for convenience. Since typedefs do not trigger
+// argument-dependent lookup, these operators have to be defined for the
+// underlying types.
+inline bool operator==(const gvr_vec2f& lhs, const gvr_vec2f& rhs) {
+  return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+inline bool operator!=(const gvr_vec2f& lhs, const gvr_vec2f& rhs) {
+  return !(lhs == rhs);
+}
+
+inline bool operator==(const gvr_vec3f& lhs, const gvr_vec3f& rhs) {
+  return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+inline bool operator!=(const gvr_vec3f& lhs, const gvr_vec3f& rhs) {
+  return !(lhs == rhs);
+}
+
+inline bool operator==(const gvr_recti& lhs, const gvr_recti& rhs) {
+  return lhs.left == rhs.left && lhs.right == rhs.right &&
+         lhs.bottom == rhs.bottom && lhs.top == rhs.top;
+}
+
+inline bool operator!=(const gvr_recti& lhs, const gvr_recti& rhs) {
+  return !(lhs == rhs);
+}
+
+inline bool operator==(const gvr_rectf& lhs, const gvr_rectf& rhs) {
+  return lhs.left == rhs.left && lhs.right == rhs.right &&
+         lhs.bottom == rhs.bottom && lhs.top == rhs.top;
+}
+
+inline bool operator!=(const gvr_rectf& lhs, const gvr_rectf& rhs) {
+  return !(lhs == rhs);
+}
+
+inline bool operator==(const gvr_sizei& lhs, const gvr_sizei& rhs) {
+  return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const gvr_sizei& lhs, const gvr_sizei& rhs) {
+  return !(lhs == rhs);
+}
+
+#endif  // #if defined(__cplusplus) && !defined(GVR_NO_CPP_WRAPPER)
+
+#endif  // VR_GVR_CAPI_INCLUDE_GVR_TYPES_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h
new file mode 100644
index 0000000..5bd6174
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_experimental.h
@@ -0,0 +1,231 @@
+#ifndef VR_GVR_CAPI_SRC_GVR_EXPERIMENTAL_H_
+#define VR_GVR_CAPI_SRC_GVR_EXPERIMENTAL_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "vr/gvr/capi/include/gvr_types.h"
+#include "vr/gvr/capi/src/gvr_types_experimental.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// NOTE: APIs added to this file are *not* part of the core GVR library, and
+// should only be used for prototyping or experimental projects. The idea is
+// that APIs added here can be used for testing and development, graduating to
+// the core API (gvr.h or gvr_private.h) after we're ready to commit to them
+// indefinitely.
+
+// ************************************************************************** //
+// *                     DaydreamOS experimental APIs                       * //
+// ************************************************************************** //
+
+/// Gets the position and rotation from start space to head space.  The head
+/// space is a space where the head is at the origin and faces the -Z direction.
+///
+/// @param gvr Pointer to the gvr instance from which to get the pose.
+/// @param time The time at which to get the head pose. The time should be in
+///     the future. If the time is not in the future, it will be clamped to now.
+/// @return A matrix representation of the position and rotation from start
+//      space (the space where the head was last reset) to head space (the space
+///     with the head at the origin, and the axes aligned to the view vector).
+gvr_mat4f gvr_get_head_space_from_start_space_pose(
+    gvr_context* gvr, const gvr_clock_time_point time);
+
+/// Sets the compositor z-order of the swap chain.
+///
+/// @param swap_chain the swap chain to change.
+/// @param z_order Z order, higher values are displayed on top of lower ones,
+///     the default value is 0.
+void gvr_swap_chain_set_z_order(const gvr_swap_chain* swap_chain, int z_order);
+
+/// Creates a gvr_external_surface instance.
+/// An external surface is mainly used to pass external content (such as video
+/// frames, pre-rendered 2D Android UI) into distortion pass for compositing.
+/// The method gvr_external_surface_get_surface can be used to bridge Android
+/// components with GVR distortion pass via a traditional Android Surface
+/// instance.
+///
+/// @param gvr Pointer to the gvr instance from which to create the external
+///     surface.
+/// @return Pointer to an allocated gvr_external_surface object. The caller
+//      is responsible for calling gvr_external_surface_destroy() on the
+///     returned object when it is no longer needed.
+gvr_external_surface* gvr_external_surface_create(gvr_context* gvr);
+
+/// Frees a gvr_external_surface instance and clears the pointer.
+/// Note that once a gvr_external_surface is destroyed, the Java Surface object
+/// returned from gvr_external_surface_get_surface remains to be accessible and
+/// functioning. It's up to Java's garbage collection to release all resources
+/// behind the Java Surface object.
+///
+/// @param surface Pointer to a pointer to the gvr_external_surface instance to
+///     be destroyed and nulled.
+void gvr_external_surface_destroy(gvr_external_surface** surface);
+
+/// Get an Android Surface as a Java object from the gvr_external_surface. This
+/// API is mainly used by standalone display service (aka when
+/// gvr_using_vr_display_service returns true) to access an Android Surface.
+///
+/// @param surface The gvr_external_surface associated with the Android Surface.
+///     Note that this API has to be called within a JNIEnv and is using the
+///     JNIEnv passed in during gvr_create.
+/// @return A jobject that is an instance of the 'android/view/Surface' Java
+///     class, NULL on failure. Note that the return value is really an opaque
+///     handle to a Java object and the life cycle of that object is maintained
+///     by Java (i.e. it will get garbage collected eventually). Thus, there is
+///     no need for an explicit destroy call.
+void* gvr_external_surface_get_surface(const gvr_external_surface* surface);
+
+/// Get the Surface ID associated with the gvr_external_surface. Note that the
+/// returned ID is used for internal bookkeeping only and should not be used
+/// by the app itself for lookups.
+/// @param surface The gvr_external_surface to query the ID for.
+/// @return The external surface ID associated with the gvr_external_surface.
+int32_t gvr_external_surface_get_surface_id(
+    const gvr_external_surface* surface);
+
+/// Sets the z order of the layer to be created.
+/// Note that this API is a short-term workaround for SysUI work and is never
+/// meant to graduate as is to either gvr.h or gvr_private.h. The proper
+/// solution is tracked in b/33946428 and probably involves setting the
+/// attribute on some data structure that represents a layer.
+///
+/// @param spec Buffer specification.
+/// @param z_order Z order, higher values are displayed on top of lower ones,
+///     the default value is 0.
+void gvr_buffer_spec_set_z_order(gvr_buffer_spec* spec, int z_order);
+
+/// Sets the initial visibility of the layer to be created.
+/// Note that this API is a short-term workaround for SysUI work and is never
+/// meant to graduate as is to either gvr.h or gvr_private.h. The proper
+/// solution is tracked in b/33946428 and probably involves setting the
+/// attribute on some data structure that represents a layer.
+///
+/// @param spec Buffer specification.
+/// @param visibility Initial visibility of the layer, defaults to GVR_VISIBLE.
+///     See enum gvr_visibility for possible values.
+void gvr_buffer_spec_set_visibility(gvr_buffer_spec* spec,
+                                    int32_t visibility);
+
+/// Sets whether to blur layers below the layer to be created.
+/// Blurring is applied only to visible layers and only when the layer is
+/// visible.
+/// Note that this API currently is only implemented by the DreamOS
+/// implementation of GVR and is a no-op in other implementations.
+/// TODO(b/33946428): investigate the proper way to surface this feature
+/// to SysUI.
+///
+/// @param spec Buffer specification.
+/// @param blur_behind whether to blur layers behind, defaults to
+///     GVR_BLUR_BEHIND_TRUE. See enum gvr_blur_behind for possible values.
+void gvr_buffer_spec_set_blur_behind(gvr_buffer_spec* spec,
+                                     int32_t blur_behind);
+
+// ************************************************************************** //
+// *                     Daydream PlexEng experimental APIs                 * //
+// ************************************************************************** //
+
+// Registers a new performance event listener that will be invoked on points
+// of interest. By default no event listener is attached.  If multiple event
+// listeners are attached they will all be invoked.  Failures can be checked
+// with gvr_get_error().
+// @param out_handle The pointer to memory where a successfully created handle
+//     will be written.
+// @param gvr The context to register callbacks for.
+// @param user_data The pointer that will be passed back on callbacks for
+//     user_data.
+// @param event_callback The callback to be invoked when an event is observed.
+//     On performance events callback will be invoked with the
+//     user_data passed here, the gvr_perf_event_callback_type, and a float
+//     value (if applicable) or -1.f.
+// @return Returns GVR_EXPERIMENTAL_ERROR_NONE if a handle was created,
+//     GVR_EXPERIMENTAL_ERROR_UNIMPLEMENTED if this feature is disabled,
+//     or GVR_EXPERIMENTAL_ERROR_INVALID_ARGUMENT if a null pointer was passed.
+bool gvr_experimental_register_perf_event_callback(
+    gvr_context* gvr, int* out_handle, void* user_data,
+    void (*event_callback)(void*, int, float));
+
+// Unregisters a previously registered callback by its handle. Failures can be
+// checked with gvr_get_error().
+// @param handle The handle which was returned when registering the callback.
+// @return Returns GVR_EXPERIMENTAL_ERROR_NONE if callback was unregistered,
+//     GVR_EXPERIMENTAL_ERROR_INVALID_ARGUMENT if the if the handle wasn't
+//     previously
+//     registered.  If this feature is not enabled it will return
+//     GVR_EXPERIMENTAL_ERROR_UNIMPLEMENTED.
+bool gvr_experimental_unregister_perf_event_callback(gvr_context* gvr,
+                                                     int handle);
+
+// ************************************************************************** //
+// *                    GVR Analytics experimental APIs                     * //
+// ************************************************************************** //
+// TODO(b/31634289): These functions are experimental because their main client
+// case is the performance monitoring HUD, whose form and function is still
+// under development. Consequently, the analytics API may change as the HUD's
+// needs become clearer.
+//
+// These functions will be moved into the main API (probably gvr_private.h) once
+// the HUD is ready to be shipped as part of the SDK.
+//
+// Contacts: georgelu@
+
+/// Returns whether the "Performance Monitoring" developer option is enabled.
+///
+/// @param user_prefs Pointer to the gvr_user_prefs object returned by
+///     gvr_get_user_prefs.
+/// @return True if the "Performance Monitoring" developer option is enabled.
+bool gvr_user_prefs_get_performance_monitoring_enabled(
+    const gvr_user_prefs* user_prefs);
+
+// Opaque struct returned by gvr_get_analytics that can be queried through
+// gvr_analytics_get* functions.
+//
+// Note: The struct is never actually defined since gvr_analytics is actually
+// just a static_cast of a gvr_context, similar to how gvr_user_prefs works.
+typedef struct gvr_analytics_ gvr_analytics;
+
+// Returns an opaque struct that can be queried for analytics data. The returned
+// struct remains valid as long as the context is valid.
+//
+// @param gvr Pointer to the current gvr_context instance.
+// @return An opaque struct that can be queried through gvr_analytics_*
+//     functions.
+const gvr_analytics* gvr_get_analytics(gvr_context* gvr);
+
+// If the "Performance Monitoring" developer option in VR settings is enabled,
+// returns a gvr_analytics_sample* containing analytics data. Caller is
+// responsible for calling gvr_analytics_destroy_sample on the returned object.
+//
+// @param analytics gvr_analytics* returned by gvr_get_analytics.
+// @return gvr_analytics_sample* containing analytics data.
+const gvr_analytics_sample* gvr_analytics_create_sample(
+    const gvr_analytics* analytics);
+
+// Returns pointer to a buffer containing a serialized AnalyticsSample proto.
+// The buffer is valid only for the lifetime of the gvr_analytics_sample.
+//
+// @param Pointer to a gvr_analytics_sample object.
+// @return Pointer to buffer.
+const char* gvr_analytics_sample_get_buffer(const gvr_analytics_sample* sample);
+
+// Returns the length of the buffer returned by gvr_analytics_sample_get_buffer.
+//
+// @param Pointer to a gvr_analytics_sample object.
+// @return Length of buffer.
+size_t gvr_analytics_sample_get_buffer_length(
+    const gvr_analytics_sample* sample);
+
+// Destroys a gvr_analytics_sample* previously created through
+// gvr_analytics_create_sample.
+//
+// @param sample Pointer to pointer that will be set to null and whose
+//     underlying gvr_analytics_sample will be destroyed.
+void gvr_analytics_destroy_sample(const gvr_analytics_sample** sample);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // VR_GVR_CAPI_SRC_GVR_EXPERIMENTAL_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_private.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_private.h
new file mode 100644
index 0000000..0a9d30e
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_private.h
@@ -0,0 +1,378 @@
+#ifndef VR_GVR_CAPI_SRC_GVR_PRIVATE_H_
+#define VR_GVR_CAPI_SRC_GVR_PRIVATE_H_
+
+#include <stddef.h>
+
+#include "vr/gvr/capi/include/gvr_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Opaque handle to gvr_tracker_state object containing serialized state.
+typedef struct gvr_tracker_state_ gvr_tracker_state;
+
+// Opaque handle to gvr_display_synchronizer object for display synchronization.
+typedef struct gvr_display_synchronizer_ gvr_display_synchronizer;
+
+// Internal Google VR C API methods. These methods are exposed only to internal
+// targets, but should follow all the same backwards-compatible restrictions
+// as the public C API.
+
+/// Sets whether asynchronous reprojection is currently enabled.
+///
+/// If enabled, frames will be collected by the rendering system and
+/// asynchronously re-projected in sync with the scanout of the display. This
+/// feature may not be available on every platform, and requires a
+/// high-priority render thread with special extensions to function properly.
+///
+/// Note: On Android, this feature can be enabled solely via the GvrLayout Java
+/// instance which (indirectly) owns this gvr_context. The corresponding
+/// method call is GvrLayout.setAsyncReprojectionEnabled().
+///
+/// @param gvr Pointer to the gvr instance.
+/// @Param enabled Whether to enable async reprojection.
+/// @return Whether the setting was succesfully applied.
+bool gvr_set_async_reprojection_enabled(gvr_context* gvr, bool enabled);
+
+// Initializes necessary GL-related objects and uses the current thread and
+// GL context for racing the scanline. This function should only be called
+// by the SDK itself, not by any application, unless that application is
+// providing a high-priority thread and GL context for async reprojection.
+//
+// Note: This method is private as it is intended for use solely by the
+// hidden async reprojection implementation in ScanlineRacingRenderer.java,
+// called in onSurfaceCreated().
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_on_surface_created_reprojection_thread(gvr_context* gvr);
+
+// Renders the scanline layer. This function should only be called
+// in the same thread that gvr_initialize_gl_projection_thread was called in.
+// This function should only be called by the SDK itself, not by any
+// application, unless that application is providing a high-priority
+// thread and GL context for async reprojection.
+//
+// Note: This method is private as it is intended for use solely by the
+// hidden async reprojection implementation in ScanlineRacingRenderer.java.
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_render_reprojection_thread(gvr_context* gvr);
+
+// Signals to the reprojection thread that it is paused. This is necessary
+// in case the application render thread is blocked on pending work by the
+// reprojection thread. This function will abort any blocking.
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_on_pause_reprojection_thread(gvr_context* gvr);
+
+// Sets the parameters for the external surface managed by the reprojection
+// thread.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param surface_id The ID of the external Surface managed by the reprojection
+//     thread. The ID is issued by the SurfaceTextureManager.
+// @param texture_id The GL texture ID associated with the external Surface.
+// @param timestamp The timestamp of the most recent frame the Surface holds.
+// @param surface_transfrom Matrix that transforms homogeneous texture coords to
+//     the external surface texture space.
+void gvr_update_surface_reprojection_thread(gvr_context* gvr,
+      int32_t surface_id, int32_t texture_id, gvr_clock_time_point timestamp,
+      gvr_mat4f surface_transform);
+
+// Removes all external surfaces managed by the reprojection thread. This does
+// not destoy the surfaces: it removes tracking by the reprojection thread.
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_remove_all_surfaces_reprojection_thread(gvr_context* gvr);
+
+// Reconnects the sensors when the sensor producers are created internally.
+//
+// Note: This function is not thread-safe, and should be called only on the
+// rendering thread. It is intended to be used internally by GvrLayout when
+// the target presentation display changes.
+//
+// @param gvr Pointer to the gvr_context instance.
+void gvr_reconnect_sensors(gvr_context* gvr);
+
+// Sets VR viewer params for the current context.
+//
+// Note: This function does not update the viewer proto in the common storage
+// location. Rather, it overrides the viewer params solely for the provided
+// gvr_context.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param serialized_viewer_params A pointer to the payload containing the
+//     serialized viewer params proto.
+// @param serialized_viewer_params_size_bytes The length in bytes of the
+//     serialized viewer params payload.
+// @return Whether the serialized viewer params proto was successfully applied.
+bool gvr_set_viewer_params(gvr_context* gvr,
+                           const void* serialized_viewer_params,
+                           size_t serialized_viewer_params_size_bytes);
+
+// Sets the lens offset.
+//
+// @param offset The offset of the lens center from the expected location in
+// screen space.
+void gvr_set_lens_offset(gvr_context* gvr, gvr_vec2f offset);
+
+// Sets display metrics for the current context.
+//
+// Note: This function does not update the phone proto in the commom storage
+// location. Rather, it overrides the internal metrics solely for the provided
+// |gvr| context.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param size_pixels The dimensions in pixels of the active display.
+// @param meters_per_pixel The density of the current display in meters/pixel.
+// @param 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.
+void gvr_set_display_metrics(gvr_context* gvr, gvr_sizei size_pixels,
+                             gvr_vec2f meters_per_pixel,
+                             float border_size_meters);
+
+// Sets the display rotation offset that is applied at distortion correction
+// time to take into account the device's display orientation.
+//
+// For instance, calling this with display_output_rotation set to 1 allows
+// clients to lock their phone orientation to portrait on an Android phone and
+// still get a correctly rendered VR mode with the two eyes stacked up along the
+// longer phone dimension.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param display_output_rotation Value encoding the rotation used when
+//     performing distortion correction. Supported values are:
+//     0 - Default mode. Eye viewports are positioned side-by-side along the
+//         "width" dimension, with left eye in the x < 0.5 half.
+//     1 - Applies a clock-wise rotation of 90 degrees on the display when
+//         doing distortion correction. Eye viewports are positioned
+//         side-by-side along the "height" dimension, with left eye in the
+//         y > 0.5 half.
+// Rotation modes used when performing distortion correction.
+enum {
+  GVR_PRIVATE_DISPLAY_OUTPUT_ROTATION_0 = 0,
+  GVR_PRIVATE_DISPLAY_OUTPUT_ROTATION_90 = 1,
+};
+void gvr_set_display_output_rotation(gvr_context* gvr,
+                                     int32_t display_output_rotation);
+
+// Gets the size of the border around the display used by the given gvr_context.
+//
+// @param gvr Pointer to the gvr_context instance.
+float gvr_get_border_size_meters(const gvr_context* gvr);
+
+// Returns whether the surface size was changed since the last call to this
+// function (it's changed with gvr_set_surface_size()).
+//
+// @param gvr Pointer to the gvr_context instance.
+// @return Whether the surface size was changed.
+bool gvr_check_surface_size_changed(gvr_context* gvr);
+
+// Returns the current surface size in pixels, or (0, 0) if the surface size
+// matches that of the active display (which is the default).
+//
+// @param gvr Pointer to the gvr_context instance.
+// @return The current surface size in pixels.
+gvr_sizei gvr_get_surface_size(const gvr_context* gvr);
+
+// Sets a handler that is called back when the back gesture is detected,
+// which is when the phone changes from landscape to portrait orientation
+// within a few seconds.
+//
+// @param gvr Pointer to the gvr_context instance.
+// @param handler The event_handler callback. May be null to clear the
+//     registered event_handler.
+// @param user_data An opaque pointer to user_data which will be supplied
+//     as the callback argument. The caller is responsible for ensuring the
+//     validity of this data for the duration of the handler registration.
+typedef void (*event_handler)(void* user_data);
+void gvr_set_back_gesture_event_handler(gvr_context* gvr, event_handler handler,
+                                        void* user_data);
+
+// Internal method to pause head tracking used by GvrLayout. Disables all
+// sensors (to save power) and gets the serialized tracker state.
+//
+// @param gvr Pointer to the gvr instance for which tracking will be paused and
+//     sensors disabled.
+//
+// @return Pointer to a tracker_state object containing the serialized tracker
+//     state. The caller is responsible for calling destroy on the returned
+//     handle.
+gvr_tracker_state* gvr_pause_tracking_get_state(gvr_context* gvr);
+
+// Internal method to resume head tracking used by GvrLayout. Re-enables all
+// sensors and sets the tracker state.
+//
+// @param gvr Pointer to the gvr instance for which tracking will be resumed.
+//     serialized tracker state object.
+// @param tracker_state Pointer to a tracker_state object containing the
+//     serialized tracker state object.
+void gvr_resume_tracking_set_state(
+    gvr_context* gvr, gvr_tracker_state* tracker_state);
+
+// Sets the internal flag that ignores calls to the public API's
+// gvr_pause_tracking and gvr_resume_tracking.
+// When true, the tracker is handled through GvrLayout
+// gvr_pause_tracking_private / gvr_resume_tracking_private direct calls through
+// the GvrApi instance are ignored. This is workaround to temporarily support
+// clients using GvrLayout that manually call pause/ resume tracking.
+// TODO(b/30404822) : clean this up once all existing clients move away from the
+// obsolete behavior.
+//
+// @param gvr Pointer to the gvr instance.
+// @param should_ignore Whether manual pause / resume tracker should be ignored.
+void gvr_set_ignore_manual_tracker_pause_resume(gvr_context* gvr,
+                                                bool should_ignore);
+
+// Creates a new tracker state object from the serialized tracker state buffer.
+//
+// @param tracker_state_buffer Pointer to buffer containing the serialized
+// tracker state.
+// @param buf_size Size of the tracker state buffer.
+//
+// @return Pointer to a tracker_state object containing the serialized tracker
+// state string. The caller is responsible for calling destroy on the returned
+// handle.
+gvr_tracker_state* gvr_tracker_state_create(const char* tracker_state_buffer,
+                                            size_t buf_size);
+
+// Gets the size of the buffer that is required to hold the serialized
+// gvr_tracker_state.
+//
+// @param Pointer to a gvr_tracker_state object containing the serialized
+// tracker state.
+//
+// @return Size of the buffer,
+size_t gvr_tracker_state_get_buffer_size(gvr_tracker_state* tracker_state);
+
+// Gets the buffer that holds the serialized gvr_tracker_state.
+//
+// @param Pointer to a tracker_state object containing the serialized tracker
+// state.
+//
+// @return Pointer to the buffer.
+const char* gvr_tracker_state_get_buffer(gvr_tracker_state* tracker_state);
+
+// Destroys a gvr_tracker_state instance.
+//
+// @param tracker_state Pointer to a pointer of the gvr_tracker_state instance
+// to be destroyed and nulled.
+void gvr_tracker_state_destroy(gvr_tracker_state** tracker_state);
+
+// Creates a new synchronizer instance.
+//
+// @return synchronizer Pointer to the new gvr_display_synchronizer instance.
+gvr_display_synchronizer* gvr_display_synchronizer_create();
+
+// Destroy the synchonronizer instance and null the pointer.
+//
+// @param synchronizer Pointer to a pointer to the gvr_display_synchronizer
+//     instance.
+void gvr_display_synchronizer_destroy(gvr_display_synchronizer** synchronizer);
+
+// Resets the synchronizer with updated vsync timing data.
+//
+// @param synchronizer Pointer to the new gvr_display_synchronizer instance.
+// @param expected_interval_nanos The expected average time between
+//     synchronization times, in nanoseconds, or 0 if unknown.
+// @param vsync_offset_nanos The duration, in nanos, such that the current sync
+//     time minus the display vsync offset is the time when the physical
+//     scan-out hardware begins to read data from the frame buffer.
+void gvr_display_synchronizer_reset(gvr_display_synchronizer* synchronizer,
+                                    int64_t expected_interval_nanos,
+                                    int64_t vsync_offset_nanos);
+
+// Updates the synchronizer with dispplay data for a new frame.
+//
+// @param vsync_time The new frame's vsync time.
+// @param rotation_degrees The screen rotation from sensor space to display
+//     space in degrees.
+void gvr_display_synchronizer_update(gvr_display_synchronizer* synchronizer,
+                                     gvr_clock_time_point vsync_time,
+                                     int32_t rotation);
+
+// Installs the display synchronizer into a GVR context.
+//
+// @param gvr Pointer to the current gvr_context instance.
+// @param synchronizer Pointer to the gvr_display_synchronizer instance, to be
+//     used by the context implementation during rendering.
+void gvr_set_display_synchronizer(gvr_context* gvr,
+                                  gvr_display_synchronizer* synchronizer);
+
+// Sets the current error code. Overwrites any existing error code.
+//
+// @param gvr Pointer to the current gvr_context instance.
+// @param error_code The error code to set.
+void gvr_set_error(gvr_context* gvr, int32_t error_code);
+
+// Called by the platform layer to to indicate the application is paused. (e.g.
+// On Android, this function is called by GvrLayout.OnPause().)
+//
+// @param gvr Pointer to the current gvr_context instance.
+void gvr_pause(gvr_context* gvr);
+
+// Called by the platform layer to to indicate the application has resumed.
+// (e.g. On Android, this function is called by GvrLayout.OnResume().)
+//
+// @param gvr Pointer to the current gvr_context instance.
+void gvr_resume(gvr_context* gvr);
+
+// Dumps additional data to logcat or disk to be included in bug reports.
+//
+// @param gvr Pointer to the current gvr_context instance.
+void gvr_dump_debug_data(gvr_context* gvr);
+
+// Returns true if the libgvr implementation is using the dedicated VR display
+// service, false otherwise.
+//
+// @param gvr Pointer to the current gvr_context instance.
+bool gvr_using_vr_display_service(gvr_context* gvr);
+
+// Creates a new gvr_context using the supplied tracker, only for testing.
+//
+// Note: The pose returned is *in start space*. This is *not* the same space as
+// the pose normally returned by |gvr_get_head_space_from_start_space_rotation|.
+//
+// @param tracker The test pose tracker to use.
+// @param user_data An opaque pointer to user_data which will be supplied
+//     as the callback argument. The caller is responsible for ensuring the
+//     validity of this data for the duration of the handler registration.
+typedef gvr_mat4f (*gvr_test_pose_tracker)(void*, gvr_clock_time_point);
+gvr_context* gvr_create_with_tracker_for_testing(gvr_test_pose_tracker tracker,
+                                                 void* user_data);
+
+// Request resource sharing between the application's OpenGL context and the
+// scanline racing context.  This must be called before gvr_initialize_gl.
+// <p>
+// This is a best effort request rather than an explicit toggle; it is a no-op
+// if the client does not enable async reprojection, or if the platform does not
+// support resource sharing.
+// <p>
+// The only OpenGL resource that we need sharing for is the framebuffer texture
+// that the app renders to, and that distortion samples from.  If resource
+// sharing is disabled, then we use an EGLImage so that it can be accessed from
+// both contexts.
+// <p>
+// Also sets a callback function that is called at the end of gvr_initialize_gl,
+// while the application's OpenGL context is still active on the current thread.
+// This is used internally to notify the scanline racing renderer that the
+// application's OpenGL context has been created.
+//
+// @param gvr Pointer to the current gvr_context instance.
+// @param handler Callback that gets called when the app context becomes ready.
+// @param user_data An opaque pointer to user data which will be supplied
+//     as the callback argument. The caller is responsible for ensuring the
+//     validity of this data for the duration of the handler registration.
+typedef void (*gvr_egl_context_listener)(void*);
+void gvr_request_context_sharing(gvr_context* gvr,
+                                 gvr_egl_context_listener handler,
+                                 void* user_data);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // VR_GVR_CAPI_SRC_GVR_PRIVATE_H_
diff --git a/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h
new file mode 100644
index 0000000..f7ae6a5
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/include/vr/gvr/capi/src/gvr_types_experimental.h
@@ -0,0 +1,66 @@
+#ifndef VR_GVR_CAPI_SRC_GVR_TYPES_EXPERIMENTAL_H_
+#define VR_GVR_CAPI_SRC_GVR_TYPES_EXPERIMENTAL_H_
+
+#include <string>
+
+// ************************************************************************** //
+// *                     DaydreamOS experimental Types                * //
+// ************************************************************************** //
+
+// Visibility of a layer.
+typedef enum {
+  GVR_INVISIBLE = 0,
+  GVR_VISIBLE = 1,
+} gvr_visibility;
+
+// Whether to blur layers behind a layer.
+typedef enum {
+  GVR_BLUR_BEHIND_FALSE = 0,
+  GVR_BLUR_BEHIND_TRUE = 1,
+} gvr_blur_behind;
+
+// GVR external surface
+typedef struct gvr_external_surface_ gvr_external_surface;
+
+// ************************************************************************** //
+// *                     Daydream PlexEng experimental Types                * //
+// ************************************************************************** //
+
+// Types of events that can have callbacks registered.
+// If documented, type will return a payload value when called, or will
+// otherwise be invoked with -1.f.
+// This enum has to be duplicated because there is no way to include from
+// /vr/gvr/render/performance_registry.h.  Duplicate changes made here there.
+typedef enum {
+  // Will be invoked with value -1.f.
+  GVR_ON_ASYNC_REPROJECTION_FRAME_START = 0,
+  // Will be invoked with value -1.f.
+  GVR_ON_ASYNC_REPROJECTION_FRAME_STOP = 1,
+  // When invoked will be called with how late in microseconds the frame was.
+  GVR_ON_ASYNC_REPROJECTION_FRAME_DROP = 2,
+  // The number of types of performance events you can have.
+  // Also note that this value is considered invalid.
+  GVR_NUM_PERF_EVENT_CALLBACK_TYPES = 3,
+} gvr_perf_event_callback_type;
+
+// Experimental VR-specific features which may or may not be supported on the
+// underlying platform.  These values should not overlap with current or future
+// gvr_feature values, so we're starting with 1000000 and increasing upward.
+typedef enum {
+  // Head tracking with 6 degrees of freedom (position & rotation)
+  GVR_FEATURE_HEAD_POSE_6DOF = 1000000,
+} gvr_experimental_feature;
+
+// ************************************************************************** //
+// *                    GVR Analytics experimental APIs                     * //
+// ************************************************************************** //
+
+// Opaque struct returned by gvr_analytics_create_sample, used to transmit an
+// AnaylticsSample proto across the native layer.
+typedef struct gvr_analytics_sample_ {
+  // Serialized AnalyticsSample proto. Note that this is not a C string, meaning
+  // it is not null-terminated and may contain non-terminating nulls.
+  std::string serialized_proto;
+} gvr_analytics_sample;
+
+#endif  // VR_GVR_CAPI_SRC_GVR_TYPES_EXPERIMENTAL_H_
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so
new file mode 100644
index 0000000..1d0ba50
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so
new file mode 100644
index 0000000..905ca64
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so
new file mode 100644
index 0000000..d62f7ca
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so
new file mode 100644
index 0000000..e342f6a
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_arm64/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr.so
new file mode 100644
index 0000000..8092138
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr_audio.so
new file mode 100644
index 0000000..3fe5b2c
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_x86/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr.so b/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr.so
new file mode 100644
index 0000000..3bcf60e
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr_audio.so b/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr_audio.so
new file mode 100644
index 0000000..2f2d834
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/android_x86_64/libgvr_audio.so
Binary files differ
diff --git a/libs/vr/libgvr/prebuilt/lib/common_library.aar b/libs/vr/libgvr/prebuilt/lib/common_library.aar
new file mode 100644
index 0000000..13147fe
--- /dev/null
+++ b/libs/vr/libgvr/prebuilt/lib/common_library.aar
Binary files differ
diff --git a/libs/vr/libgvr/shim_gvr.cpp b/libs/vr/libgvr/shim_gvr.cpp
new file mode 100644
index 0000000..264952e
--- /dev/null
+++ b/libs/vr/libgvr/shim_gvr.cpp
@@ -0,0 +1,1351 @@
+#define LOG_TAG "libgvr_shim"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl31.h>
+#include <GLES3/gl3ext.h>
+#include <algorithm>
+#include <cmath>
+
+#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/log.h>
+#include <dvr/graphics.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <private/dvr/buffer_hub_queue_core.h>
+#include <private/dvr/buffer_hub_queue_producer.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_client.h>
+#include <private/dvr/graphics_private.h>
+#include <private/dvr/internal_types.h>
+#include <private/dvr/numeric.h>
+#include <private/dvr/types.h>
+#include <private/dvr/video_mesh_surface_client.h>
+#include <sys/system_properties.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/include/gvr_ext.h>
+#include <vr/gvr/capi/include/gvr_util.h>
+#include <vr/gvr/capi/src/gvr_experimental.h>
+#include <vr/gvr/capi/src/gvr_private.h>
+
+#include <android_runtime/android_view_Surface.h>
+#include <gui/Surface.h>
+
+using android::dvr::DisplayClient;
+using android::dvr::EigenToGvrMatrix;
+using android::dvr::FieldOfView;
+using android::dvr::FovRadiansToDegrees;
+using android::dvr::GetSystemClockNs;
+using android::dvr::GvrIdentityMatrix;
+using android::dvr::GvrMatrixToPosef;
+using android::dvr::GvrToDvrFov;
+using android::dvr::GvrToEigenMatrix;
+using android::dvr::GvrToEigenRotation;
+using android::dvr::GvrTranslationMatrix;
+using android::dvr::IsEqual;
+using android::dvr::PosefToGvrMatrix;
+using android::dvr::mat3;
+using android::dvr::mat4;
+using android::dvr::Posef;
+using android::dvr::quat;
+using android::dvr::vec3;
+
+namespace {
+
+constexpr static int32_t GVR_SDK_MAJOR_VERSION = 2;
+constexpr static int32_t GVR_SDK_MINOR_VERSION = 0;
+constexpr static int32_t GVR_SDK_PATCH_VERSION = 0;
+
+// The "DaydreamOS" part has been appended to make easier to see when VrCore
+// dynamic GVR API loading is effectively working.
+static const char* kVersionString = "2.0.0 DaydreamOS";
+static const char* kViewerVendor = "Google";
+static const char* kViewerModel = "Lucid";
+
+// Experimental system property used to provide 6DoF information on 3DoF APIs.
+static const char* kForce6DofProp = "experimental.force_6dof";
+
+static constexpr int kControllerCount = 2;
+
+gvr_frame* GetFrameFromSwapChain(gvr_swap_chain* swap_chain) {
+  return reinterpret_cast<gvr_frame*>(swap_chain);
+}
+
+gvr_swap_chain* GetSwapChainForFrame(gvr_frame* frame) {
+  return reinterpret_cast<gvr_swap_chain*>(frame);
+}
+
+const gvr_swap_chain* GetSwapChainForFrame(const gvr_frame* frame) {
+  return reinterpret_cast<const gvr_swap_chain*>(frame);
+}
+
+// Returns the world to head transform as a Posef.
+Posef ToPosef(const DvrPoseAsync& pose) {
+  return Posef(
+      quat(pose.orientation[3], pose.orientation[0], pose.orientation[1],
+           pose.orientation[2]),
+      vec3(pose.translation[0], pose.translation[1], pose.translation[2]));
+}
+
+// Returns the world to head transform, with 0 position, as a gvr matrix
+gvr_mat4f Gvr6dofTo3dof(const gvr_mat4f& pose) {
+  gvr_mat4f ret = pose;
+  ret.m[0][3] = 0;
+  ret.m[1][3] = 0;
+  ret.m[2][3] = 0;
+  return ret;
+}
+
+void GvrToDvrPose(gvr_mat4f world_to_head_transform,
+                  /*out*/ float32x4_t* orientation,
+                  /*out */ float32x4_t* translation) {
+  Posef pose = GvrMatrixToPosef(world_to_head_transform);
+  (*orientation)[0] = pose.GetRotation().x();
+  (*orientation)[1] = pose.GetRotation().y();
+  (*orientation)[2] = pose.GetRotation().z();
+  (*orientation)[3] = pose.GetRotation().w();
+  (*translation)[0] = pose.GetPosition().x();
+  (*translation)[1] = pose.GetPosition().y();
+  (*translation)[2] = pose.GetPosition().z();
+  (*translation)[3] = 0;
+}
+
+bool MatricesAlmostEqual(const gvr_mat4f& m1, const gvr_mat4f& m2,
+                         float tolerance) {
+  for (int row = 0; row < 4; ++row) {
+    for (int col = 0; col < 4; ++col) {
+      if (!IsEqual(m1.m[row][col], m2.m[row][col], tolerance))
+        return false;
+    }
+  }
+  return true;
+}
+
+gvr_mat4f FovToViewportTransform(const gvr_rectf& fov) {
+  // Depth range (1 1000) is chosen to match gvr impl in google3, which is
+  // chosen to match Unity integration.
+  return EigenToGvrMatrix(
+      GvrToDvrFov(fov).GetProjectionMatrix(1.f, 1000.f).inverse());
+}
+
+gvr_rectf ViewportTransformToFov(const gvr_mat4f& transform) {
+  return DvrToGvrFov(
+      FieldOfView::FromProjectionMatrix(GvrToEigenMatrix(transform).inverse()));
+}
+
+bool GetGlColorFormat(int32_t gvr_color_format,
+                      /*out*/ GLenum* gl_color_format) {
+  switch (gvr_color_format) {
+    case GVR_COLOR_FORMAT_RGBA_8888:
+      *gl_color_format = GL_RGBA8;
+      break;
+    case GVR_COLOR_FORMAT_RGB_565:
+      *gl_color_format = GL_RGB565;
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+bool GetGlDepthFormat(int32_t gvr_depth_format,
+                      /*out*/ GLenum* gl_depth_format) {
+  switch (gvr_depth_format) {
+    case GVR_DEPTH_STENCIL_FORMAT_DEPTH_16:
+      *gl_depth_format = GL_DEPTH_COMPONENT16;
+      break;
+    case GVR_DEPTH_STENCIL_FORMAT_DEPTH_24:
+      *gl_depth_format = GL_DEPTH_COMPONENT24;
+      break;
+    case GVR_DEPTH_STENCIL_FORMAT_DEPTH_24_STENCIL_8:
+      *gl_depth_format = GL_DEPTH24_STENCIL8;
+      break;
+    case GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F:
+      *gl_depth_format = GL_DEPTH_COMPONENT32F;
+      break;
+    case GVR_DEPTH_STENCIL_FORMAT_DEPTH_32_F_STENCIL_8:
+      *gl_depth_format = GL_DEPTH32F_STENCIL8;
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+// Returns true on success, false on failure. If the swap_chain already has a
+// DvrGraphicsContext and gvr buffer, they'll be freed first. If creation fails,
+// the DvrGraphicsContext in the swap_chain will be set to null and the
+// corresponding gvr buffer will be freed.
+bool CreateDvrGraphicsContextAndGvrBuffer(gvr_swap_chain* swap_chain) {
+  if (swap_chain->buffers_.empty()) {
+    ALOGE("Can't create a graphics context for an empty swap chain");
+    return false;
+  }
+
+  // We currently only render the first gvr buffer. Create a DvrGraphicsContext
+  // for the first buffer only.
+  gvr_buffer& buf = swap_chain->buffers_[0];
+  buf.FreeGl();
+
+  bool visible;
+  int z_order;
+  if (swap_chain->graphics_context_ != nullptr) {
+    visible = dvrGraphicsSurfaceGetVisible(swap_chain->graphics_context_);
+    z_order = dvrGraphicsSurfaceGetZOrder(swap_chain->graphics_context_);
+    dvrGraphicsContextDestroy(swap_chain->graphics_context_);
+    swap_chain->graphics_context_ = nullptr;
+  } else {
+    visible = buf.spec.initially_visible;
+    z_order = buf.spec.z_order;
+  }
+
+  int width = 0, height = 0;
+  GLuint texture_id = 0;
+  GLenum texture_target = 0;
+  DvrSurfaceParameter surface_params[] = {
+      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, false),
+      DVR_SURFACE_PARAMETER_IN(CREATE_GL_CONTEXT, 0),
+      DVR_SURFACE_PARAMETER_IN(WIDTH, buf.spec.size.width),
+      DVR_SURFACE_PARAMETER_IN(HEIGHT, buf.spec.size.height),
+      DVR_SURFACE_PARAMETER_IN(BLUR_BEHIND, buf.spec.blur_behind),
+      DVR_SURFACE_PARAMETER_IN(VISIBLE, visible),
+      DVR_SURFACE_PARAMETER_IN(Z_ORDER, z_order),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &width),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &height),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_TYPE, &texture_target),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_ID, &texture_id),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+
+  DvrGraphicsContext* graphics_context;
+  int ret = dvrGraphicsContextCreate(surface_params, &graphics_context);
+  if (ret < 0) {
+    ALOGE("dvrGraphicsContextCreate failed: %d (%s)", ret, strerror(-ret));
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    return false;
+  }
+
+  // Sanity check that the size of the buffer we allocated from the system is
+  // what we expect
+  if (buf.spec.size != gvr_sizei{width, height}) {
+    ALOGE(
+        "The created surface is the wrong size."
+        " Should be %dx%d, instead got %dx%d.",
+        buf.spec.size.width, buf.spec.size.height, width, height);
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    dvrGraphicsContextDestroy(graphics_context);
+    return false;
+  }
+
+  buf = gvr_buffer(swap_chain->context, buf.spec, texture_id, texture_target);
+  if (buf.frame_buffer == 0) {
+    dvrGraphicsContextDestroy(graphics_context);
+    return false;
+  }
+
+  swap_chain->graphics_context_ = graphics_context;
+  return true;
+}
+
+bool SwapChainResizeBuffer(gvr_swap_chain* swap_chain, int buffer_index) {
+  gvr_buffer& buf = swap_chain->buffers_[buffer_index];
+  buf.FreeGl();
+  gvr_sizei orig_size = buf.spec.size;
+  buf.spec.size = buf.requested_size;
+  bool resize_successful = false;
+  if (buffer_index == 0) {
+    resize_successful = CreateDvrGraphicsContextAndGvrBuffer(swap_chain);
+  } else {
+    buf = gvr_buffer(swap_chain->context, buf.spec, 0, GL_TEXTURE_2D);
+    resize_successful = buf.frame_buffer != 0;
+  }
+
+  if (resize_successful) {
+    // The resize was successful, so clear the resize request
+    buf.requested_size = {-1, -1};
+  } else {
+    ALOGE("Failed to resize buffer. orig_size=%dx%d requested_size=%dx%d.",
+          orig_size.width, orig_size.height, buf.requested_size.width,
+          buf.requested_size.height);
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    buf.spec.size = orig_size;
+  }
+
+  return resize_successful;
+}
+
+void WaitNextFrame(gvr_swap_chain* swap_chain, int64_t start_delay_nanos,
+                   gvr_frame_schedule* out_next_frame_schedule,
+                   bool called_by_app) {
+  if (called_by_app)
+    swap_chain->wait_next_frame_called_by_app_ = true;
+
+  DvrFrameSchedule dvr_schedule;
+  int ret = dvrGraphicsWaitNextFrame(swap_chain->graphics_context_,
+                                     start_delay_nanos, &dvr_schedule);
+  if (ret < 0) {
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    return;
+  }
+  if (out_next_frame_schedule) {
+    out_next_frame_schedule->vsync_count = dvr_schedule.vsync_count;
+    out_next_frame_schedule->scheduled_finish.monotonic_system_time_nanos =
+        dvr_schedule.scheduled_frame_finish_ns;
+  }
+
+  DvrPoseAsync pose;
+  ret = dvrPoseGet(swap_chain->context->pose_client_, dvr_schedule.vsync_count,
+                   &pose);
+  if (ret < 0) {
+    ALOGW("dvrPoseGet failed: %d", ret);
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    return;
+  }
+
+  swap_chain->context->next_frame_6dof_pose_ = PosefToGvrMatrix(ToPosef(pose));
+
+  for (int i = 0; i < kControllerCount; ++i) {
+    ret = dvrPoseGetController(swap_chain->context->pose_client_, i,
+                               dvr_schedule.vsync_count, &pose);
+    if (ret == 0) {
+      // Silently fail when there are no controllers.
+      swap_chain->context->next_frame_controller_pose_[i] =
+          PosefToGvrMatrix(ToPosef(pose).Inverse());
+    }
+  }
+}
+
+bool VerifyBufferIndex(const std::string& function_name,
+                       const gvr_swap_chain* swap_chain, int index) {
+  if (index > static_cast<int32_t>(swap_chain->buffers_.size())) {
+    ALOGE("%s out of range buffer index. index=%d num_buffers=%zu.",
+          function_name.c_str(), index, swap_chain->buffers_.size());
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    return false;
+  }
+  return true;
+}
+
+}  // anonymous namespace
+
+gvr_context* gvr_create(JNIEnv* env, jobject /* app_context */,
+                        jobject /* class_loader */) {
+  std::unique_ptr<gvr_context> context(new gvr_context);
+
+  // Set cpu set to avoid default scheduling randomness.
+  dvrSetCpuPartition(0, "/application/performance");
+
+  context->jni_env_ = env;
+  context->pose_client_ = dvrPoseCreate();
+  if (!context->pose_client_) {
+    ALOGE("Failed to create pose client");
+    return nullptr;
+  }
+
+  context->display_client_ = DisplayClient::Create();
+  if (!context->display_client_) {
+    ALOGE("Failed to create display client");
+    return nullptr;
+  }
+
+  int ret =
+      context->display_client_->GetDisplayMetrics(&context->display_metrics_);
+  if (ret < 0) {
+    ALOGE("Failed to get display metrics: %d (%s)", ret, strerror(-ret));
+    return nullptr;
+  }
+
+  const float* left_fov = context->display_metrics_.left_fov_lrbt.data();
+  context->left_eye_viewport_transform_ =
+      FovToViewportTransform(FovRadiansToDegrees(
+          gvr_rectf{left_fov[0], left_fov[1], left_fov[2], left_fov[3]}));
+
+  const float* right_fov = context->display_metrics_.right_fov_lrbt.data();
+  context->right_eye_viewport_transform_ =
+      FovToViewportTransform(FovRadiansToDegrees(
+          gvr_rectf{right_fov[0], right_fov[1], right_fov[2], right_fov[3]}));
+
+  context->next_frame_6dof_pose_ = GvrIdentityMatrix();
+
+  for (int i = 0; i < kControllerCount; ++i) {
+    context->next_frame_controller_pose_[i] = GvrIdentityMatrix();
+  }
+
+  // Check the system property to force 6DoF when requested 3DoF.
+  char prop_buffer[PROP_VALUE_MAX];
+  if (__system_property_get(kForce6DofProp, prop_buffer) &&
+      (!strncasecmp("1", prop_buffer, PROP_VALUE_MAX) ||
+       !strncasecmp("true", prop_buffer, PROP_VALUE_MAX))) {
+    context->force_6dof_ = true;
+  }
+
+  return context.release();
+}
+
+gvr_version gvr_get_version() {
+  gvr_version version = {};
+  version.major = GVR_SDK_MAJOR_VERSION;
+  version.minor = GVR_SDK_MINOR_VERSION;
+  version.patch = GVR_SDK_PATCH_VERSION;
+  return version;
+}
+
+const char* gvr_get_version_string() { return kVersionString; }
+
+int32_t gvr_get_error(gvr_context* gvr) { return gvr->last_error_; }
+
+int32_t gvr_clear_error(gvr_context* gvr) {
+  int32_t last_error = gvr->last_error_;
+  gvr->last_error_ = GVR_ERROR_NONE;
+  return last_error;
+}
+
+const char* gvr_get_error_string(int32_t error_code) {
+  switch (error_code) {
+    case GVR_ERROR_NONE:
+      return "No error";
+    case GVR_ERROR_CONTROLLER_CREATE_FAILED:
+      return "Creation of GVR controller context failed";
+    case GVR_ERROR_NO_FRAME_AVAILABLE:
+      return "No frame available in swap chain";
+    case GVR_ERROR_INTERNAL:
+      return "Internal error";
+    default:
+      return "(Internal error: unknown error code)";
+  }
+}
+
+const gvr_user_prefs* gvr_get_user_prefs(gvr_context* gvr) {
+  return &gvr->user_prefs_;
+}
+
+int32_t gvr_user_prefs_get_controller_handedness(
+    const gvr_user_prefs* /* user_prefs */) {
+  return GVR_CONTROLLER_RIGHT_HANDED;
+}
+
+gvr_context_::~gvr_context_() {
+  for (gvr_swap_chain* swap_chain : swap_chains_)
+    swap_chain->context = nullptr;
+  if (pose_client_)
+    dvrPoseDestroy(pose_client_);
+}
+
+void gvr_destroy(gvr_context** gvr) {
+  if (!gvr || !(*gvr)) {
+    ALOGW("gvr_destroy: Invalid gvr_context pointer.");
+    return;
+  }
+  delete *gvr;
+  *gvr = nullptr;
+}
+
+void gvr_initialize_gl(gvr_context* /* gvr */) {}
+
+bool gvr_get_async_reprojection_enabled(const gvr_context* /* gvr */) {
+  return true;
+}
+
+void gvr_get_recommended_buffer_viewports(
+    const gvr_context* gvr, gvr_buffer_viewport_list* viewport_list) {
+  gvr_buffer_viewport left(
+      /*buffer_index*/ 0,
+      /*uv*/ {0, .5f, 0, 1}, gvr->left_eye_viewport_transform_, GVR_LEFT_EYE,
+      GVR_EXTERNAL_SURFACE_ID_NONE, GVR_REPROJECTION_FULL);
+
+  gvr_buffer_viewport right(
+      /*buffer_index*/ 0,
+      /*uv*/ {.5f, 1, 0, 1}, gvr->right_eye_viewport_transform_, GVR_RIGHT_EYE,
+      GVR_EXTERNAL_SURFACE_ID_NONE, GVR_REPROJECTION_FULL);
+
+  viewport_list->viewports.resize(2);
+  viewport_list->viewports[0] = left;
+  viewport_list->viewports[1] = right;
+}
+
+void gvr_get_screen_buffer_viewports(const gvr_context* gvr,
+                                     gvr_buffer_viewport_list* viewport_list) {
+  gvr_get_recommended_buffer_viewports(gvr, viewport_list);
+}
+
+gvr_sizei gvr_get_maximum_effective_render_target_size(const gvr_context* gvr) {
+  return gvr_sizei{
+      static_cast<int32_t>(gvr->display_metrics_.distorted_width),
+      static_cast<int32_t>(gvr->display_metrics_.distorted_height)};
+}
+
+gvr_sizei gvr_get_screen_target_size(const gvr_context* gvr) {
+  // DisplayMetrics returns native_width and native_height for the display in
+  // portrait orientation, which our device is never in. Swap the width and
+  // height to account for this.
+  return gvr_sizei{
+      static_cast<int32_t>(gvr->display_metrics_.display_native_height),
+      static_cast<int32_t>(gvr->display_metrics_.display_native_width)};
+}
+
+void gvr_set_surface_size(gvr_context* gvr,
+                          gvr_sizei /* surface_size_pixels */) {
+  // TODO(leandrogracia): this needs to be properly implemented.
+  ALOGE("gvr_set_surface_size not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+void gvr_distort_to_screen(
+    gvr_context* gvr, int32_t /* texture_id */,
+    const gvr_buffer_viewport_list* /* viewport_list */,
+    gvr_mat4f /* head_space_from_start_space */,
+    gvr_clock_time_point /* target_presentation_time */) {
+  // TODO(leandrogracia): this needs to be properly implemented.
+  ALOGE("gvr_distort_to_screen not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+bool gvr_is_feature_supported(const gvr_context* /*gvr*/, int32_t feature) {
+  return feature == GVR_FEATURE_ASYNC_REPROJECTION;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Viewports and viewport lists
+/////////////////////////////////////////////////////////////////////////////
+
+bool gvr_buffer_viewport::operator==(const gvr_buffer_viewport_& other) const {
+  return buffer_index == other.buffer_index && uv == other.uv &&
+         eye == other.eye && external_surface_id == other.external_surface_id &&
+         reprojection == other.reprojection &&
+         MatricesAlmostEqual(transform, other.transform, 1e-5f);
+}
+
+gvr_buffer_viewport* gvr_buffer_viewport_create(gvr_context* /* gvr */) {
+  return new gvr_buffer_viewport;
+}
+
+void gvr_buffer_viewport_destroy(gvr_buffer_viewport** viewport) {
+  if (viewport) {
+    delete *viewport;
+    *viewport = nullptr;
+  }
+}
+
+gvr_rectf gvr_buffer_viewport_get_source_uv(
+    const gvr_buffer_viewport* viewport) {
+  return viewport->uv;
+}
+
+void gvr_buffer_viewport_set_source_uv(gvr_buffer_viewport* viewport,
+                                       gvr_rectf uv) {
+  viewport->uv = uv;
+}
+
+gvr_rectf gvr_buffer_viewport_get_source_fov(
+    const gvr_buffer_viewport* viewport) {
+  return ViewportTransformToFov(viewport->transform);
+}
+
+void gvr_buffer_viewport_set_source_fov(gvr_buffer_viewport* viewport,
+                                        gvr_rectf fov) {
+  viewport->transform = FovToViewportTransform(fov);
+}
+
+gvr_mat4f gvr_buffer_viewport_get_transform(
+    const gvr_buffer_viewport* viewport) {
+  return viewport->transform;
+}
+
+void gvr_buffer_viewport_set_transform(gvr_buffer_viewport* viewport,
+                                       gvr_mat4f transform) {
+  viewport->transform = transform;
+}
+
+int32_t gvr_buffer_viewport_get_target_eye(
+    const gvr_buffer_viewport* viewport) {
+  return viewport->eye;
+}
+
+void gvr_buffer_viewport_set_target_eye(gvr_buffer_viewport* viewport,
+                                        int32_t index) {
+  viewport->eye = index;
+}
+
+int32_t gvr_buffer_viewport_get_source_buffer_index(
+    const gvr_buffer_viewport* viewport) {
+  return viewport->buffer_index;
+}
+
+void gvr_buffer_viewport_set_source_buffer_index(gvr_buffer_viewport* viewport,
+                                                 int32_t buffer_index) {
+  viewport->buffer_index = buffer_index;
+}
+
+int32_t gvr_buffer_viewport_get_external_surface_id(
+    const gvr_buffer_viewport* viewport) {
+  return viewport->external_surface_id;
+}
+
+void gvr_buffer_viewport_set_external_surface_id(gvr_buffer_viewport* viewport,
+                                                 int32_t external_surface_id) {
+  viewport->external_surface_id = external_surface_id;
+}
+
+int32_t gvr_buffer_viewport_get_reprojection(
+    const gvr_buffer_viewport* viewport) {
+  return viewport->reprojection;
+}
+
+void gvr_buffer_viewport_set_reprojection(gvr_buffer_viewport* viewport,
+                                          int32_t reprojection) {
+  viewport->reprojection = static_cast<gvr_reprojection>(reprojection);
+}
+
+bool gvr_buffer_viewport_equal(const gvr_buffer_viewport* a,
+                               const gvr_buffer_viewport* b) {
+  return *a == *b;
+}
+
+gvr_buffer_viewport_list* gvr_buffer_viewport_list_create(
+    const gvr_context* /* gvr */) {
+  return new gvr_buffer_viewport_list;
+}
+
+void gvr_buffer_viewport_list_destroy(
+    gvr_buffer_viewport_list** viewport_list) {
+  if (!viewport_list || !(*viewport_list)) {
+    ALOGW("gvr_buffer_viewport_list_destroy: Invalid list pointer.");
+    return;
+  }
+  delete *viewport_list;
+  *viewport_list = nullptr;
+}
+
+size_t gvr_buffer_viewport_list_get_size(
+    const gvr_buffer_viewport_list* viewport_list) {
+  return viewport_list->viewports.size();
+}
+
+void gvr_buffer_viewport_list_get_item(
+    const gvr_buffer_viewport_list* viewport_list, size_t index,
+    gvr_buffer_viewport* viewport) {
+  *viewport = viewport_list->viewports[index];
+}
+
+void gvr_buffer_viewport_list_set_item(gvr_buffer_viewport_list* viewport_list,
+                                       size_t index,
+                                       const gvr_buffer_viewport* viewport) {
+  if (index < viewport_list->viewports.size())
+    viewport_list->viewports[index] = *viewport;
+  else
+    viewport_list->viewports.push_back(*viewport);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Swapchains and frames
+/////////////////////////////////////////////////////////////////////////////
+
+gvr_buffer_spec* gvr_buffer_spec_create(gvr_context* /* gvr */) {
+  return new gvr_buffer_spec;
+}
+
+void gvr_buffer_spec_destroy(gvr_buffer_spec** spec) {
+  if (spec) {
+    delete *spec;
+    *spec = nullptr;
+  }
+}
+
+gvr_sizei gvr_buffer_spec_get_size(const gvr_buffer_spec* spec) {
+  return spec->size;
+}
+
+void gvr_buffer_spec_set_size(gvr_buffer_spec* spec, gvr_sizei size) {
+  spec->size = size;
+}
+
+int32_t gvr_buffer_spec_get_samples(const gvr_buffer_spec* spec) {
+  return spec->msaa_samples;
+}
+
+void gvr_buffer_spec_set_samples(gvr_buffer_spec* spec, int32_t num_samples) {
+  spec->msaa_samples = num_samples;
+}
+
+void gvr_buffer_spec_set_color_format(gvr_buffer_spec* spec,
+                                      int32_t color_format) {
+  spec->color_format = color_format;
+}
+
+void gvr_buffer_spec_set_depth_stencil_format(gvr_buffer_spec* spec,
+                                              int32_t depth_stencil_format) {
+  spec->depth_stencil_format = depth_stencil_format;
+}
+
+void gvr_buffer_spec_set_z_order(gvr_buffer_spec* spec, int z_order) {
+  spec->z_order = z_order;
+}
+
+void gvr_buffer_spec_set_visibility(gvr_buffer_spec* spec,
+                                    int32_t visibility) {
+  spec->initially_visible = (visibility != GVR_INVISIBLE);
+}
+
+void gvr_buffer_spec_set_blur_behind(gvr_buffer_spec* spec,
+                                     int32_t blur_behind) {
+  spec->blur_behind = (blur_behind != GVR_BLUR_BEHIND_FALSE);
+}
+
+void gvr_buffer::SetDefaults() {
+  spec = gvr_buffer_spec();
+  frame_buffer = 0;
+  color_render_buffer = 0;
+  depth_stencil_render_buffer = 0;
+  requested_size = {-1, -1};
+}
+
+gvr_buffer::gvr_buffer() { SetDefaults(); }
+
+gvr_buffer::gvr_buffer(gvr_context* gvr, const gvr_buffer_spec& spec_in,
+                       GLuint texture_id, GLenum texture_target) {
+  SetDefaults();
+  spec = spec_in;
+
+  glGetError();  // Clear error state
+  glGenFramebuffers(1, &frame_buffer);
+  glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer);
+
+  if (texture_id == 0) {
+    GLenum gl_color_format;
+    if (!GetGlColorFormat(spec.color_format, &gl_color_format)) {
+      ALOGE("Unknown color format: %d", spec.color_format);
+      gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+      FreeGl();
+      return;
+    }
+
+    glGenRenderbuffers(1, &color_render_buffer);
+    glBindRenderbuffer(GL_RENDERBUFFER, color_render_buffer);
+    if (spec.msaa_samples < 2) {
+      glRenderbufferStorage(GL_RENDERBUFFER, gl_color_format, spec.size.width,
+                            spec.size.height);
+    } else {
+      glRenderbufferStorageMultisample(GL_RENDERBUFFER, spec.msaa_samples,
+                                       gl_color_format, spec.size.width,
+                                       spec.size.height);
+    }
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                              GL_RENDERBUFFER, color_render_buffer);
+  } else {
+    if (spec.msaa_samples < 2) {
+      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                             texture_target, texture_id, 0);
+    } else {
+      glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                           texture_target, texture_id, 0,
+                                           spec.msaa_samples);
+    }
+  }
+
+  if (spec.depth_stencil_format != GVR_DEPTH_STENCIL_FORMAT_NONE) {
+    GLenum gl_depth_format;
+    if (!GetGlDepthFormat(spec.depth_stencil_format, &gl_depth_format)) {
+      ALOGE("Unknown depth/stencil format: %d", spec.depth_stencil_format);
+      gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+      FreeGl();
+      return;
+    }
+
+    glGenRenderbuffers(1, &depth_stencil_render_buffer);
+    glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_render_buffer);
+    if (spec.msaa_samples < 2) {
+      glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, spec.size.width,
+                            spec.size.height);
+    } else {
+      glRenderbufferStorageMultisample(GL_RENDERBUFFER, spec.msaa_samples,
+                                       gl_depth_format, spec.size.width,
+                                       spec.size.height);
+    }
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                              GL_RENDERBUFFER, depth_stencil_render_buffer);
+  }
+
+  GLenum gl_error = glGetError();
+  if (gl_error != GL_NO_ERROR) {
+    ALOGE("GL error after creating framebuffer: %d", gl_error);
+    gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+    FreeGl();
+    return;
+  }
+
+  GLenum framebuffer_complete_result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+  if (framebuffer_complete_result != GL_FRAMEBUFFER_COMPLETE) {
+    ALOGE("Framebuffer setup failed. glCheckFramebufferStatus returned %d",
+          framebuffer_complete_result);
+    gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+    FreeGl();
+    return;
+  }
+}
+
+void gvr_buffer::FreeGl() {
+  if (frame_buffer != 0) {
+    glDeleteFramebuffers(1, &frame_buffer);
+    frame_buffer = 0;
+  }
+  if (color_render_buffer != 0) {
+    glDeleteRenderbuffers(1, &color_render_buffer);
+    color_render_buffer = 0;
+  }
+  if (depth_stencil_render_buffer != 0) {
+    glDeleteRenderbuffers(1, &depth_stencil_render_buffer);
+    depth_stencil_render_buffer = 0;
+  }
+}
+
+gvr_buffer::~gvr_buffer() { FreeGl(); }
+
+gvr_buffer::gvr_buffer(gvr_buffer&& other) {
+  spec = other.spec;
+  frame_buffer = other.frame_buffer;
+  color_render_buffer = other.color_render_buffer;
+  depth_stencil_render_buffer = other.depth_stencil_render_buffer;
+  requested_size = other.requested_size;
+  other.SetDefaults();
+}
+
+gvr_buffer& gvr_buffer::operator=(gvr_buffer&& other) {
+  if (this == &other)
+    return *this;
+  spec = other.spec;
+  frame_buffer = other.frame_buffer;
+  color_render_buffer = other.color_render_buffer;
+  depth_stencil_render_buffer = other.depth_stencil_render_buffer;
+  requested_size = other.requested_size;
+  other.SetDefaults();
+  return *this;
+}
+
+gvr_swap_chain* gvr_swap_chain_create(gvr_context* gvr,
+                                      const gvr_buffer_spec** buffers,
+                                      int32_t count) {
+  if (count == 0) {
+    ALOGE("At least one buffer must be requested");
+    gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+    return nullptr;
+  }
+
+  // We only support one buffer, but it's common for gvr apps to use more than
+  // one. Print an error to the log if the app requests more than one buffer,
+  // but continue on. We'll only render the first buffer in that case.
+  if (count > 1) {
+    ALOGE(
+        "Only one buffer is supported but the app requested %d."
+        " Only the first buffer will be rendered.",
+        count);
+  }
+
+  std::unique_ptr<gvr_swap_chain> swap_chain(new gvr_swap_chain(gvr));
+
+  // The first buffer gets a DvrGraphicsContext, which contains the surface we
+  // pass to displayd for rendering.
+  swap_chain->buffers_.push_back(gvr_buffer());
+  swap_chain->buffers_.back().spec = *buffers[0];
+  if (!CreateDvrGraphicsContextAndGvrBuffer(swap_chain.get()))
+    return nullptr;
+
+  // The rest of the buffers, which we don't render for now, get color render
+  // buffers.
+  for (int i = 1; i < count; ++i) {
+    swap_chain->buffers_.push_back(
+        gvr_buffer(gvr, *buffers[i], 0, GL_TEXTURE_2D));
+    if (swap_chain->buffers_.back().frame_buffer == 0)
+      return nullptr;
+  }
+
+  gvr->swap_chains_.push_back(swap_chain.get());
+  return swap_chain.release();
+}
+
+gvr_swap_chain_::~gvr_swap_chain_() {
+  if (context) {
+    auto iter = std::find(std::begin(context->swap_chains_),
+                          std::end(context->swap_chains_), this);
+    if (iter != context->swap_chains_.end())
+      context->swap_chains_.erase(iter);
+  }
+  buffers_.clear();
+  if (graphics_context_ != nullptr)
+    dvrGraphicsContextDestroy(graphics_context_);
+}
+
+void gvr_swap_chain_destroy(gvr_swap_chain** swap_chain) {
+  if (!swap_chain || !(*swap_chain)) {
+    ALOGW("gvr_swap_chain_destroy: Invalid swap chain pointer.");
+    return;
+  }
+  delete *swap_chain;
+  *swap_chain = nullptr;
+}
+
+int32_t gvr_swap_chain_get_buffer_count(const gvr_swap_chain* swap_chain) {
+  return swap_chain ? static_cast<int32_t>(swap_chain->buffers_.size()) : 0;
+}
+
+gvr_sizei gvr_swap_chain_get_buffer_size(gvr_swap_chain* swap_chain,
+                                         int32_t index) {
+  if (!VerifyBufferIndex("gvr_swap_chain_get_buffer_size", swap_chain, index))
+    return gvr_sizei{0, 0};
+
+  gvr_buffer& buf = swap_chain->buffers_[index];
+  if (buf.requested_size != gvr_sizei{-1, -1})
+    return buf.requested_size;
+  else
+    return buf.spec.size;
+}
+
+void gvr_swap_chain_resize_buffer(gvr_swap_chain* swap_chain, int32_t index,
+                                  gvr_sizei size) {
+  if (!VerifyBufferIndex("gvr_swap_chain_resize_buffer", swap_chain, index))
+    return;
+
+  gvr_buffer& buf = swap_chain->buffers_[index];
+  if (size != buf.spec.size)
+    buf.requested_size = size;
+  else
+    buf.requested_size = {-1, -1};
+}
+
+gvr_frame* gvr_swap_chain_acquire_frame(gvr_swap_chain* swap_chain) {
+  if (!swap_chain)
+    return nullptr;
+
+  if (swap_chain->frame_acquired_) {
+    gvr_set_error(swap_chain->context, GVR_ERROR_NO_FRAME_AVAILABLE);
+    return nullptr;
+  }
+
+  // Resize buffers if necessary
+  for (int i = 0; i < static_cast<int>(swap_chain->buffers_.size()); ++i) {
+    gvr_buffer& buf = swap_chain->buffers_[i];
+    if (buf.requested_size != gvr_sizei{-1, -1}) {
+      if (!SwapChainResizeBuffer(swap_chain, i))
+        return nullptr;
+    }
+  }
+
+  // Only call gvr_wait_next_frame() if the app didn't call it already.
+  if (!swap_chain->wait_next_frame_called_by_app_)
+    WaitNextFrame(swap_chain, 0, nullptr, /*called_by_app*/ false);
+
+  int ret = dvrBeginRenderFrame(swap_chain->graphics_context_);
+  if (ret < 0) {
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    return nullptr;
+  }
+
+  swap_chain->frame_acquired_ = true;
+  return GetFrameFromSwapChain(swap_chain);
+}
+
+void gvr_frame_bind_buffer(gvr_frame* frame, int32_t index) {
+  gvr_swap_chain* swap_chain = GetSwapChainForFrame(frame);
+  if (!VerifyBufferIndex("gvr_frame_bind_buffer", swap_chain, index))
+    return;
+  glBindFramebuffer(GL_FRAMEBUFFER, swap_chain->buffers_[index].frame_buffer);
+}
+
+void gvr_frame_unbind(gvr_frame* /* frame */) {
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+gvr_sizei gvr_frame_get_buffer_size(const gvr_frame* frame, int32_t index) {
+  const gvr_swap_chain* swap_chain = GetSwapChainForFrame(frame);
+  if (!VerifyBufferIndex("gvr_frame_get_buffer_size", swap_chain, index))
+    return gvr_sizei{0, 0};
+  return swap_chain->buffers_[index].spec.size;
+}
+
+int32_t gvr_frame_get_framebuffer_object(const gvr_frame* frame,
+                                         int32_t index) {
+  const gvr_swap_chain* swap_chain = GetSwapChainForFrame(frame);
+  if (!VerifyBufferIndex("gvr_frame_get_framebuffer_object", swap_chain, index))
+    return 0;
+  return swap_chain->buffers_[index].frame_buffer;
+}
+
+void gvr_frame_submit(gvr_frame** frame, const gvr_buffer_viewport_list* list,
+                      gvr_mat4f head_space_from_start_space) {
+  if (!frame)
+    return;
+
+  gvr_swap_chain* swap_chain = GetSwapChainForFrame(*frame);
+
+  if (!swap_chain->frame_acquired_) {
+    ALOGE("Frame was never acquired before being submitted");
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    return;
+  }
+
+  *frame = nullptr;
+  swap_chain->frame_acquired_ = false;
+
+  // Currently, support for arbitrary buffer viewport configs is very limited.
+  // We assume that the first two viewports have to be the recommended color
+  // buffer viewports, followed by pairs of external external buffer viewports
+  // for video rendering.
+  gvr_buffer_viewport_list supported_viewports;
+  gvr_get_recommended_buffer_viewports(swap_chain->context,
+                                       &supported_viewports);
+  for (size_t i = 0; i < supported_viewports.viewports.size(); ++i) {
+    if (i >= list->viewports.size() ||
+        supported_viewports.viewports[i] != list->viewports[i]) {
+      ALOGE("Custom viewport configurations are not fully supported.");
+      gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+      return;
+    }
+  }
+
+  for (size_t i = supported_viewports.viewports.size();
+       i < list->viewports.size(); ++i) {
+    int32_t external_surface_id = list->viewports[i].external_surface_id;
+    // Ignore additional custom buffer viewport for now, only those buffer
+    // viewports backed by external surfaces are supported.
+    // TODO(b/31442094, b/31771861, 28954457) Add full GVR buffer viewport
+    // support.
+    if (external_surface_id == GVR_EXTERNAL_SURFACE_ID_NONE)
+      continue;
+
+    auto surface_it = swap_chain->external_surfaces_.find(external_surface_id);
+    if (surface_it == swap_chain->external_surfaces_.end()) {
+      ALOGE("Cannot find external_surface by id: %d.", external_surface_id);
+      gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+      return;
+    }
+
+    // Pass the transfrom matrix of video mesh to displayd.
+    dvrGraphicsVideoMeshSurfacePresent(
+        swap_chain->graphics_context_, surface_it->second->video_surface,
+        list->viewports[i].eye,
+        GvrToEigenMatrix(list->viewports[i].transform).data());
+  }
+
+  float32x4_t pose_orientation, pose_translation;
+  GvrToDvrPose(head_space_from_start_space, &pose_orientation,
+               &pose_translation);
+  int ret = dvrSetEdsPose(swap_chain->graphics_context_, pose_orientation,
+                          pose_translation);
+  if (ret < 0)
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+
+  ret = dvrPresent(swap_chain->graphics_context_);
+  if (ret < 0) {
+    gvr_set_error(swap_chain->context, GVR_ERROR_INTERNAL);
+    return;
+  }
+}
+
+void gvr_bind_default_framebuffer(gvr_context* /* gvr */) {
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Head tracking
+/////////////////////////////////////////////////////////////////////////////
+
+gvr_clock_time_point gvr_get_time_point_now() {
+  return gvr_clock_time_point{GetSystemClockNs()};
+}
+
+gvr_mat4f gvr_get_head_space_from_start_space_rotation(
+    const gvr_context* gvr, const gvr_clock_time_point /* time */) {
+  // TODO(steventhomas): Implement prediction according to the supplied time
+  // value.
+  return gvr->force_6dof_ ? gvr->next_frame_6dof_pose_
+                          : Gvr6dofTo3dof(gvr->next_frame_6dof_pose_);
+}
+
+gvr_mat4f gvr_apply_neck_model(const gvr_context* /* gvr */,
+                               gvr_mat4f head_space_from_start_space_rotation,
+                               float /* factor */) {
+  // TODO(leandrogracia): this needs to be properly implemented.
+  ALOGE("gvr_apply_neck_model not implemented.");
+  return head_space_from_start_space_rotation;
+}
+
+// This is used to turn off sensors to save power. Not relevant for our all in
+// one device.
+void gvr_pause_tracking(gvr_context* /* gvr */) {}
+
+// This is used to turn on sensors. Not relevant for our all in one device.
+void gvr_resume_tracking(gvr_context* /* gvr */) {}
+
+void gvr_reset_tracking(gvr_context* gvr) {
+  // TODO(leandrogracia): this needs to be properly implemented.
+  ALOGE("gvr_reset_tracking not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+void gvr_recenter_tracking(gvr_context* gvr) {
+  // TODO(leandrogracia): this needs to be properly implemented.
+  ALOGE("gvr_recenter_tracking not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Head mounted display
+/////////////////////////////////////////////////////////////////////////////
+
+bool gvr_set_default_viewer_profile(gvr_context* gvr,
+                                    const char* /* viewer_profile_uri */) {
+  // TODO(leandrogracia): this needs to be properly implemented.
+  ALOGE("gvr_set_default_viewer_profile not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+  return false;
+}
+
+void gvr_refresh_viewer_profile(gvr_context* /* gvr */) {}
+
+const char* gvr_get_viewer_vendor(const gvr_context* /* gvr */) {
+  return kViewerVendor;
+}
+
+const char* gvr_get_viewer_model(const gvr_context* /* gvr */) {
+  return kViewerModel;
+}
+
+int32_t gvr_get_viewer_type(const gvr_context* /* gvr */) {
+  // TODO(leandrogracia): this needs to be properly implemented.
+  // In this case, we will probably need to define a new viewer type that
+  // has 6DoF support.
+  return GVR_VIEWER_TYPE_DAYDREAM;
+}
+
+gvr_mat4f gvr_get_eye_from_head_matrix(const gvr_context* gvr,
+                                       const int32_t eye) {
+  float eye_mult = eye == GVR_LEFT_EYE ? 1 : -1;
+  return GvrTranslationMatrix(
+      .5f * eye_mult * gvr->display_metrics_.inter_lens_distance_m, 0, 0);
+}
+
+gvr_recti gvr_get_window_bounds(const gvr_context* gvr) {
+  // Our app windows are always full screen
+  gvr_sizei screen_size = gvr_get_screen_target_size(gvr);
+  return gvr_recti{0, screen_size.width, 0, screen_size.height};
+}
+
+void gvr_compute_distorted_point(const gvr_context* /* gvr */,
+                                 const int32_t /* eye */,
+                                 const gvr_vec2f /* uv_in */,
+                                 gvr_vec2f /* uv_out */[3]) {
+  // TODO(leandrogracia): this needs to be properly implemented.
+  ALOGE("gvr_compute_distorted_point not implemented.");
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// GVR API extension (from gvr_ext.h)
+/////////////////////////////////////////////////////////////////////////////
+
+gvr_frame_schedule* gvr_frame_schedule_create() {
+  return new gvr_frame_schedule;
+}
+
+void gvr_frame_schedule_destroy(gvr_frame_schedule** schedule) {
+  if (!schedule || !(*schedule)) {
+    ALOGW("gvr_frame_schedule_destroy: Invalid frame schedule pointer.");
+    return;
+  }
+  delete *schedule;
+  *schedule = nullptr;
+}
+
+uint32_t gvr_frame_schedule_get_vsync_count(gvr_frame_schedule* schedule) {
+  return schedule->vsync_count;
+}
+
+gvr_clock_time_point gvr_frame_schedule_get_scheduled_finish(
+    gvr_frame_schedule* schedule) {
+  return schedule->scheduled_finish;
+}
+
+void gvr_wait_next_frame(gvr_swap_chain* swap_chain, int64_t start_delay_nanos,
+                         gvr_frame_schedule* out_next_frame_schedule) {
+  WaitNextFrame(swap_chain, start_delay_nanos, out_next_frame_schedule,
+                /*called_by_app*/ true);
+}
+
+gvr_mat4f gvr_get_6dof_head_pose_in_start_space(gvr_context* gvr,
+                                                uint32_t vsync_count) {
+  DvrPoseAsync pose;
+  int ret = dvrPoseGet(gvr->pose_client_, vsync_count, &pose);
+  if (ret < 0) {
+    ALOGW("dvrPoseGet failed: %d", ret);
+    gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+    return GvrIdentityMatrix();
+  }
+
+  return PosefToGvrMatrix(ToPosef(pose));
+}
+
+gvr_mat4f gvr_get_head_space_from_start_space_pose(
+    gvr_context* gvr, const gvr_clock_time_point /* time */) {
+  // TODO(leandrogracia): implement prediction based on the provided time.
+  // We need to do the same for the 3dof version too.
+  return gvr->next_frame_6dof_pose_;
+}
+
+void gvr_swap_chain_set_z_order(const gvr_swap_chain* swap_chain, int z_order) {
+  dvrGraphicsSurfaceSetZOrder(swap_chain->graphics_context_, z_order);
+}
+
+bool gvr_experimental_register_perf_event_callback(
+    gvr_context* gvr, int* /* out_handle */, void* /* user_data */,
+    void (* /* event_callback */)(void*, int, float)) {
+  ALOGE("gvr_experimental_register_perf_event_callback not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+  return false;
+}
+
+bool gvr_experimental_unregister_perf_event_callback(gvr_context* gvr,
+                                                     int /* handle */) {
+  ALOGE("gvr_experimental_unregister_perf_event_callback not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+  return false;
+}
+
+const gvr_analytics* gvr_get_analytics(gvr_context* gvr) {
+  ALOGE("gvr_get_analytics not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+  return nullptr;
+}
+
+const gvr_analytics_sample* gvr_analytics_create_sample(
+    const gvr_analytics* analytics) {
+  ALOGE("gvr_analytics_create_sample not implemented.");
+  return nullptr;
+}
+
+const char* gvr_analytics_sample_get_buffer(const gvr_analytics_sample* sample) {
+  ALOGE("gvr_analytics_sample_get_buffer not implemented.");
+  return nullptr;
+}
+
+size_t gvr_analytics_sample_get_buffer_length(
+    const gvr_analytics_sample* sample) {
+  ALOGE("gvr_analytics_sample_get_buffer_length not implemented.");
+  return 0;
+}
+
+void gvr_analytics_destroy_sample(const gvr_analytics_sample** sample) {
+  ALOGE("gvr_analytics_destroy_sample not implemented.");
+}
+
+bool gvr_user_prefs_get_performance_monitoring_enabled(
+    const gvr_user_prefs* /* user_prefs */) {
+  ALOGW("gvr_user_prefs_get_performance_monitoring_enabled not implemented.");
+  return false;
+}
+
+void gvr_enable_context_sharing(gvr_context* gvr,
+                                gvr_egl_context_listener /* handler */,
+                                void* /* user_data */) {
+  ALOGW("gvr_enable_context_sharing not implemented.");
+  gvr_set_error(gvr, GVR_ERROR_INTERNAL);
+}
+
+gvr_mat4f gvr_get_start_space_from_controller_space_pose(
+    gvr_context* gvr, int controller_id,
+    const gvr_clock_time_point /* time */) {
+  if (controller_id < 0 || controller_id >= kControllerCount) {
+    return GvrIdentityMatrix();
+  }
+
+  // TODO(leandrogracia): implement prediction based on the provided time.
+  // We need to do the same for the 3dof version too.
+  return gvr->next_frame_controller_pose_[controller_id];
+}
+
+gvr_external_surface* gvr_external_surface_create(gvr_context* context) {
+  // A |gvr_external_surface| is bound to a DVR Graphics context at the
+  // moment, which means we need an |gvr_swap_chain| created prior to the call
+  // of |gvr_external_surface_create|. Check whether the current GVR context
+  // has |gvr_swap_chain| created. Fail if there is no swap chain created
+  // already.
+  if (context->swap_chains_.empty()) {
+    ALOGE("gvr_external_surface_create: No swapchain has been created yet.");
+    return nullptr;
+  }
+
+  // In case there are multiple swap chains in the context, the first is
+  // implicitly chosen. Actually, this should not happen as current scanline
+  // racing based GVR implementation only supports single swap chain per GVR
+  // context.
+  if (context->swap_chains_.size() > 1) {
+    ALOGW("gvr_external_surface_create: Multiple swap chains detected. "
+          "Choosing the first one but this may yield unexpected results.");
+  }
+  gvr_swap_chain* swap_chain = context->swap_chains_[0];
+  DvrVideoMeshSurface* video_surface = dvrGraphicsVideoMeshSurfaceCreate(
+      swap_chain->graphics_context_);
+
+  if (video_surface == nullptr) {
+    ALOGE("gvr_external_surface_create: Failed to create video mesh surface.");
+    return nullptr;
+  }
+
+  gvr_external_surface* surface = new gvr_external_surface;
+  surface->id = swap_chain->next_external_surface_id_++;
+  surface->swap_chain = swap_chain;
+  surface->video_surface = video_surface;
+
+  // Insert the surface into a lookup table in swap_chain. This will be
+  // needed to by the external_surface_id in |gvr_buffer_viewport|.
+  swap_chain->external_surfaces_.insert({surface->id, surface});
+  return surface;
+}
+
+void gvr_external_surface_destroy(gvr_external_surface** surface) {
+  if (!surface || !(*surface)) {
+    ALOGW("gvr_external_surface_destroy: Invalid external surface pointer.");
+    return;
+  }
+
+  (*surface)->swap_chain->external_surfaces_.erase((*surface)->id);
+  if ((*surface)->video_surface != nullptr) {
+    dvrGraphicsVideoMeshSurfaceDestroy((*surface)->video_surface);
+  }
+
+  delete *surface;
+  *surface = nullptr;
+}
+
+void* gvr_external_surface_get_surface(const gvr_external_surface* surface) {
+  CHECK(surface->swap_chain != nullptr &&
+        surface->swap_chain->context != nullptr &&
+        surface->swap_chain->context->jni_env_ != nullptr)
+      << "gvr_external_surface_get_surface: Surface must be constructed within "
+      << "a JNIEnv. Check |gvr_create| call.";
+
+  CHECK(surface->video_surface != nullptr)
+      << "gvr_external_surface_get_surface: Invalid surface.";
+
+  std::shared_ptr<android::dvr::ProducerQueue> producer_queue =
+      surface->video_surface->client->GetProducerQueue();
+  std::shared_ptr<android::dvr::BufferHubQueueCore> core =
+      android::dvr::BufferHubQueueCore::Create(producer_queue);
+
+  return android_view_Surface_createFromIGraphicBufferProducer(
+      surface->swap_chain->context->jni_env_,
+      new android::dvr::BufferHubQueueProducer(core));
+}
+
+int32_t gvr_external_surface_get_surface_id(
+    const gvr_external_surface* surface) {
+  return surface->id;
+}
diff --git a/libs/vr/libgvr/shim_gvr_controller.cpp b/libs/vr/libgvr/shim_gvr_controller.cpp
new file mode 100644
index 0000000..54bc270
--- /dev/null
+++ b/libs/vr/libgvr/shim_gvr_controller.cpp
@@ -0,0 +1,168 @@
+#define LOG_TAG "libgvr_controller_shim"
+
+#include <cutils/log.h>
+#include <vr/gvr/capi/include/gvr_controller.h>
+#include <vr/gvr/capi/include/gvr_types.h>
+
+gvr_controller_context* gvr_controller_create_and_init(int32_t options,
+                                                       gvr_context* context) {
+  ALOGE("gvr_controller_create_and_init not implemented.");
+  return nullptr;
+}
+
+gvr_controller_context* gvr_controller_create_and_init_android(
+    JNIEnv* env, jobject android_context, jobject class_loader, int32_t options,
+    gvr_context* context) {
+  ALOGE("gvr_controller_create_and_init_android not implemented.");
+  return nullptr;
+}
+
+void gvr_controller_destroy(gvr_controller_context** api) {
+  ALOGE("gvr_controller_destroy not implemented.");
+}
+
+gvr_controller_state* gvr_controller_state_create() {
+  ALOGE("gvr_controller_state_create not implemented.");
+  return nullptr;
+}
+
+void gvr_controller_state_destroy(gvr_controller_state** state) {
+  ALOGE("gvr_controller_state_destroy not implemented.");
+}
+
+void gvr_controller_state_update(gvr_controller_context* api, int32_t flags,
+                                 gvr_controller_state* out_state) {
+  ALOGE("gvr_controller_state_update not implemented.");
+}
+
+int64_t gvr_controller_state_get_last_button_timestamp(
+    const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_last_button_timestamp not implemented.");
+  return 0;
+}
+
+bool gvr_controller_state_get_button_state(const gvr_controller_state* state,
+                                           int32_t button) {
+  ALOGE("gvr_controller_state_get_button_state not implemented.");
+  return false;
+}
+
+bool gvr_controller_state_get_button_down(const gvr_controller_state* state,
+                                          int32_t button) {
+  ALOGE("gvr_controller_state_get_button_down not implemented.");
+  return false;
+}
+
+bool gvr_controller_state_get_button_up(const gvr_controller_state* state,
+                                        int32_t button) {
+  ALOGE("gvr_controller_state_get_button_up not implemented.");
+  return false;
+}
+
+bool gvr_controller_state_is_touching(const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_is_touching not implemented.");
+  return false;
+}
+
+gvr_vec2f gvr_controller_state_get_touch_pos(
+    const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_touch_pos not implemented.");
+  return {0.0f, 0.0f};
+}
+
+bool gvr_controller_state_get_touch_down(const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_touch_down not implemented.");
+  return false;
+}
+
+bool gvr_controller_state_get_touch_up(const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_touch_up not implemented.");
+  return false;
+}
+
+int64_t gvr_controller_state_get_last_touch_timestamp(
+    const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_last_touch_timestamp not implemented.");
+  return 0;
+}
+
+gvr_quatf gvr_controller_state_get_orientation(
+    const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_orientation not implemented.");
+  return {0.0f, 0.0f, 0.0f, 0.0f};
+}
+
+int64_t gvr_controller_state_get_last_orientation_timestamp(
+    const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_last_orientation_timestamp not implemented.");
+  return 0;
+}
+
+const char* gvr_controller_api_status_to_string(int32_t status) {
+  ALOGE("gvr_controller_api_status_to_string not implemented.");
+  return nullptr;
+}
+
+const char* gvr_controller_connection_state_to_string(int32_t state) {
+  ALOGE("gvr_controller_connection_state_to_string not implemented.");
+  return nullptr;
+}
+
+const char* gvr_controller_button_to_string(int32_t button) {
+  ALOGE("gvr_controller_button_to_string not implemented.");
+  return nullptr;
+}
+
+int32_t gvr_controller_get_default_options() {
+  ALOGE("gvr_controller_get_default_options not implemented.");
+  return 0;
+}
+
+void gvr_controller_pause(gvr_controller_context* api) {
+  ALOGE("gvr_controller_pause not implemented.");
+}
+
+void gvr_controller_resume(gvr_controller_context* api) {
+  ALOGE("gvr_controller_resume not implemented.");
+}
+
+int32_t gvr_controller_state_get_api_status(const gvr_controller_state* state) {
+  return GVR_CONTROLLER_API_OK;
+}
+
+int32_t gvr_controller_state_get_connection_state(
+    const gvr_controller_state* state) {
+  return GVR_CONTROLLER_CONNECTED;
+}
+
+gvr_vec3f gvr_controller_state_get_gyro(const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_gyro not implemented.");
+  return {0.0, 0.0, 0.0};
+}
+
+gvr_vec3f gvr_controller_state_get_accel(const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_accel not implemented.");
+  return {0.0, 0.0, 0.0};
+}
+
+int64_t gvr_controller_state_get_last_gyro_timestamp(
+    const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_last_gyro_timestamp not implemented.");
+  return 0;
+}
+
+int64_t gvr_controller_state_get_last_accel_timestamp(
+    const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_last_accel_timestamp not implemented.");
+  return 0;
+}
+
+bool gvr_controller_state_get_recentered(const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_recentered not implemented.");
+  return false;
+}
+
+bool gvr_controller_state_get_recentering(const gvr_controller_state* state) {
+  ALOGE("gvr_controller_state_get_recentering not implemented.");
+  return false;
+}
diff --git a/libs/vr/libgvr/shim_gvr_private.cpp b/libs/vr/libgvr/shim_gvr_private.cpp
new file mode 100644
index 0000000..6ab6971
--- /dev/null
+++ b/libs/vr/libgvr/shim_gvr_private.cpp
@@ -0,0 +1,234 @@
+#define LOG_TAG "libgvr_shim_private"
+
+#include <cutils/log.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/internal_types.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/src/gvr_private.h>
+
+#include <pdx/rpc/remote_method.h>
+#include "deviceparams/CardboardDevice.nolite.pb.h"
+
+bool gvr_set_async_reprojection_enabled(gvr_context* /* gvr */,
+                                        bool /* enabled */) {
+  return true;
+}
+
+void gvr_on_surface_created_reprojection_thread(gvr_context* /* gvr */) {}
+
+void gvr_render_reprojection_thread(gvr_context* /* gvr */) {}
+
+void gvr_on_pause_reprojection_thread(gvr_context* /* gvr */) {}
+
+void gvr_update_surface_reprojection_thread(
+    gvr_context* /* gvr */, int32_t /* surface_id */, int32_t /* texture_id */,
+    gvr_clock_time_point /* timestamp */, gvr_mat4f /* surface_transform */) {
+  ALOGE("gvr_update_surface_reprojection_thread not implemented");
+}
+
+void gvr_remove_all_surfaces_reprojection_thread(gvr_context* /* gvr */) {
+  ALOGE("gvr_remove_all_surfaces_reprojection_thread not implemented");
+}
+
+void gvr_reconnect_sensors(gvr_context* /* gvr */) {
+  ALOGE("gvr_reconnect_sensors not implemented");
+}
+
+bool gvr_set_viewer_params(gvr_context* gvr,
+                           const void* serialized_viewer_params,
+                           size_t serialized_viewer_params_size_bytes) {
+  std::string serialized_device_params_string(
+      reinterpret_cast<const char*>(serialized_viewer_params),
+      serialized_viewer_params_size_bytes);
+  std::unique_ptr<proto::DeviceParams> device_params(new proto::DeviceParams);
+  if (!device_params->ParseFromString(serialized_device_params_string)) {
+    LOG(ERROR) << "Invalid serialized Cardboard DeviceParams";
+    return false;
+  }
+
+  android::dvr::ViewerParams viewer_params;
+
+  viewer_params.screen_to_lens_distance =
+      device_params->screen_to_lens_distance();
+  viewer_params.inter_lens_distance = device_params->inter_lens_distance();
+  for (int i = 0; i < device_params->left_eye_field_of_view_angles_size();
+       ++i) {
+    viewer_params.left_eye_field_of_view_angles.push_back(
+        device_params->left_eye_field_of_view_angles(i));
+  }
+
+  viewer_params.vertical_alignment =
+      static_cast<android::dvr::ViewerParams::VerticalAlignmentType>(
+          device_params->vertical_alignment());
+  viewer_params.tray_to_lens_distance = device_params->tray_to_lens_distance();
+
+  // TODO(hendrikw) Leave the g and b coefficients empty until we support
+  // chromatic aberration correction.
+  for (int i = 0; i < device_params->distortion_coefficients_size(); ++i) {
+    viewer_params.distortion_coefficients_r.push_back(
+        device_params->distortion_coefficients(i));
+  }
+
+  viewer_params.screen_center_to_lens_distance =
+      viewer_params.inter_lens_distance / 2.0;
+  if (device_params->has_internal()) {
+    for (int i = 0; i < device_params->internal().eye_orientations_size();
+         ++i) {
+      viewer_params.eye_orientations.push_back(
+          static_cast<android::dvr::ViewerParams::EyeOrientation>(
+              device_params->internal().eye_orientations(i)));
+    }
+
+    if (device_params->internal().has_screen_center_to_lens_distance())
+      viewer_params.screen_center_to_lens_distance =
+          device_params->internal().screen_center_to_lens_distance();
+  }
+
+  if (device_params->has_daydream_internal()) {
+    viewer_params.daydream_internal.version =
+        device_params->daydream_internal().version();
+    for (int i = 0;
+         i < device_params->daydream_internal().alignment_markers_size(); ++i) {
+      viewer_params.daydream_internal.alignment_markers.push_back(
+          {device_params->daydream_internal().alignment_markers(i).horizontal(),
+           device_params->daydream_internal().alignment_markers(i).vertical()});
+    }
+  }
+
+  gvr->display_client_->SetViewerParams(viewer_params);
+  return true;
+}
+
+void gvr_set_lens_offset(gvr_context* /* gvr */, gvr_vec2f /* offset */) {
+  ALOGE("gvr_set_lens_offset not implemented");
+}
+
+void gvr_set_display_metrics(gvr_context* /* gvr */,
+                             gvr_sizei /* size_pixels */,
+                             gvr_vec2f /* meters_per_pixel */,
+                             float /* border_size_meters */) {
+  ALOGE("gvr_set_display_metrics not implemented");
+}
+
+void gvr_set_display_output_rotation(gvr_context* /* gvr */,
+                                     int /* display_output_rotation */) {
+  ALOGE("gvr_set_display_output_rotation not implemented");
+}
+
+float gvr_get_border_size_meters(const gvr_context* /* gvr */) {
+  ALOGE("gvr_get_border_size_meters not implemented");
+  return 0.0f;
+}
+
+bool gvr_check_surface_size_changed(gvr_context* /* gvr */) { return false; }
+
+gvr_sizei gvr_get_surface_size(const gvr_context* /* gvr */) {
+  ALOGE("gvr_get_surface_size not implemented");
+  return {0, 0};
+}
+
+void gvr_set_back_gesture_event_handler(gvr_context* /* gvr */,
+                                        event_handler /* handler */,
+                                        void* /* user_data */) {
+  ALOGE("gvr_set_back_gesture_event_handler not implemented");
+}
+
+gvr_tracker_state* gvr_pause_tracking_get_state(gvr_context* /* gvr */) {
+  ALOGE("gvr_pause_tracking_get_state not implemented");
+  return nullptr;
+}
+
+void gvr_resume_tracking_set_state(gvr_context* /* gvr */,
+                                   gvr_tracker_state* /* tracker_state */) {
+  ALOGE("gvr_resume_tracking_set_state not implemented");
+}
+
+void gvr_set_ignore_manual_tracker_pause_resume(gvr_context* /* gvr */,
+                                                bool /* should_ignore */) {
+  ALOGE("gvr_set_ignore_manual_tracker_pause_resume not implemented");
+}
+
+gvr_tracker_state* gvr_tracker_state_create(
+    const char* /* tracker_state_buffer */, size_t /* buf_size */) {
+  ALOGE("gvr_tracker_state_create not implemented");
+  return nullptr;
+}
+
+size_t gvr_tracker_state_get_buffer_size(
+    gvr_tracker_state* /* tracker_state */) {
+  ALOGE("gvr_tracker_state_get_buffer_size not implemented");
+  return 0;
+}
+
+const char* gvr_tracker_state_get_buffer(
+    gvr_tracker_state* /* tracker_state */) {
+  ALOGE("gvr_tracker_state_get_buffer not implemented");
+  return nullptr;
+}
+
+void gvr_tracker_state_destroy(gvr_tracker_state** /* tracker_state */) {
+  ALOGE("gvr_tracker_state_destroy not implemented");
+}
+
+gvr_display_synchronizer* gvr_display_synchronizer_create() {
+  // We don't actually support (or need) any of the synchronizer functionality,
+  // but if we return null here the gvr setup code in the app fails. Instead
+  // return a dummy object that does nothing, which allows gvr apps to work.
+  return new gvr_display_synchronizer;
+}
+
+void gvr_display_synchronizer_destroy(gvr_display_synchronizer** synchronizer) {
+  if (synchronizer) {
+    delete *synchronizer;
+    *synchronizer = nullptr;
+  }
+}
+
+void gvr_display_synchronizer_reset(
+    gvr_display_synchronizer* /* synchronizer */,
+    int64_t /* expected_interval_nanos */, int64_t /* vsync_offset_nanos */) {}
+
+void gvr_display_synchronizer_update(
+    gvr_display_synchronizer* /* synchronizer */,
+    gvr_clock_time_point /* vsync_time */, int32_t /* rotation */) {}
+
+void gvr_set_display_synchronizer(
+    gvr_context* /* gvr */, gvr_display_synchronizer* /* synchronizer */) {}
+
+void gvr_set_error(gvr_context* gvr, int32_t error_code) {
+  if (gvr->last_error_ != GVR_ERROR_NONE) {
+    ALOGW("Overwriting existing error code: %d (%s)", gvr->last_error_,
+          gvr_get_error_string(gvr->last_error_));
+  }
+  gvr->last_error_ = error_code;
+}
+
+void gvr_pause(gvr_context* gvr) {
+  if (gvr == nullptr) {
+    ALOGW("gvr_pause called with a null gvr_context. This is a bug.");
+    return;
+  }
+  for (gvr_swap_chain* swap_chain : gvr->swap_chains_) {
+    if (swap_chain->graphics_context_)
+      dvrGraphicsSurfaceSetVisible(swap_chain->graphics_context_, 0);
+  }
+}
+
+void gvr_resume(gvr_context* gvr) {
+  if (gvr == nullptr) {
+    ALOGW("gvr_resume called with a null gvr_context. This is a bug.");
+    return;
+  }
+  for (gvr_swap_chain* swap_chain : gvr->swap_chains_) {
+    if (swap_chain->graphics_context_)
+      dvrGraphicsSurfaceSetVisible(swap_chain->graphics_context_, 1);
+  }
+}
+
+void gvr_dump_debug_data(gvr_context* /* gvr */) {}
+
+bool gvr_using_vr_display_service(gvr_context* /* gvr */) { return true; }
+
+void gvr_request_context_sharing(gvr_context* /* gvr */,
+                                 gvr_egl_context_listener /* handler */,
+                                 void* /* user_data */) {}
diff --git a/libs/vr/libimageio/Android.mk b/libs/vr/libimageio/Android.mk
new file mode 100644
index 0000000..b3b88ac
--- /dev/null
+++ b/libs/vr/libimageio/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	image_io.cpp \
+	image_io_png.cpp \
+	image_io_ppm.cpp
+
+includeFiles := \
+  $(LOCAL_PATH)/include
+
+sharedLibraries := \
+	libcutils \
+	libpng
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES += $(includeFiles)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_CFLAGS := -Wall -Wextra
+LOCAL_MODULE := libimageio
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
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..009cad4
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_base.h
@@ -0,0 +1,56 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_BASE_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_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_DREAMOS_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..3c0f2a5
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
@@ -0,0 +1,39 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
+
+// This header acts as cutils/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 <cutils/log.h>
+#endif  // LOG_TO_STDERR
+
+#endif  // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_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..e3b19db
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_png.h
@@ -0,0 +1,24 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PNG_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_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_DREAMOS_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..00264bd
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_ppm.h
@@ -0,0 +1,28 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PPM_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_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_DREAMOS_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..c318628
--- /dev/null
+++ b/libs/vr/libpdx/client.cpp
@@ -0,0 +1,290 @@
+#include "pdx/client.h"
+
+#define LOG_TAG "ServiceFramework"
+#include <log/log.h>
+
+#include <pdx/trace.h>
+#include "errno_guard.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");
+  ErrnoGuard errno_guard;
+
+  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");
+  ErrnoGuard errno_guard;
+
+  auto status = CheckReconnect();
+  if (!status)
+    return status;
+
+  status = channel_->SendImpulse(opcode, buffer, length);
+  CheckDisconnect(status);
+  return status;
+}
+
+void Client::Close(int error) {
+  ErrnoGuard errno_guard;
+  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);
+}
+
+FileReference Transaction::PushFileHandle(const LocalHandle& handle) {
+  return client_.CheckReconnect() && EnsureStateAllocated()
+             ? client_.GetChannel()->PushFileHandle(state_, handle)
+             : -1;
+}
+
+FileReference Transaction::PushFileHandle(const BorrowedHandle& handle) {
+  return client_.CheckReconnect() && EnsureStateAllocated()
+             ? client_.GetChannel()->PushFileHandle(state_, handle)
+             : -1;
+}
+
+FileReference Transaction::PushFileHandle(const RemoteHandle& handle) {
+  return handle.Get();
+}
+
+ChannelReference Transaction::PushChannelHandle(
+    const LocalChannelHandle& handle) {
+  return client_.CheckReconnect() && EnsureStateAllocated()
+             ? client_.GetChannel()->PushChannelHandle(state_, handle)
+             : -1;
+}
+
+ChannelReference Transaction::PushChannelHandle(
+    const BorrowedChannelHandle& handle) {
+  return client_.CheckReconnect() && EnsureStateAllocated()
+             ? client_.GetChannel()->PushChannelHandle(state_, handle)
+             : -1;
+}
+
+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..f1fb6d1
--- /dev/null
+++ b/libs/vr/libpdx/client_tests.cpp
@@ -0,0 +1,566 @@
+#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}));
+
+  EXPECT_CALL(*mock_channel(),
+              PushFileHandle(kTransactionState, A<const BorrowedHandle&>()))
+      .WillOnce(Return(2));
+  EXPECT_EQ(2, transaction_.PushFileHandle(BorrowedHandle{-1}));
+
+  EXPECT_EQ(3, transaction_.PushFileHandle(RemoteHandle{3}));
+
+  EXPECT_CALL(
+      *mock_channel(),
+      PushChannelHandle(kTransactionState, A<const LocalChannelHandle&>()))
+      .WillOnce(Return(11));
+  EXPECT_EQ(11, transaction_.PushChannelHandle(LocalChannelHandle{nullptr, 1}));
+
+  EXPECT_CALL(
+      *mock_channel(),
+      PushChannelHandle(kTransactionState, A<const BorrowedChannelHandle&>()))
+      .WillOnce(Return(12));
+  EXPECT_EQ(12, transaction_.PushChannelHandle(BorrowedChannelHandle{2}));
+
+  EXPECT_EQ(13, transaction_.PushChannelHandle(RemoteChannelHandle{13}));
+}
+
+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/errno_guard.h b/libs/vr/libpdx/errno_guard.h
new file mode 100644
index 0000000..fc7dfdf
--- /dev/null
+++ b/libs/vr/libpdx/errno_guard.h
@@ -0,0 +1,34 @@
+#ifndef ANDROID_PDX_ERRNO_GUARD_H_
+#define ANDROID_PDX_ERRNO_GUARD_H_
+
+#include <errno.h>
+
+namespace android {
+namespace pdx {
+
+// Automatically saves and restores the system errno for API implementations to
+// prevent internal use errno from affecting API callers.
+class ErrnoGuard {
+ public:
+  ErrnoGuard() : saved_errno_(errno) {}
+  ~ErrnoGuard() { errno = saved_errno_; }
+
+  int saved_errno() const { return saved_errno_; }
+
+ private:
+  int saved_errno_;
+
+  ErrnoGuard(const ErrnoGuard&) = delete;
+  void operator=(const ErrnoGuard&) = delete;
+};
+
+// Checks |return_code| and returns either it or the negated system errno based
+// on the return code value.
+inline int ReturnCodeOrError(int return_code) {
+  return return_code < 0 ? -errno : return_code;
+}
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_ERRNO_GUARD_H_
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..a590087
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/client.h
@@ -0,0 +1,300 @@
+#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
+  FileReference PushFileHandle(const LocalHandle& handle) override;
+  FileReference PushFileHandle(const BorrowedHandle& handle) override;
+  FileReference PushFileHandle(const RemoteHandle& handle) override;
+  ChannelReference PushChannelHandle(const LocalChannelHandle& handle) override;
+  ChannelReference PushChannelHandle(
+      const BorrowedChannelHandle& handle) override;
+  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..0cb6e40
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/message_writer.h
@@ -0,0 +1,38 @@
+#ifndef ANDROID_PDX_MESSAGE_WRITER_H_
+#define ANDROID_PDX_MESSAGE_WRITER_H_
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+
+class OutputResourceMapper {
+ public:
+  virtual FileReference PushFileHandle(const LocalHandle& handle) = 0;
+  virtual FileReference PushFileHandle(const BorrowedHandle& handle) = 0;
+  virtual FileReference PushFileHandle(const RemoteHandle& handle) = 0;
+  virtual ChannelReference PushChannelHandle(
+      const LocalChannelHandle& handle) = 0;
+  virtual ChannelReference PushChannelHandle(
+      const BorrowedChannelHandle& handle) = 0;
+  virtual 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..3c513d7
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_message_writer.h
@@ -0,0 +1,32 @@
+#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, FileReference(const LocalHandle& handle));
+  MOCK_METHOD1(PushFileHandle, FileReference(const BorrowedHandle& handle));
+  MOCK_METHOD1(PushFileHandle, FileReference(const RemoteHandle& handle));
+  MOCK_METHOD1(PushChannelHandle,
+               ChannelReference(const LocalChannelHandle& handle));
+  MOCK_METHOD1(PushChannelHandle,
+               ChannelReference(const BorrowedChannelHandle& handle));
+  MOCK_METHOD1(PushChannelHandle,
+               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..ead74d5
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/mock_service_endpoint.h
@@ -0,0 +1,66 @@
+#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, int(Service* service));
+  MOCK_METHOD2(SetChannel, int(int channel_id, Channel* channel));
+  MOCK_METHOD1(CloseChannel, int(int channel_id));
+  MOCK_METHOD3(ModifyChannelEvents,
+               int(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(DefaultHandleMessage, int(const MessageInfo& info));
+  MOCK_METHOD1(MessageReceive, int(Message* message));
+  MOCK_METHOD2(MessageReply, int(Message* message, int return_code));
+  MOCK_METHOD2(MessageReplyFd, int(Message* message, unsigned int push_fd));
+  MOCK_METHOD2(MessageReplyChannelHandle,
+               int(Message* message, const LocalChannelHandle& handle));
+  MOCK_METHOD2(MessageReplyChannelHandle,
+               int(Message* message, const BorrowedChannelHandle& handle));
+  MOCK_METHOD2(MessageReplyChannelHandle,
+               int(Message* message, const RemoteChannelHandle& handle));
+  MOCK_METHOD3(ReadMessageData, ssize_t(Message* message, const iovec* vector,
+                                        size_t vector_length));
+  MOCK_METHOD3(WriteMessageData, ssize_t(Message* message, const iovec* vector,
+                                         size_t vector_length));
+  MOCK_METHOD2(PushFileHandle,
+               FileReference(Message* message, const LocalHandle& handle));
+  MOCK_METHOD2(PushFileHandle,
+               FileReference(Message* message, const BorrowedHandle& handle));
+  MOCK_METHOD2(PushFileHandle,
+               FileReference(Message* message, const RemoteHandle& handle));
+  MOCK_METHOD2(PushChannelHandle,
+               ChannelReference(Message* message,
+                                const LocalChannelHandle& handle));
+  MOCK_METHOD2(PushChannelHandle,
+               ChannelReference(Message* message,
+                                const BorrowedChannelHandle& handle));
+  MOCK_METHOD2(PushChannelHandle,
+               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, int());
+};
+
+}  // 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..5fdad72
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/function_traits.h
@@ -0,0 +1,55 @@
+#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 <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..49bee40
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method.h
@@ -0,0 +1,550 @@
+#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>
+
+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 int ret = message.ReplyError(error_code);
+  ALOGE_IF(ret < 0, "RemoteMethodError: Failed to reply to message: %s",
+           strerror(-ret));
+}
+
+// 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 int ret = message.Reply(return_value);
+  ALOGE_IF(ret < 0, "RemoteMethodReturn: Failed to reply to message: %s",
+           strerror(-ret));
+}
+
+// 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);
+
+  int ret;
+  auto size = message.Write(payload.Data(), payload.Size());
+  if (size < static_cast<decltype(size)>(payload.Size()))
+    ret = message.ReplyError(EIO);
+  else
+    ret = message.Reply(0);
+  ALOGE_IF(ret < 0, "RemoteMethodReturn: Failed to reply to message: %s",
+           strerror(-ret));
+}
+
+// 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);
+
+  auto size = message.Read(payload.Data(), payload.Size());
+  if (size < 0) {
+    RemoteMethodError(message, -size);
+    return;
+  }
+
+  payload.Resize(size);
+
+  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 check. Overload for int return types.
+template <typename RemoteMethodType, typename Class, typename... Args,
+          typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+                          int (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);
+
+  auto size = message.Read(payload.Data(), payload.Size());
+  if (size < 0) {
+    RemoteMethodError(message, -size);
+    return;
+  }
+
+  payload.Resize(size);
+
+  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 check. Overload for FileHandle return types.
+template <typename RemoteMethodType, FileHandleMode Mode, typename Class,
+          typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+                          FileHandle<Mode> (Class::*method)(Message&, Args...),
+                          Message& message,
+                          std::size_t max_capacity = InitialBufferCapacity) {
+  using Signature =
+      typename RemoteMethodType::template RewriteSignature<FileHandle<Mode>,
+                                                           Args...>;
+  rpc::ServicePayload<ReceiveBuffer> payload(message);
+  payload.Resize(max_capacity);
+
+  auto size = message.Read(payload.Data(), payload.Size());
+  if (size < 0) {
+    RemoteMethodError(message, -size);
+    return;
+  }
+
+  payload.Resize(size);
+
+  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 check. Overload for ChannelHandle return types.
+template <typename RemoteMethodType, ChannelHandleMode Mode, typename Class,
+          typename... Args, typename = EnableIfNotVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(
+    Class& instance, ChannelHandle<Mode> (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);
+
+  auto size = message.Read(payload.Data(), payload.Size());
+  if (size < 0) {
+    RemoteMethodError(message, -size);
+    return;
+  }
+
+  payload.Resize(size);
+
+  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 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);
+
+  auto size = message.Read(payload.Data(), payload.Size());
+  if (size < 0) {
+    RemoteMethodError(message, -size);
+    return;
+  }
+
+  payload.Resize(size);
+
+  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);
+}
+
+#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 int return type.
+template <typename RemoteMethodType, typename Class,
+          typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance, int (Class::*method)(Message&),
+                          Message& message) {
+  const int 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 FileHandle return type.
+template <typename RemoteMethodType, typename Class, FileHandleMode Mode,
+          typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+                          FileHandle<Mode> (Class::*method)(Message&),
+                          Message& message) {
+  FileHandle<Mode> 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 ChannelHandle return types.
+template <typename RemoteMethodType, typename Class, ChannelHandleMode Mode,
+          typename = EnableIfVoidMethod<RemoteMethodType>>
+void DispatchRemoteMethod(Class& instance,
+                          ChannelHandle<Mode> (Class::*method)(Message&),
+                          Message& message) {
+  ChannelHandle<Mode> 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 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);
+}
+#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..de9a3cc
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/remote_method_type.h
@@ -0,0 +1,67 @@
+#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 <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..fccd028
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/serialization.h
@@ -0,0 +1,1996 @@
+#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>
+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 = EnableIfHasSerializableMembers<T>>
+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>
+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 = EnableIfHasSerializableMembers<T>>
+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 FileReference value =
+      writer->GetOutputResourceMapper()->PushFileHandle(fd);
+  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 ChannelReference value =
+      writer->GetOutputResourceMapper()->PushChannelHandle(handle);
+  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>
+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 = EnableIfHasSerializableMembers<T>>
+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..09789e5
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -0,0 +1,693 @@
+#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<T, U> {};
+template <typename T, typename First, typename... Rest>
+struct HasType<T, First, Rest...>
+    : std::integral_constant<
+          bool, std::is_same<T, First>::value || HasType<T, Rest...>::value> {};
+
+template <typename T, typename... Types>
+using HasTypeIgnoreRef =
+    HasType<typename std::remove_reference<T>::type, Types...>;
+
+// 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<HasTypeIgnoreRef<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<!HasTypeIgnoreRef<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<!HasTypeIgnoreRef<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<HasTypeIgnoreRef<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 TypeTagIgnoreRef = TypeTag<typename std::remove_reference<T>::type>;
+  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 T>
+  using HasTypeIgnoreRef = detail::HasTypeIgnoreRef<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<HasTypeIgnoreRef<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_, TypeTagIgnoreRef<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(TypeTagIgnoreRef<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(TypeTag<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(TypeTag<T>{});
+    else
+      return nullptr;
+  }
+  template <typename T>
+  const T* get() const {
+    if (is<T>())
+      return &value_.template get(TypeTag<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<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..029e6bf
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service.h
@@ -0,0 +1,703 @@
+#ifndef ANDROID_PDX_SERVICE_H_
+#define ANDROID_PDX_SERVICE_H_
+
+#include <cutils/log.h>
+#include <errno.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.
+   */
+  ssize_t ReadVector(const iovec* vector, size_t vector_length);
+  ssize_t Read(void* buffer, size_t length);
+  ssize_t WriteVector(const iovec* vector, size_t vector_length);
+  ssize_t Write(const void* buffer, size_t length);
+
+  template <size_t N>
+  inline ssize_t ReadVector(const iovec (&vector)[N]) {
+    return ReadVector(vector, N);
+  }
+
+  template <size_t N>
+  inline ssize_t WriteVector(const iovec (&vector)[N]) {
+    return WriteVector(vector, N);
+  }
+
+  // OutputResourceMapper
+  FileReference PushFileHandle(const LocalHandle& handle) override;
+  FileReference PushFileHandle(const BorrowedHandle& handle) override;
+  FileReference PushFileHandle(const RemoteHandle& handle) override;
+  ChannelReference PushChannelHandle(const LocalChannelHandle& handle) override;
+  ChannelReference PushChannelHandle(
+      const BorrowedChannelHandle& handle) override;
+  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.
+   */
+  int Reply(int return_code);
+  int ReplyError(unsigned error);
+  int ReplyFileDescriptor(unsigned int fd);
+  int Reply(const LocalHandle& handle);
+  int Reply(const BorrowedHandle& handle);
+  int Reply(const RemoteHandle& handle);
+  int Reply(const LocalChannelHandle& handle);
+  int Reply(const BorrowedChannelHandle& handle);
+  int Reply(const RemoteChannelHandle& handle);
+
+  template <typename T>
+  inline int Reply(const Status<T>& status) {
+    return status ? Reply(status.get()) : ReplyError(status.error());
+  }
+
+  /*
+   * Update the channel event bits with the given clear and set masks.
+   */
+  int 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;
+  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.
+   */
+  int 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.
+   */
+  int 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.
+   */
+  int 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 int 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.
+   */
+  int 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().
+   */
+  int 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.
+   */
+  int ReceiveAndDispatch();
+
+ private:
+  friend class Message;
+
+  bool 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 {                                                                        \
+    int __ret = message.ReplyError(error);                                    \
+    CHECK_ERROR(__ret < 0, error_label,                                       \
+                PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+                strerror(-__ret));                                            \
+    goto error_label;                                                         \
+  } while (0)
+
+#define REPLY_ERROR_RETURN(message, error, ...)                          \
+  do {                                                                   \
+    int __ret = message.ReplyError(error);                               \
+    ALOGE_IF(__ret < 0,                                                  \
+             PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+             strerror(-__ret));                                          \
+    return __VA_ARGS__;                                                  \
+  } while (0)
+
+#define REPLY_MESSAGE(message, message_return_code, error_label)              \
+  do {                                                                        \
+    int __ret = message.Reply(message_return_code);                           \
+    CHECK_ERROR(__ret < 0, error_label,                                       \
+                PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+                strerror(-__ret));                                            \
+    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 {                                                                   \
+    int __ret = message.Reply(message_return_code);                      \
+    ALOGE_IF(__ret < 0,                                                  \
+             PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+             strerror(-__ret));                                          \
+    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 {                                                                        \
+    int __ret = message.ReplyFileDescriptor(push_fd);                         \
+    CHECK_ERROR(__ret < 0, error_label,                                       \
+                PDX_ERROR_PREFIX " Failed to reply to message because: %s\n", \
+                strerror(-__ret));                                            \
+    goto error_label;                                                         \
+  } while (0)
+
+#define REPLY_FD_RETURN(message, push_fd, ...)                           \
+  do {                                                                   \
+    int __ret = message.ReplyFileDescriptor(push_fd);                    \
+    ALOGE_IF(__ret < 0,                                                  \
+             PDX_ERROR_PREFIX " Failed to reply to message because: %s", \
+             strerror(-__ret));                                          \
+    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..613be7c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/service_endpoint.h
@@ -0,0 +1,149 @@
+#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 int SetService(Service* service) = 0;
+
+  // Set the channel context for the given channel.
+  virtual int 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 int CloseChannel(int channel_id) = 0;
+
+  // Update the event bits for the given channel (given by id), using the
+  // given clear and set masks.
+  virtual int 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;
+
+  // The default message handler. It is important that all messages
+  // (eventually) get a reply. This method should be called for any unrecognized
+  // opcodes or otherwise unhandled messages to prevent erroneous requests from
+  // blocking indefinitely.
+  virtual int DefaultHandleMessage(const MessageInfo& info) = 0;
+
+  // Receives a message on the given endpoint file descriptor.
+  virtual int MessageReceive(Message* message) = 0;
+
+  // Replies to the message with a return code.
+  virtual int MessageReply(Message* message, int return_code) = 0;
+
+  // Replies to the message with a file descriptor.
+  virtual int MessageReplyFd(Message* message, unsigned int push_fd) = 0;
+
+  // Replies to the message with a local channel handle.
+  virtual int MessageReplyChannelHandle(Message* message,
+                                        const LocalChannelHandle& handle) = 0;
+
+  // Replies to the message with a borrowed local channel handle.
+  virtual int MessageReplyChannelHandle(
+      Message* message, const BorrowedChannelHandle& handle) = 0;
+
+  // Replies to the message with a remote channel handle.
+  virtual int MessageReplyChannelHandle(Message* message,
+                                        const RemoteChannelHandle& handle) = 0;
+
+  // Reads message data into an array of memory buffers.
+  virtual ssize_t ReadMessageData(Message* message, const iovec* vector,
+                                  size_t vector_length) = 0;
+
+  // Sends reply data for message.
+  virtual ssize_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 FileReference PushFileHandle(Message* message,
+                                       const LocalHandle& handle) = 0;
+  virtual FileReference PushFileHandle(Message* message,
+                                       const BorrowedHandle& handle) = 0;
+  virtual FileReference PushFileHandle(Message* message,
+                                       const RemoteHandle& handle) = 0;
+  virtual ChannelReference PushChannelHandle(
+      Message* message, const LocalChannelHandle& handle) = 0;
+  virtual ChannelReference PushChannelHandle(
+      Message* message, const BorrowedChannelHandle& handle) = 0;
+  virtual 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 int 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..ca2832c
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/status.h
@@ -0,0 +1,168 @@
+#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 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); }
+  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..c8c717c
--- /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:
+  FileReference PushFileHandle(const LocalHandle& handle) override {
+    return handle.Get();
+  }
+
+  FileReference PushFileHandle(const BorrowedHandle& handle) override {
+    return handle.Get();
+  }
+
+  FileReference PushFileHandle(const RemoteHandle& handle) override {
+    return handle.Get();
+  }
+
+  ChannelReference PushChannelHandle(
+      const LocalChannelHandle& handle) override {
+    return handle.value();
+  }
+
+  ChannelReference PushChannelHandle(
+      const BorrowedChannelHandle& handle) override {
+    return handle.value();
+  }
+
+  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
+  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();
+    }
+  }
+
+  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();
+    }
+  }
+
+  FileReference PushFileHandle(const RemoteHandle& handle) override {
+    return handle.Get();
+  }
+
+  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();
+    }
+  }
+
+  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();
+    }
+  }
+
+  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..0053af8
--- /dev/null
+++ b/libs/vr/libpdx/service.cpp
@@ -0,0 +1,680 @@
+#define LOG_TAG "ServiceFramework"
+#include "pdx/service.h"
+
+#include <cutils/log.h>
+#include <fcntl.h>
+#include <utils/misc.h>
+
+#include <algorithm>
+#include <cstdint>
+
+#include <pdx/trace.h>
+#include "errno_guard.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->endpoint()->DefaultHandleMessage(info_);
+    }
+    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);
+}
+
+ssize_t Message::ReadVector(const struct iovec* vector, size_t vector_length) {
+  PDX_TRACE_NAME("Message::ReadVector");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    const ssize_t ret =
+        svc->endpoint()->ReadMessageData(this, vector, vector_length);
+    return ReturnCodeOrError(ret);
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+ssize_t Message::Read(void* buffer, size_t length) {
+  PDX_TRACE_NAME("Message::Read");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    const struct iovec vector = {buffer, length};
+    const ssize_t ret = svc->endpoint()->ReadMessageData(this, &vector, 1);
+    return ReturnCodeOrError(ret);
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+ssize_t Message::WriteVector(const struct iovec* vector, size_t vector_length) {
+  PDX_TRACE_NAME("Message::WriteVector");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    const ssize_t ret =
+        svc->endpoint()->WriteMessageData(this, vector, vector_length);
+    return ReturnCodeOrError(ret);
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+ssize_t Message::Write(const void* buffer, size_t length) {
+  PDX_TRACE_NAME("Message::Write");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    const struct iovec vector = {const_cast<void*>(buffer), length};
+    const ssize_t ret = svc->endpoint()->WriteMessageData(this, &vector, 1);
+    return ReturnCodeOrError(ret);
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+FileReference Message::PushFileHandle(const LocalHandle& handle) {
+  PDX_TRACE_NAME("Message::PushFileHandle");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+FileReference Message::PushFileHandle(const BorrowedHandle& handle) {
+  PDX_TRACE_NAME("Message::PushFileHandle");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+FileReference Message::PushFileHandle(const RemoteHandle& handle) {
+  PDX_TRACE_NAME("Message::PushFileHandle");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+ChannelReference Message::PushChannelHandle(const LocalChannelHandle& handle) {
+  PDX_TRACE_NAME("Message::PushChannelHandle");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+ChannelReference Message::PushChannelHandle(
+    const BorrowedChannelHandle& handle) {
+  PDX_TRACE_NAME("Message::PushChannelHandle");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+ChannelReference Message::PushChannelHandle(const RemoteChannelHandle& handle) {
+  PDX_TRACE_NAME("Message::PushChannelHandle");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
+  } else {
+    return -ESHUTDOWN;
+  }
+}
+
+bool Message::GetFileHandle(FileReference ref, LocalHandle* handle) {
+  PDX_TRACE_NAME("Message::GetFileHandle");
+  auto svc = service_.lock();
+  if (!svc)
+    return false;
+
+  if (ref >= 0) {
+    ErrnoGuard errno_guard;
+    *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) {
+    ErrnoGuard errno_guard;
+    *handle = svc->endpoint()->GetChannelHandle(this, ref);
+    if (!handle->valid())
+      return false;
+  } else {
+    *handle = LocalChannelHandle{nullptr, ref};
+  }
+  return true;
+}
+
+int Message::Reply(int return_code) {
+  PDX_TRACE_NAME("Message::Reply");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    const int ret = svc->endpoint()->MessageReply(this, return_code);
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::ReplyFileDescriptor(unsigned int fd) {
+  PDX_TRACE_NAME("Message::ReplyFileDescriptor");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    const int ret = svc->endpoint()->MessageReplyFd(this, fd);
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::ReplyError(unsigned error) {
+  PDX_TRACE_NAME("Message::ReplyError");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    const int ret = svc->endpoint()->MessageReply(this, -error);
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::Reply(const LocalHandle& handle) {
+  PDX_TRACE_NAME("Message::ReplyFileHandle");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    int ret;
+
+    if (handle)
+      ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+    else
+      ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::Reply(const BorrowedHandle& handle) {
+  PDX_TRACE_NAME("Message::ReplyFileHandle");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    int ret;
+
+    if (handle)
+      ret = svc->endpoint()->MessageReplyFd(this, handle.Get());
+    else
+      ret = svc->endpoint()->MessageReply(this, handle.Get());
+
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::Reply(const RemoteHandle& handle) {
+  PDX_TRACE_NAME("Message::ReplyFileHandle");
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    const int ret = svc->endpoint()->MessageReply(this, handle.Get());
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::Reply(const LocalChannelHandle& handle) {
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::Reply(const BorrowedChannelHandle& handle) {
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::Reply(const RemoteChannelHandle& handle) {
+  auto svc = service_.lock();
+  if (!replied_ && svc) {
+    ErrnoGuard errno_guard;
+    const int ret = svc->endpoint()->MessageReplyChannelHandle(this, handle);
+    replied_ = ret == 0;
+    return ReturnCodeOrError(ret);
+  } else {
+    return -EINVAL;
+  }
+}
+
+int Message::ModifyChannelEvents(int clear_mask, int set_mask) {
+  PDX_TRACE_NAME("Message::ModifyChannelEvents");
+  if (auto svc = service_.lock()) {
+    ErrnoGuard errno_guard;
+    const int ret =
+        svc->endpoint()->ModifyChannelEvents(info_.cid, clear_mask, set_mask);
+    return ReturnCodeOrError(ret);
+  } else {
+    return -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(); }
+
+void Message::SetChannel(const std::shared_ptr<Channel>& chan) {
+  channel_ = chan;
+
+  if (auto svc = service_.lock())
+    svc->SetChannel(info_.cid, chan);
+}
+
+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 int ret = endpoint_->SetService(this);
+  ALOGE_IF(ret < 0, "Failed to set service context because: %s",
+           strerror(-ret));
+}
+
+Service::~Service() {
+  if (endpoint_) {
+    const int ret = endpoint_->SetService(nullptr);
+    ALOGE_IF(ret < 0, "Failed to clear service context because: %s",
+             strerror(-ret));
+  }
+}
+
+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*/) {}
+
+int Service::SetChannel(int channel_id,
+                        const std::shared_ptr<Channel>& channel) {
+  PDX_TRACE_NAME("Service::SetChannel");
+  ErrnoGuard errno_guard;
+  std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+  const int ret = endpoint_->SetChannel(channel_id, channel.get());
+  if (ret == -1) {
+    ALOGE("%s::SetChannel: Failed to set channel context: %s\n", name_.c_str(),
+          strerror(errno));
+
+    // 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 (errno == ENOENT)
+      channels_.erase(channel_id);
+
+    return ReturnCodeOrError(ret);
+  }
+
+  if (channel != nullptr)
+    channels_[channel_id] = channel;
+  else
+    channels_.erase(channel_id);
+
+  return ret;
+}
+
+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;
+}
+
+int Service::CloseChannel(int channel_id) {
+  PDX_TRACE_NAME("Service::CloseChannel");
+  ErrnoGuard errno_guard;
+  std::lock_guard<std::mutex> autolock(channels_mutex_);
+
+  const int ret = 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 ReturnCodeOrError(ret);
+}
+
+int 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");
+  ErrnoGuard errno_guard;
+
+  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");
+  ErrnoGuard errno_guard;
+
+  // 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 ""; }
+
+int Service::HandleMessage(Message& message) {
+  return DefaultHandleMessage(message);
+}
+
+void Service::HandleImpulse(Message& /*impulse*/) {}
+
+bool 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));
+      message.Reply(0);
+      return true;
+    }
+
+    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);
+      message.Reply(0);
+      return true;
+    }
+
+    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();
+      message.Reply(0);
+      return true;
+
+    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 ssize_t bytes_written =
+          message.Write(response.data(), response_size);
+      if (bytes_written < static_cast<ssize_t>(response_size))
+        message.ReplyError(EIO);
+      else
+        message.Reply(bytes_written);
+      return true;
+    }
+
+    default:
+      return false;
+  }
+}
+
+int 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:
+      HandleSystemMessage(message);
+      return 0;
+
+    default:
+      return message.ReplyError(ENOTSUP);
+  }
+}
+
+void Service::OnSysPropChange() {}
+
+int Service::ReceiveAndDispatch() {
+  ErrnoGuard errno_guard;
+  Message message;
+  const int ret = endpoint_->MessageReceive(&message);
+  if (ret < 0) {
+    ALOGE("Failed to receive message: %s\n", strerror(errno));
+    return ReturnCodeOrError(ret);
+  }
+
+  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 -EINVAL;
+  }
+
+  if (message.IsImpulse()) {
+    service->HandleImpulse(message);
+    return 0;
+  } else if (service->HandleSystemMessage(message)) {
+    return 0;
+  } else {
+    return service->HandleMessage(message);
+  }
+}
+
+int Service::Cancel() {
+  ErrnoGuard errno_guard;
+  const int ret = endpoint_->Cancel();
+  return ReturnCodeOrError(ret);
+}
+
+}  // 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..fc0c8db
--- /dev/null
+++ b/libs/vr/libpdx/service_tests.cpp
@@ -0,0 +1,796 @@
+#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::SetErrnoAndReturn;
+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, int(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(0));
+    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(), DefaultHandleMessage(_));
+  }
+
+  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(0));
+  EXPECT_CALL(*endpoint(), MessageReply(&message, 0)).WillOnce(Return(0));
+
+  EXPECT_EQ(0, 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(0));
+  EXPECT_CALL(*endpoint(), MessageReply(&message, 0)).WillOnce(Return(0));
+
+  EXPECT_EQ(0, 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(0));
+
+  EXPECT_EQ(0, 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(0));
+
+  EXPECT_EQ(0, 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(0));
+
+  EXPECT_EQ(0, 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(0));
+
+  EXPECT_EQ(0, service_->Service::HandleMessage(message));
+}
+
+TEST_F(ServiceTest, HandleMessageCustom) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+  Message message{info};
+
+  EXPECT_CALL(*endpoint(), MessageReply(&message, -ENOTSUP))
+      .WillOnce(Return(0));
+
+  EXPECT_EQ(0, 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));
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchMessage) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info, kTestOp);
+  ExpectDefaultHandleMessage();
+
+  auto on_receive = [&info](Message* message) {
+    *message = Message{info};
+    return 0;
+  };
+  EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+  EXPECT_CALL(*service_, HandleMessage(_)).WillOnce(Return(0));
+
+  EXPECT_EQ(0, service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, ReceiveAndDispatchImpulse) {
+  MessageInfo info;
+  SetupMessageInfoAndDefaultExpectations(&info, kTestOp, true);
+
+  auto on_receive = [&info](Message* message) {
+    *message = Message{info};
+    return 0;
+  };
+  EXPECT_CALL(*endpoint(), MessageReceive(_)).WillOnce(Invoke(on_receive));
+  EXPECT_CALL(*service_, HandleImpulse(_));
+
+  EXPECT_EQ(0, service_->ReceiveAndDispatch());
+}
+
+TEST_F(ServiceTest, Cancel) {
+  EXPECT_CALL(*endpoint(), Cancel()).WillOnce(Return(0));
+  EXPECT_EQ(0, service_->Cancel());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Message class tests
+///////////////////////////////////////////////////////////////////////////////
+
+TEST_F(ServiceMessageTest, Reply) {
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+      .WillOnce(Return(0));
+  EXPECT_FALSE(message_->replied());
+  EXPECT_EQ(0, message_->Reply(12));
+  EXPECT_TRUE(message_->replied());
+
+  EXPECT_EQ(-EINVAL, message_->Reply(12));  // Already replied.
+}
+
+TEST_F(ServiceMessageTest, ReplyFail) {
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), 12))
+      .WillOnce(SetErrnoAndReturn(EIO, -1));
+  EXPECT_EQ(-EIO, message_->Reply(12));
+
+  ExpectDefaultHandleMessage();
+}
+
+TEST_F(ServiceMessageTest, ReplyError) {
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -12))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->ReplyError(12));
+}
+
+TEST_F(ServiceMessageTest, ReplyFileDescriptor) {
+  EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), 5))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->ReplyFileDescriptor(5));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalFileHandle) {
+  const int kFakeFd = 12345;
+  LocalHandle handle{kFakeFd};
+  EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, 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(0));
+  EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandle) {
+  const int kFakeFd = 12345;
+  BorrowedHandle handle{kFakeFd};
+  EXPECT_CALL(*endpoint(), MessageReplyFd(message_.get(), kFakeFd))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedFileHandleError) {
+  BorrowedHandle handle{-EACCES};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EACCES))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandle) {
+  RemoteHandle handle{123};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), handle.Get()))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteFileHandleError) {
+  RemoteHandle handle{-EIO};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -EIO))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyLocalChannelHandle) {
+  LocalChannelHandle handle{nullptr, 12345};
+  EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+                               message_.get(), A<const LocalChannelHandle&>()))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyBorrowedChannelHandle) {
+  BorrowedChannelHandle handle{12345};
+  EXPECT_CALL(*endpoint(),
+              MessageReplyChannelHandle(message_.get(),
+                                        A<const BorrowedChannelHandle&>()))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyRemoteChannelHandle) {
+  RemoteChannelHandle handle{12345};
+  EXPECT_CALL(*endpoint(), MessageReplyChannelHandle(
+                               message_.get(), A<const RemoteChannelHandle&>()))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->Reply(handle));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusInt) {
+  Status<int> status{123};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), status.get()))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, message_->Reply(status));
+}
+
+TEST_F(ServiceMessageTest, ReplyStatusError) {
+  Status<int> status{ErrorStatus{EIO}};
+  EXPECT_CALL(*endpoint(), MessageReply(message_.get(), -status.error()))
+      .WillOnce(Return(0));
+  EXPECT_EQ(0, 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(SetErrnoAndReturn(EACCES, -1));
+  EXPECT_EQ(50, message_->Read(kDataBuffer, kDataSize));
+  EXPECT_EQ(-EACCES, message_->Read(kDataBuffer, kDataSize));
+}
+
+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(SetErrnoAndReturn(EBADF, -1));
+  EXPECT_EQ(30, message_->ReadVector(vec, 2));
+  EXPECT_EQ(15, message_->ReadVector(vec));
+  EXPECT_EQ(-EBADF, message_->ReadVector(vec));
+}
+
+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(SetErrnoAndReturn(EBADMSG, -1));
+  EXPECT_EQ(50, message_->Write(kDataBuffer, kDataSize));
+  EXPECT_EQ(-EBADMSG, message_->Write(kDataBuffer, kDataSize));
+}
+
+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(SetErrnoAndReturn(EIO, -1));
+  EXPECT_EQ(30, message_->WriteVector(vec, 2));
+  EXPECT_EQ(15, message_->WriteVector(vec));
+  EXPECT_EQ(-EIO, message_->WriteVector(vec, 2));
+}
+
+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(SetErrnoAndReturn(EIO, -1));
+  EXPECT_EQ(12, message_->PushFileHandle(handle));
+  EXPECT_EQ(-EIO, message_->PushFileHandle(handle));
+  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(SetErrnoAndReturn(EACCES, -1));
+  EXPECT_EQ(13, message_->PushFileHandle(handle));
+  EXPECT_EQ(-EACCES, message_->PushFileHandle(handle));
+}
+
+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(SetErrnoAndReturn(EIO, -1));
+  EXPECT_EQ(kFakeFd, message_->PushFileHandle(handle));
+  EXPECT_EQ(-EIO, message_->PushFileHandle(handle));
+}
+
+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(SetErrnoAndReturn(EIO, -1));
+  EXPECT_EQ(7, message_->PushChannelHandle(handle));
+  EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+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(SetErrnoAndReturn(EIO, -1));
+  EXPECT_EQ(8, message_->PushChannelHandle(handle));
+  EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+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(SetErrnoAndReturn(EIO, -1));
+  EXPECT_EQ(kValue, message_->PushChannelHandle(handle));
+  EXPECT_EQ(-EIO, message_->PushChannelHandle(handle));
+}
+
+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(0));
+  EXPECT_EQ(0, 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(0));
+  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(0));
+  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..c6a7b0b
--- /dev/null
+++ b/libs/vr/libpdx/thread_local_buffer_tests.cpp
@@ -0,0 +1,116 @@
+#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>();
+  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..c30c055
--- /dev/null
+++ b/libs/vr/libpdx/variant_tests.cpp
@@ -0,0 +1,1087 @@
+#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<bool> b(true);
+    Variant<int, bool, float> v(b.take());
+    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::HasTypeIgnoreRef<int&, int, float, bool>::value));
+  EXPECT_FALSE((detail::HasTypeIgnoreRef<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..655adb8
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -0,0 +1,69 @@
+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: [
+        "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..de02401
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp
@@ -0,0 +1,1098 @@
+// 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::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();
+  }
+
+  int 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) == 0);
+        }
+        return 0;
+
+      case BenchmarkOps::Write: {
+        VLOG(1) << "BenchmarkService::HandleMessage: op=write send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        const ssize_t expected_length =
+            static_cast<ssize_t>(message.GetSendLength());
+        const ssize_t actual_length =
+            expected_length > 0
+                ? message.Read(send_buffer.data(), message.GetSendLength())
+                : 0;
+
+        {
+          ATRACE_NAME("Reply");
+          if (actual_length < expected_length)
+            CHECK(message.ReplyError(EIO) == 0);
+          else
+            CHECK(message.Reply(actual_length) == 0);
+        }
+        return 0;
+      }
+
+      case BenchmarkOps::Read: {
+        VLOG(1) << "BenchmarkService::HandleMessage: op=read send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        const ssize_t expected_length =
+            static_cast<ssize_t>(message.GetReceiveLength());
+        const ssize_t actual_length =
+            expected_length > 0
+                ? message.Write(receive_buffer.data(),
+                                message.GetReceiveLength())
+                : 0;
+
+        {
+          ATRACE_NAME("Reply");
+          if (actual_length < expected_length)
+            CHECK(message.ReplyError(EIO) == 0);
+          else
+            CHECK(message.Reply(actual_length) == 0);
+        }
+        return 0;
+      }
+
+      case BenchmarkOps::Echo: {
+        VLOG(1) << "BenchmarkService::HandleMessage: op=echo send_length="
+                << message.GetSendLength()
+                << " receive_length=" << message.GetReceiveLength();
+
+        const ssize_t expected_length =
+            static_cast<ssize_t>(message.GetSendLength());
+        ssize_t actual_length =
+            expected_length > 0
+                ? message.Read(send_buffer.data(), message.GetSendLength())
+                : 0;
+
+        if (actual_length < expected_length) {
+          CHECK(message.ReplyError(EIO) == 0);
+          return 0;
+        }
+
+        actual_length =
+            expected_length > 0
+                ? message.Write(send_buffer.data(), message.GetSendLength())
+                : 0;
+
+        {
+          ATRACE_NAME("Reply");
+          if (actual_length < expected_length)
+            CHECK(message.ReplyError(EIO) == 0);
+          else
+            CHECK(message.Reply(actual_length) == 0);
+        }
+        return 0;
+      }
+
+      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 0;
+      }
+
+      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 0;
+
+      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 0;
+
+      case BenchmarkOps::Quit:
+        Cancel();
+        return -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::Create(
+            GetServicePath(path, service_id_counter),
+            android::pdx::default_transport::Endpoint::kDefaultMode,
+            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 inital schedstats for this thread from procfs.
+            local_service->UpdateSchedStats();
+
+            ATRACE_NAME("BenchmarkService::Dispatch");
+            while (!done) {
+              const int ret = local_service->ReceiveAndDispatch();
+              if (ret < 0) {
+                if (ret != -ESHUTDOWN) {
+                  std::cerr << "Error while dispatching message on thread "
+                            << thread_id << " service " << service_id << ": "
+                            << strerror(-ret) << 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..22c6b3f
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/pdx/default_transport/service_utility.h
@@ -0,0 +1,45 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICE_UTILITY_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);
+  }
+
+ private:
+  friend BASE;
+
+  ServiceUtility(const std::string& endpoint_path, int* error = nullptr)
+      : BASE(ClientChannelFactory::Create(endpoint_path)) {
+    if (error)
+      *error = Client::error();
+  }
+
+  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..09eeaa0
--- /dev/null
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -0,0 +1,47 @@
+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: [
+        "libpdx",
+    ],
+}
+
+cc_test {
+    name: "libpdx_uds_tests",
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "remote_method_tests.cpp",
+        "service_framework_tests.cpp",
+    ],
+    static_libs: [
+        "libpdx_uds",
+        "libpdx",
+    ],
+    shared_libs: [
+        "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..f8baeab
--- /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_create(1), &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..4cbdb94
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel.cpp
@@ -0,0 +1,289 @@
+#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(int 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(int 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(int 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) {
+  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(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) {
+  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(channel_handle_.value(), state, opcode, send_vector,
+                            send_count, max_recv_len);
+  if (status) {
+    status = ReceiveResponse(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..1879127
--- /dev/null
+++ b/libs/vr/libpdx_uds/client_channel_factory.cpp
@@ -0,0 +1,89 @@
+#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 <uds/channel_manager.h>
+#include <uds/client_channel.h>
+#include <uds/ipc_helper.h>
+
+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)} {}
+
+std::unique_ptr<pdx::ClientChannelFactory> ClientChannelFactory::Create(
+    const std::string& endpoint_path) {
+  return std::unique_ptr<pdx::ClientChannelFactory>{
+      new ClientChannelFactory{endpoint_path}};
+}
+
+Status<std::unique_ptr<pdx::ClientChannel>> ClientChannelFactory::Connect(
+    int64_t timeout_ms) const {
+  auto status = WaitForEndpoint(endpoint_path_, timeout_ms);
+  if (!status)
+    return ErrorStatus(status.error());
+
+  LocalHandle socket_fd{socket(AF_UNIX, SOCK_STREAM, 0)};
+  if (!socket_fd) {
+    ALOGE("ClientChannelFactory::Connect: socket error %s", strerror(errno));
+    return ErrorStatus(errno);
+  }
+
+  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';
+
+  int ret = RETRY_EINTR(connect(
+      socket_fd.Get(), reinterpret_cast<sockaddr*>(&remote), sizeof(remote)));
+  if (ret == -1) {
+    ALOGE(
+        "ClientChannelFactory::Connect: Failed to initialize connection when "
+        "connecting %s",
+        strerror(errno));
+    return ErrorStatus(errno);
+  }
+
+  RequestHeader<BorrowedHandle> request;
+  InitRequest(&request, opcodes::CHANNEL_OPEN, 0, 0, false);
+  status = SendData(socket_fd.Get(), request);
+  if (!status)
+    return ErrorStatus(status.error());
+  ResponseHeader<LocalHandle> response;
+  status = ReceiveData(socket_fd.Get(), &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_fd), std::move(event_fd)));
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/ipc_helper.cpp b/libs/vr/libpdx_uds/ipc_helper.cpp
new file mode 100644
index 0000000..ee7299e
--- /dev/null
+++ b/libs/vr/libpdx_uds/ipc_helper.cpp
@@ -0,0 +1,416 @@
+#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 {
+
+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(int socket_fd) {
+  return Send(socket_fd, nullptr);
+}
+
+Status<void> SendPayload::Send(int socket_fd, const ucred* cred) {
+  MessagePreamble preamble;
+  preamble.magic = kMagicPreamble;
+  preamble.data_size = buffer_.size();
+  preamble.fd_count = file_handles_.size();
+
+  ssize_t ret =
+      RETRY_EINTR(send(socket_fd, &preamble, sizeof(preamble), MSG_NOSIGNAL));
+  if (ret < 0)
+    return ErrorStatus(errno);
+  if (ret != sizeof(preamble))
+    return ErrorStatus(EIO);
+
+  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);
+    }
+  }
+
+  ret = RETRY_EINTR(sendmsg(socket_fd, &msg, MSG_NOSIGNAL));
+  if (ret < 0)
+    return ErrorStatus(errno);
+  if (static_cast<size_t>(ret) != buffer_.size())
+    return ErrorStatus(EIO);
+  return {};
+}
+
+// MessageWriter
+void* SendPayload::GetNextWriteBufferSection(size_t size) {
+  return buffer_.grow_by(size);
+}
+
+OutputResourceMapper* SendPayload::GetOutputResourceMapper() { return this; }
+
+// OutputResourceMapper
+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();
+  }
+}
+
+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();
+  }
+}
+
+FileReference SendPayload::PushFileHandle(const RemoteHandle& handle) {
+  return handle.Get();
+}
+
+ChannelReference SendPayload::PushChannelHandle(
+    const LocalChannelHandle& /*handle*/) {
+  return -1;
+}
+ChannelReference SendPayload::PushChannelHandle(
+    const BorrowedChannelHandle& /*handle*/) {
+  return -1;
+}
+ChannelReference SendPayload::PushChannelHandle(
+    const RemoteChannelHandle& /*handle*/) {
+  return -1;
+}
+
+Status<void> ReceivePayload::Receive(int socket_fd) {
+  return Receive(socket_fd, nullptr);
+}
+
+Status<void> ReceivePayload::Receive(int socket_fd, ucred* cred) {
+  MessagePreamble preamble;
+  ssize_t ret =
+      RETRY_EINTR(recv(socket_fd, &preamble, sizeof(preamble), MSG_WAITALL));
+  if (ret < 0)
+    return ErrorStatus(errno);
+  else if (ret == 0)
+    return ErrorStatus(ESHUTDOWN);
+  else if (ret != sizeof(preamble) || preamble.magic != kMagicPreamble)
+    return ErrorStatus(EIO);
+
+  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 = RETRY_EINTR(recvmsg(socket_fd, &msg, MSG_WAITALL));
+  if (ret < 0)
+    return ErrorStatus(errno);
+  else if (ret == 0)
+    return ErrorStatus(ESHUTDOWN);
+  else if (static_cast<uint32_t>(ret) != preamble.data_size)
+    return ErrorStatus(EIO);
+
+  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) {
+    return ErrorStatus(EIO);
+  }
+
+  return {};
+}
+
+// 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(int socket_fd, const void* data, size_t size) {
+  ssize_t size_written = RETRY_EINTR(send(socket_fd, data, size, MSG_NOSIGNAL));
+  if (size_written < 0)
+    return ErrorStatus(errno);
+  if (static_cast<size_t>(size_written) != size)
+    return ErrorStatus(EIO);
+  return {};
+}
+
+Status<void> SendDataVector(int socket_fd, const iovec* data, size_t count) {
+  msghdr msg = {};
+  msg.msg_iov = const_cast<iovec*>(data);
+  msg.msg_iovlen = count;
+  ssize_t size_written = RETRY_EINTR(sendmsg(socket_fd, &msg, MSG_NOSIGNAL));
+  if (size_written < 0)
+    return ErrorStatus(errno);
+  if (static_cast<size_t>(size_written) != CountVectorSize(data, count))
+    return ErrorStatus(EIO);
+  return {};
+}
+
+Status<void> ReceiveData(int socket_fd, void* data, size_t size) {
+  ssize_t size_read = RETRY_EINTR(recv(socket_fd, data, size, MSG_WAITALL));
+  if (size_read < 0)
+    return ErrorStatus(errno);
+  else if (size_read == 0)
+    return ErrorStatus(ESHUTDOWN);
+  else if (static_cast<size_t>(size_read) != size)
+    return ErrorStatus(EIO);
+  return {};
+}
+
+Status<void> ReceiveDataVector(int socket_fd, const iovec* data, size_t count) {
+  msghdr msg = {};
+  msg.msg_iov = const_cast<iovec*>(data);
+  msg.msg_iovlen = count;
+  ssize_t size_read = RETRY_EINTR(recvmsg(socket_fd, &msg, MSG_WAITALL));
+  if (size_read < 0)
+    return ErrorStatus(errno);
+  else if (size_read == 0)
+    return ErrorStatus(ESHUTDOWN);
+  else if (static_cast<size_t>(size_read) != CountVectorSize(data, count))
+    return ErrorStatus(EIO);
+  return {};
+}
+
+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/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..45f6473
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/client_channel.h
@@ -0,0 +1,82 @@
+#ifndef ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
+#define ANDROID_PDX_UDS_CLIENT_CHANNEL_H_
+
+#include <pdx/client_channel.h>
+
+#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_;
+};
+
+}  // 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..6f80d31
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/client_channel_factory.h
@@ -0,0 +1,33 @@
+#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);
+
+  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);
+
+  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..00f3490
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/ipc_helper.h
@@ -0,0 +1,164 @@
+#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 {
+
+#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:
+  Status<void> Send(int socket_fd);
+  Status<void> Send(int socket_fd, const ucred* cred);
+
+  // MessageWriter
+  void* GetNextWriteBufferSection(size_t size) override;
+  OutputResourceMapper* GetOutputResourceMapper() override;
+
+  // OutputResourceMapper
+  FileReference PushFileHandle(const LocalHandle& handle) override;
+  FileReference PushFileHandle(const BorrowedHandle& handle) override;
+  FileReference PushFileHandle(const RemoteHandle& handle) override;
+  ChannelReference PushChannelHandle(const LocalChannelHandle& handle) override;
+  ChannelReference PushChannelHandle(
+      const BorrowedChannelHandle& handle) override;
+  ChannelReference PushChannelHandle(
+      const RemoteChannelHandle& handle) override;
+
+ private:
+  ByteBuffer buffer_;
+  std::vector<int> file_handles_;
+};
+
+class ReceivePayload : public MessageReader, public InputResourceMapper {
+ public:
+  Status<void> Receive(int socket_fd);
+  Status<void> Receive(int 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:
+  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(int socket_fd, const T& data) {
+  SendPayload payload;
+  rpc::Serialize(data, &payload);
+  return payload.Send(socket_fd);
+}
+
+template <typename FileHandleType>
+inline Status<void> SendData(int socket_fd,
+                             const RequestHeader<FileHandleType>& request) {
+  SendPayload payload;
+  rpc::Serialize(request, &payload);
+  return payload.Send(socket_fd, &request.cred);
+}
+
+Status<void> SendData(int socket_fd, const void* data, size_t size);
+Status<void> SendDataVector(int socket_fd, const iovec* data, size_t count);
+
+template <typename T>
+inline Status<void> ReceiveData(int 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(int 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(int socket_fd, void* data, size_t size);
+Status<void> ReceiveDataVector(int 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..3ec8519
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/service_endpoint.h
@@ -0,0 +1,143 @@
+#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; }
+  int SetService(Service* service) override;
+  int SetChannel(int channel_id, Channel* channel) override;
+  int CloseChannel(int channel_id) override;
+  int 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;
+  int DefaultHandleMessage(const MessageInfo& info) override;
+  int MessageReceive(Message* message) override;
+  int MessageReply(Message* message, int return_code) override;
+  int MessageReplyFd(Message* message, unsigned int push_fd) override;
+  int MessageReplyChannelHandle(Message* message,
+                                const LocalChannelHandle& handle) override;
+  int MessageReplyChannelHandle(Message* message,
+                                const BorrowedChannelHandle& handle) override;
+  int MessageReplyChannelHandle(Message* message,
+                                const RemoteChannelHandle& handle) override;
+  ssize_t ReadMessageData(Message* message, const iovec* vector,
+                          size_t vector_length) override;
+  ssize_t WriteMessageData(Message* message, const iovec* vector,
+                           size_t vector_length) override;
+  FileReference PushFileHandle(Message* message,
+                               const LocalHandle& handle) override;
+  FileReference PushFileHandle(Message* message,
+                               const BorrowedHandle& handle) override;
+  FileReference PushFileHandle(Message* message,
+                               const RemoteHandle& handle) override;
+  ChannelReference PushChannelHandle(Message* message,
+                                     const LocalChannelHandle& handle) override;
+  ChannelReference PushChannelHandle(
+      Message* message, const BorrowedChannelHandle& handle) override;
+  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;
+
+  int 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().
+  static std::unique_ptr<Endpoint> Create(const std::string& endpoint_path,
+                                          mode_t /*unused_mode*/ = kDefaultMode,
+                                          bool blocking = kDefaultBlocking);
+
+  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);
+
+  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(int channel_id, Message* message);
+
+  Status<void> AcceptConnection(Message* message);
+  Status<void> ReceiveMessageForChannel(int channel_id, Message* message);
+  Status<void> OnNewChannel(LocalHandle channel_fd);
+  Status<ChannelData*> OnNewChannelLocked(LocalHandle channel_fd,
+                                          Channel* channel_state);
+  int CloseChannelLocked(int channel_id);
+  Status<void> ReenableEpollEvent(int fd);
+  Channel* GetChannelState(int channel_id);
+  int GetChannelSocketFd(int channel_id);
+  int GetChannelEventFd(int channel_id);
+
+  std::string endpoint_path_;
+  bool is_blocking_;
+  LocalHandle socket_fd_;
+  LocalHandle cancel_event_fd_;
+  LocalHandle epoll_fd_;
+
+  mutable std::mutex channel_mutex_;
+  std::map<int, ChannelData> channels_;
+
+  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..299910c
--- /dev/null
+++ b/libs/vr/libpdx_uds/remote_method_tests.cpp
@@ -0,0 +1,907 @@
+#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::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 {
+
+// 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,
+  };
+
+  // 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_API(API, Add, Foo, Concatenate, SumVector, StringLength,
+                 SendTestType, SendVector, Rot13, NoArgs, SendFile, GetFile,
+                 GetTestFdType, OpenFiles, PushChannel);
+};
+
+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();
+  }
+
+  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:
+  int HandleMessage(Message& message) override {
+    switch (message.GetOp()) {
+      case TestInterface::Add::Opcode:
+        DispatchRemoteMethod<TestInterface::Add>(*this, &TestService::OnAdd,
+                                                 message);
+        return 0;
+
+      case TestInterface::Foo::Opcode:
+        DispatchRemoteMethod<TestInterface::Foo>(*this, &TestService::OnFoo,
+                                                 message);
+        return 0;
+
+      case TestInterface::Concatenate::Opcode:
+        DispatchRemoteMethod<TestInterface::Concatenate>(
+            *this, &TestService::OnConcatenate, message);
+        return 0;
+
+      case TestInterface::SumVector::Opcode:
+        DispatchRemoteMethod<TestInterface::SumVector>(
+            *this, &TestService::OnSumVector, message);
+        return 0;
+
+      case TestInterface::StringLength::Opcode:
+        DispatchRemoteMethod<TestInterface::StringLength>(
+            *this, &TestService::OnStringLength, message);
+        return 0;
+
+      case TestInterface::SendTestType::Opcode:
+        DispatchRemoteMethod<TestInterface::SendTestType>(
+            *this, &TestService::OnSendTestType, message);
+        return 0;
+
+      case TestInterface::SendVector::Opcode:
+        DispatchRemoteMethod<TestInterface::SendVector>(
+            *this, &TestService::OnSendVector, message);
+        return 0;
+
+      case TestInterface::Rot13::Opcode:
+        DispatchRemoteMethod<TestInterface::Rot13>(*this, &TestService::OnRot13,
+                                                   message);
+        return 0;
+
+      case TestInterface::NoArgs::Opcode:
+        DispatchRemoteMethod<TestInterface::NoArgs>(
+            *this, &TestService::OnNoArgs, message);
+        return 0;
+
+      case TestInterface::SendFile::Opcode:
+        DispatchRemoteMethod<TestInterface::SendFile>(
+            *this, &TestService::OnSendFile, message);
+        return 0;
+
+      case TestInterface::GetFile::Opcode:
+        DispatchRemoteMethod<TestInterface::GetFile>(
+            *this, &TestService::OnGetFile, message);
+        return 0;
+
+      case TestInterface::GetTestFdType::Opcode:
+        DispatchRemoteMethod<TestInterface::GetTestFdType>(
+            *this, &TestService::OnGetTestFdType, message);
+        return 0;
+
+      case TestInterface::OpenFiles::Opcode:
+        DispatchRemoteMethod<TestInterface::OpenFiles>(
+            *this, &TestService::OnOpenFiles, message);
+        return 0;
+
+      case TestInterface::ReadFile::Opcode:
+        DispatchRemoteMethod<TestInterface::ReadFile>(
+            *this, &TestService::OnReadFile, message);
+        return 0;
+
+      case TestInterface::PushChannel::Opcode:
+        DispatchRemoteMethod<TestInterface::PushChannel>(
+            *this, &TestService::OnPushChannel, message);
+        return 0;
+
+      default:
+        return Service::DefaultHandleMessage(message);
+    }
+  }
+
+ private:
+  friend BASE;
+
+  TestService()
+      : BASE("TestService", Endpoint::Create(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;
+  }
+
+  std::string OnRot13(Message&, 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;
+  }
+
+  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();
+  }
+
+  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);
+
+  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, 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..fa98f26
--- /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_create(1));  // Size arg is ignored, but must be > 0.
+  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..7bf753d
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_endpoint.cpp
@@ -0,0 +1,639 @@
+#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 <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::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;
+  }
+
+  FileReference PushFileHandle(BorrowedHandle handle) {
+    if (!handle)
+      return handle.Get();
+    response.file_descriptors.push_back(std::move(handle));
+    return response.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();
+      response.channels.push_back(std::move(channel_info));
+      return response.channels.size() - 1;
+    } else {
+      return -1;
+    }
+  }
+
+  ChannelReference PushChannelHandle(BorrowedHandle data_fd,
+                                     BorrowedHandle event_fd) {
+    if (!data_fd || !event_fd)
+      return -1;
+    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;
+  }
+
+  ssize_t WriteData(const iovec* vector, size_t vector_length) {
+    ssize_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;
+  }
+
+  ssize_t ReadData(const iovec* vector, size_t vector_length) {
+    size_t size_remaining = request_data.size() - request_data_read_pos;
+    ssize_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)
+    : endpoint_path_{ClientChannelFactory::GetEndpointPath(endpoint_path)},
+      is_blocking_{blocking} {
+  LocalHandle fd{socket(AF_UNIX, SOCK_STREAM, 0)};
+  if (!fd) {
+    ALOGE("Endpoint::Endpoint: Failed to create socket: %s", strerror(errno));
+    return;
+  }
+
+  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);
+  if (bind(fd.Get(), (struct sockaddr*)&local, sizeof(local)) == -1) {
+    ALOGE("Endpoint::Endpoint: bind error: %s", strerror(errno));
+    return;
+  }
+  if (listen(fd.Get(), kMaxBackLogForSocketListen) == -1) {
+    ALOGE("Endpoint::Endpoint: listen error: %s", strerror(errno));
+    return;
+  }
+
+  cancel_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  if (!cancel_event_fd_) {
+    ALOGE("Endpoint::Endpoint: Failed to create event fd: %s\n",
+          strerror(errno));
+    return;
+  }
+
+  epoll_fd_.Reset(epoll_create(1));  // Size arg is ignored, but must be > 0.
+  if (!epoll_fd_) {
+    ALOGE("Endpoint::Endpoint: Failed to create epoll fd: %s\n",
+          strerror(errno));
+    return;
+  }
+
+  epoll_event socket_event;
+  socket_event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+  socket_event.data.fd = fd.Get();
+
+  epoll_event cancel_event;
+  cancel_event.events = EPOLLIN;
+  cancel_event.data.fd = cancel_event_fd_.Get();
+
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd.Get(), &socket_event) < 0 ||
+      epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, cancel_event_fd_.Get(),
+                &cancel_event) < 0) {
+    ALOGE("Endpoint::Endpoint: Failed to add event fd to epoll fd: %s\n",
+          strerror(errno));
+    cancel_event_fd_.Close();
+    epoll_fd_.Close();
+  } else {
+    socket_fd_ = std::move(fd);
+  }
+}
+
+void* Endpoint::AllocateMessageState() { return new MessageState; }
+
+void Endpoint::FreeMessageState(void* state) {
+  delete static_cast<MessageState*>(state);
+}
+
+Status<void> Endpoint::AcceptConnection(Message* message) {
+  sockaddr_un remote;
+  socklen_t addrlen = sizeof(remote);
+  LocalHandle channel_fd{
+      accept(socket_fd_.Get(), reinterpret_cast<sockaddr*>(&remote), &addrlen)};
+  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);
+  }
+
+  auto status = ReceiveMessageForChannel(channel_fd.Get(), message);
+  if (status)
+    status = OnNewChannel(std::move(channel_fd));
+  return status;
+}
+
+int Endpoint::SetService(Service* service) {
+  service_ = service;
+  return 0;
+}
+
+int 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 -EINVAL;
+  channel_data->second.channel_state = channel;
+  return 0;
+}
+
+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<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;
+  const int channel_id = channel_fd.Get();
+  channel_data.event_set.AddDataFd(channel_fd);
+  channel_data.data_fd = std::move(channel_fd);
+  channel_data.channel_state = channel_state;
+  auto pair = channels_.emplace(channel_id, std::move(channel_data));
+  return &pair.first->second;
+}
+
+Status<void> Endpoint::ReenableEpollEvent(int fd) {
+  epoll_event event;
+  event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
+  event.data.fd = fd;
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_MOD, fd, &event) < 0) {
+    ALOGE(
+        "Endpoint::ReenableEpollEvent: Failed to re-enable channel to "
+        "endpoint: %s\n",
+        strerror(errno));
+    return ErrorStatus(errno);
+  }
+  return {};
+}
+
+int Endpoint::CloseChannel(int channel_id) {
+  std::lock_guard<std::mutex> autolock(channel_mutex_);
+  return CloseChannelLocked(channel_id);
+}
+
+int Endpoint::CloseChannelLocked(int channel_id) {
+  ALOGD_IF(TRACE, "Endpoint::CloseChannelLocked: channel_id=%d", channel_id);
+
+  auto channel_data = channels_.find(channel_id);
+  if (channel_data == channels_.end())
+    return -EINVAL;
+
+  int ret = 0;
+  epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
+  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_id, &dummy) < 0) {
+    ret = -errno;
+    ALOGE(
+        "Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: "
+        "%s\n",
+        strerror(errno));
+  }
+
+  channels_.erase(channel_data);
+  return ret;
+}
+
+int 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 0;
+  }
+
+  return -EINVAL;
+}
+
+Status<RemoteChannelHandle> Endpoint::PushChannel(Message* message,
+                                                  int /*flags*/,
+                                                  Channel* channel,
+                                                  int* channel_id) {
+  int channel_pair[2] = {};
+  if (socketpair(AF_UNIX, SOCK_STREAM, 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_);
+  *channel_id = local_socket.Get();
+  auto channel_data = OnNewChannelLocked(std::move(local_socket), channel);
+  if (!channel_data)
+    return ErrorStatus(channel_data.error());
+
+  // Flags are ignored for now.
+  // TODO(xiaohuit): Implement those.
+
+  auto* state = static_cast<MessageState*>(message->GetState());
+  ChannelReference ref = state->PushChannelHandle(
+      remote_socket.Borrow(),
+      channel_data.get()->event_set.event_fd().Borrow());
+  state->sockets_to_close.push_back(std::move(remote_socket));
+  return RemoteChannelHandle{ref};
+}
+
+Status<int> Endpoint::CheckChannel(const Message* /*message*/,
+                                   ChannelReference /*ref*/,
+                                   Channel** /*channel*/) {
+  // TODO(xiaohuit): Implement this.
+  return ErrorStatus(EFAULT);
+}
+
+int Endpoint::DefaultHandleMessage(const MessageInfo& /* info */) {
+  ALOGE(
+      "Endpoint::CheckChannel: Not implemented! Endpoint DefaultHandleMessage "
+      "does nothing!");
+  return 0;
+}
+
+Channel* Endpoint::GetChannelState(int 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;
+}
+
+int Endpoint::GetChannelSocketFd(int 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.data_fd.Get()
+                                           : -1;
+}
+
+int Endpoint::GetChannelEventFd(int 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.event_set.event_fd().Get()
+             : -1;
+}
+
+Status<void> Endpoint::ReceiveMessageForChannel(int channel_id,
+                                                Message* message) {
+  RequestHeader<LocalHandle> request;
+  auto status = ReceiveData(channel_id, &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_id, state->request_data.data(),
+                         state->request_data.size());
+  }
+
+  if (status && request.is_impulse)
+    status = ReenableEpollEvent(channel_id);
+
+  if (!status) {
+    if (status.error() == ESHUTDOWN) {
+      BuildCloseMessage(channel_id, message);
+      return {};
+    } else {
+      CloseChannel(channel_id);
+      return status;
+    }
+  }
+
+  return status;
+}
+
+void Endpoint::BuildCloseMessage(int 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};
+}
+
+int 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 -errno;
+  } else if (count == 0) {
+    return -ETIMEDOUT;
+  }
+
+  if (event.data.fd == cancel_event_fd_.Get()) {
+    return -ESHUTDOWN;
+  }
+
+  if (event.data.fd == socket_fd_.Get()) {
+    auto status = AcceptConnection(message);
+    if (!status)
+      return -status.error();
+    status = ReenableEpollEvent(socket_fd_.Get());
+    return status ? 0 : -status.error();
+  }
+
+  int channel_id = event.data.fd;
+  if (event.events & (EPOLLRDHUP | EPOLLHUP)) {
+    BuildCloseMessage(channel_id, message);
+    return 0;
+  }
+
+  auto status = ReceiveMessageForChannel(channel_id, message);
+  if (!status)
+    return -status.error();
+  return 0;
+}
+
+int Endpoint::MessageReply(Message* message, int return_code) {
+  const int channel_id = message->GetChannelId();
+  const int channel_socket = GetChannelSocketFd(channel_id);
+  if (channel_socket < 0)
+    return -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);
+      // Reply with the event fd.
+      return_code = state->PushFileHandle(
+          BorrowedHandle{GetChannelEventFd(channel_socket)});
+      state->response_data.clear();  // Just in case...
+      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 ? 0 : -status.error();
+}
+
+int 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)});
+  return MessageReply(message, ref);
+}
+
+int Endpoint::MessageReplyChannelHandle(Message* message,
+                                        const LocalChannelHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  auto ref = state->PushChannelHandle(handle.Borrow());
+  return MessageReply(message, ref);
+}
+
+int Endpoint::MessageReplyChannelHandle(Message* message,
+                                        const BorrowedChannelHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  auto ref = state->PushChannelHandle(handle.Duplicate());
+  return MessageReply(message, ref);
+}
+
+int Endpoint::MessageReplyChannelHandle(Message* message,
+                                        const RemoteChannelHandle& handle) {
+  return MessageReply(message, handle.value());
+}
+
+ssize_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);
+}
+
+ssize_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);
+}
+
+FileReference Endpoint::PushFileHandle(Message* message,
+                                       const LocalHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->PushFileHandle(handle.Borrow());
+}
+
+FileReference Endpoint::PushFileHandle(Message* message,
+                                       const BorrowedHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->PushFileHandle(handle.Duplicate());
+}
+
+FileReference Endpoint::PushFileHandle(Message* /*message*/,
+                                       const RemoteHandle& handle) {
+  return handle.Get();
+}
+
+ChannelReference Endpoint::PushChannelHandle(Message* message,
+                                             const LocalChannelHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->PushChannelHandle(handle.Borrow());
+}
+
+ChannelReference Endpoint::PushChannelHandle(
+    Message* message, const BorrowedChannelHandle& handle) {
+  auto* state = static_cast<MessageState*>(message->GetState());
+  return state->PushChannelHandle(handle.Duplicate());
+}
+
+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;
+}
+
+int Endpoint::Cancel() {
+  return (eventfd_write(cancel_event_fd_.Get(), 1) < 0) ? -errno : 0;
+}
+
+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));
+}
+
+}  // 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..8891600
--- /dev/null
+++ b/libs/vr/libpdx_uds/service_framework_tests.cpp
@@ -0,0 +1,677 @@
+#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::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;
+      }
+    }
+  }
+
+  int HandleMessage(Message& message) override {
+    switch (message.GetOp()) {
+      case TEST_OP_GET_SERVICE_ID:
+        REPLY_MESSAGE_RETURN(message, service_id_, 0);
+
+      // 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, 0);
+
+      // Return the channel id for the current channel.
+      case TEST_OP_GET_THIS_CHANNEL_ID:
+        REPLY_MESSAGE_RETURN(message, message.GetChannelId(), 0);
+
+      // 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(), 0);
+        else
+          REPLY_ERROR_RETURN(message, ENOENT, 0);
+
+      // Test check channel feature.
+      case TEST_OP_CHECK_CHANNEL_ID: {
+        ChannelReference ref = 0;
+        if (message.Read(&ref, sizeof(ref)) < static_cast<ssize_t>(sizeof(ref)))
+          REPLY_ERROR_RETURN(message, EIO, 0);
+
+        const Status<int> ret = message.CheckChannel<TestChannel>(ref, nullptr);
+        REPLY_MESSAGE_RETURN(message, ret, 0);
+      }
+
+      case TEST_OP_CHECK_CHANNEL_OBJECT: {
+        std::shared_ptr<TestChannel> channel;
+        ChannelReference ref = 0;
+        if (message.Read(&ref, sizeof(ref)) < static_cast<ssize_t>(sizeof(ref)))
+          REPLY_ERROR_RETURN(message, EIO, 0);
+
+        const Status<int> ret =
+            message.CheckChannel<TestChannel>(ref, &channel);
+        if (!ret)
+          REPLY_MESSAGE_RETURN(message, ret, 0);
+
+        if (channel != nullptr)
+          REPLY_MESSAGE_RETURN(message, channel->channel_id(), 0);
+        else
+          REPLY_ERROR_RETURN(message, ENODATA, 0);
+      }
+
+      case TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE: {
+        ChannelReference ref = 0;
+        if (message.Read(&ref, sizeof(ref)) < static_cast<ssize_t>(sizeof(ref)))
+          REPLY_ERROR_RETURN(message, EIO, 0);
+
+        const Status<int> ret = message.CheckChannel<TestChannel>(
+            other_service_.get(), ref, nullptr);
+        REPLY_MESSAGE_RETURN(message, ret, 0);
+      }
+
+      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, 0);
+      }
+
+      case TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE: {
+        if (!other_service_)
+          REPLY_ERROR_RETURN(message, EINVAL, 0);
+
+        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, 0);
+      }
+
+      case TEST_OP_GET_THIS_PROCESS_ID:
+        REPLY_MESSAGE_RETURN(message, message.GetProcessId(), 0);
+
+      case TEST_OP_GET_THIS_THREAD_ID:
+        REPLY_MESSAGE_RETURN(message, message.GetThreadId(), 0);
+
+      case TEST_OP_GET_THIS_EUID:
+        REPLY_MESSAGE_RETURN(message, message.GetEffectiveUserId(), 0);
+
+      case TEST_OP_GET_THIS_EGID:
+        REPLY_MESSAGE_RETURN(message, message.GetEffectiveGroupId(), 0);
+
+      case TEST_OP_POLLIN_FROM_SERVICE:
+        REPLY_MESSAGE_RETURN(message, message.ModifyChannelEvents(0, EPOLLIN),
+                             0);
+
+      case TEST_OP_SEND_LARGE_DATA_RETURN_SUM: {
+        std::array<int, kLargeDataSize> data_array;
+        ssize_t size_to_read = data_array.size() * sizeof(int);
+        ssize_t read = message.Read(data_array.data(), size_to_read);
+        if (read < size_to_read)
+          REPLY_ERROR_RETURN(message, EIO, 0);
+        int sum = std::accumulate(data_array.begin(), data_array.end(), 0);
+        REPLY_MESSAGE_RETURN(message, sum, 0);
+      }
+
+      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::Create(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);
+    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);
+    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);
+    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));
+  }
+
+  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);
+  ASSERT_TRUE((EPOLLHUP & event.events) != 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.mk b/libs/vr/libperformance/Android.mk
new file mode 100644
index 0000000..aaacb1a
--- /dev/null
+++ b/libs/vr/libperformance/Android.mk
@@ -0,0 +1,43 @@
+# 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 := \
+	performance_client.cpp \
+	performance_rpc.cpp
+
+includeFiles := \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	libpdx_default_transport \
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	liblog \
+	libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libperformance\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libperformance
+include $(BUILD_STATIC_LIBRARY)
+
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.mk b/libs/vr/libposepredictor/Android.mk
new file mode 100644
index 0000000..030f79c
--- /dev/null
+++ b/libs/vr/libposepredictor/Android.mk
@@ -0,0 +1,51 @@
+# 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 := \
+        linear_pose_predictor.cpp \
+
+includeFiles := \
+        $(LOCAL_PATH)/include
+
+staticLibraries := \
+        libdvrcommon \
+        libsensor \
+        libpdx_default_transport \
+
+sharedLibraries := \
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_CFLAGS := -DLOG_TAG=\"libposepredictor\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libposepredictor
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+        linear_pose_predictor_tests.cpp \
+
+LOCAL_STATIC_LIBRARIES := libposepredictor $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := pose_predictor_tests
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libposepredictor/include/private/dvr/linear_pose_predictor.h b/libs/vr/libposepredictor/include/private/dvr/linear_pose_predictor.h
new file mode 100644
index 0000000..1efe938
--- /dev/null
+++ b/libs/vr/libposepredictor/include/private/dvr/linear_pose_predictor.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_POSE_PREDICTOR_H_
+#define ANDROID_DVR_POSE_PREDICTOR_H_
+
+#include <private/dvr/pose_predictor.h>
+
+namespace android {
+namespace dvr {
+
+// This class makes a linear prediction using the last two samples we received.
+class LinearPosePredictor : public PosePredictor {
+ public:
+  LinearPosePredictor() = default;
+
+  // Add a new sample.
+  void Add(const Sample& sample, DvrPoseAsync* out_pose) override;
+
+  // Predict using the last two samples.
+  void Predict(int64_t left_time_ns, int64_t right_time_ns,
+               DvrPoseAsync* out_pose) const override;
+
+ private:
+  // The index of the last sample we received.
+  size_t current_index_ = 0;
+
+  // The previous two samples.
+  Sample samples_[2];
+
+  // Experimental
+  bool forward_predict_angular_speed_ = false;
+
+  // Transient variables updated when a sample is added.
+  vec3d velocity_ = vec3d::Zero();
+  vec3d rotational_velocity_ = vec3d::Zero();
+  vec3d rotational_axis_ = vec3d::Zero();
+  double last_angular_speed_ = 0;
+  double angular_speed_ = 0;
+  double angular_accel_ = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/private/dvr/pose_predictor.h b/libs/vr/libposepredictor/include/private/dvr/pose_predictor.h
new file mode 100644
index 0000000..719edbe
--- /dev/null
+++ b/libs/vr/libposepredictor/include/private/dvr/pose_predictor.h
@@ -0,0 +1,47 @@
+#ifndef ANDROID_DVR_LINEAR_POSE_PREDICTOR_H_
+#define ANDROID_DVR_LINEAR_POSE_PREDICTOR_H_
+
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// This is an abstract base class for prediction 6dof pose given
+// a set of samples.
+//
+// TODO(okana): Create a framework for testing different subclasses for
+// performance and accuracy.
+class PosePredictor {
+ public:
+  PosePredictor() = default;
+  virtual ~PosePredictor() = default;
+
+  // Encapsulates a pose sample.
+  struct Sample {
+    vec3d position = vec3d::Zero();
+    quatd orientation = quatd::Identity();
+    int64_t time_ns = 0;
+  };
+
+  // Add a pose sample coming from the sensors.
+  // Returns this sample as a dvr pose.
+  //
+  // We will use the returned pose if prediction is not enabled.
+  virtual void Add(const Sample& sample, DvrPoseAsync* out_pose) = 0;
+
+  // Make a pose prediction for the left and right eyes at specific times.
+  virtual void Predict(int64_t left_time_ns, int64_t right_time_ns,
+                       DvrPoseAsync* out_pose) const = 0;
+
+  // Helpers
+  static double NsToSeconds(int64_t time_ns) { return time_ns / 1e9; }
+  static int64_t SecondsToNs(double seconds) {
+    return static_cast<int64_t>(seconds * 1e9);
+  }
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LINEAR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/linear_pose_predictor.cpp b/libs/vr/libposepredictor/linear_pose_predictor.cpp
new file mode 100644
index 0000000..a2ce2ca
--- /dev/null
+++ b/libs/vr/libposepredictor/linear_pose_predictor.cpp
@@ -0,0 +1,147 @@
+#include <cutils/log.h>
+
+#include <private/dvr/linear_pose_predictor.h>
+
+namespace android {
+namespace dvr {
+
+using AngleAxisd = Eigen::AngleAxis<double>;
+
+void LinearPosePredictor::Add(const Sample& sample, DvrPoseAsync* out_pose) {
+  // 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);
+
+  const double inverse_dt = 1. / pose_delta_time;
+  if (pose_delta_time > 0.0) {
+    velocity_ = (sample.position - previous_sample.position) * inverse_dt;
+  } else {
+    velocity_ = vec3d::Zero();
+  }
+
+  quatd delta_q = sample.orientation.inverse() * previous_sample.orientation;
+  // Check that delta_q.w() == 1, Eigen doesn't respect this convention. If
+  // delta_q.w() == -1, we'll get the opposite velocity.
+  if (delta_q.w() < 0) {
+    delta_q.w() = -delta_q.w();
+    delta_q.vec() = -delta_q.vec();
+  }
+  rotational_velocity_ = -2.0 * delta_q.vec() * inverse_dt;
+
+  // 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_ = vec3d(0.0, 1.0, 0.0);
+  if (angular_speed_ > 0.0) {
+    rotational_axis_ = rotational_velocity_ / angular_speed_;
+  }
+
+  out_pose->orientation = {static_cast<float>(sample.orientation.vec().x()),
+                           static_cast<float>(sample.orientation.vec().y()),
+                           static_cast<float>(sample.orientation.vec().z()),
+                           static_cast<float>(sample.orientation.w())};
+
+  out_pose->translation = {static_cast<float>(sample.position.x()),
+                           static_cast<float>(sample.position.y()),
+                           static_cast<float>(sample.position.z()), 0.0f};
+
+  out_pose->right_orientation = {
+      static_cast<float>(sample.orientation.vec().x()),
+      static_cast<float>(sample.orientation.vec().y()),
+      static_cast<float>(sample.orientation.vec().z()),
+      static_cast<float>(sample.orientation.w())};
+
+  out_pose->right_translation = {static_cast<float>(sample.position.x()),
+                                 static_cast<float>(sample.position.y()),
+                                 static_cast<float>(sample.position.z()), 0.0f};
+
+  out_pose->angular_velocity = {static_cast<float>(rotational_velocity_.x()),
+                                static_cast<float>(rotational_velocity_.y()),
+                                static_cast<float>(rotational_velocity_.z()),
+                                0.0f};
+
+  out_pose->velocity = {static_cast<float>(velocity_.x()),
+                        static_cast<float>(velocity_.y()),
+                        static_cast<float>(velocity_.z()), 0.0f};
+  out_pose->timestamp_ns = sample.time_ns;
+  out_pose->flags = DVR_POSE_FLAG_HEAD | DVR_POSE_FLAG_VALID;
+  memset(out_pose->pad, 0, sizeof(out_pose->pad));
+}
+
+void LinearPosePredictor::Predict(int64_t left_time_ns, int64_t right_time_ns,
+                                  DvrPoseAsync* out_pose) const {
+  const auto& sample = samples_[current_index_];
+
+  double dt = NsToSeconds(left_time_ns - sample.time_ns);
+  double r_dt = NsToSeconds(right_time_ns - sample.time_ns);
+
+  // Temporary forward prediction code.
+  auto start_t_head_future = sample.position + velocity_ * dt;
+  auto r_start_t_head_future = sample.position + velocity_ * r_dt;
+  double angle = angular_speed_ * dt;
+  double r_angle = angular_speed_ * r_dt;
+  if (__builtin_expect(forward_predict_angular_speed_, 0)) {
+    angle += 0.5 * angular_accel_ * dt * dt;
+    r_angle += 0.5 * angular_accel_ * r_dt * r_dt;
+  }
+  auto start_q_head_future =
+      sample.orientation * quatd(AngleAxisd(angle, rotational_axis_));
+  auto r_start_q_head_future =
+      sample.orientation * quatd(AngleAxisd(r_angle, rotational_axis_));
+
+  out_pose->orientation = {static_cast<float>(start_q_head_future.x()),
+                           static_cast<float>(start_q_head_future.y()),
+                           static_cast<float>(start_q_head_future.z()),
+                           static_cast<float>(start_q_head_future.w())};
+
+  out_pose->translation = {static_cast<float>(start_t_head_future.x()),
+                           static_cast<float>(start_t_head_future.y()),
+                           static_cast<float>(start_t_head_future.z()), 0.0f};
+
+  out_pose->right_orientation = {static_cast<float>(r_start_q_head_future.x()),
+                                 static_cast<float>(r_start_q_head_future.y()),
+                                 static_cast<float>(r_start_q_head_future.z()),
+                                 static_cast<float>(r_start_q_head_future.w())};
+
+  out_pose->right_translation = {static_cast<float>(r_start_t_head_future.x()),
+                                 static_cast<float>(r_start_t_head_future.y()),
+                                 static_cast<float>(r_start_t_head_future.z()),
+                                 0.0f};
+
+  out_pose->angular_velocity = {static_cast<float>(rotational_velocity_.x()),
+                                static_cast<float>(rotational_velocity_.y()),
+                                static_cast<float>(rotational_velocity_.z()),
+                                0.0f};
+
+  out_pose->velocity = {static_cast<float>(velocity_.x()),
+                        static_cast<float>(velocity_.y()),
+                        static_cast<float>(velocity_.z()), 0.0f};
+
+  out_pose->timestamp_ns = left_time_ns;
+  out_pose->flags = DVR_POSE_FLAG_HEAD | DVR_POSE_FLAG_VALID;
+  memset(out_pose->pad, 0, sizeof(out_pose->pad));
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libposepredictor/linear_pose_predictor_tests.cpp b/libs/vr/libposepredictor/linear_pose_predictor_tests.cpp
new file mode 100644
index 0000000..6c4f58a
--- /dev/null
+++ b/libs/vr/libposepredictor/linear_pose_predictor_tests.cpp
@@ -0,0 +1,185 @@
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include <private/dvr/linear_pose_predictor.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// For comparing expected and actual.
+constexpr double kAbsErrorTolerance = 1e-5;
+
+// The default rotation axis we will be using.
+const vec3d kRotationAxis = vec3d(1, 4, 3).normalized();
+
+// Linearly interpolate between a and b.
+vec3d lerp(const vec3d& a, const vec3d& b, double t) { return (b - a) * t + a; }
+
+// Linearly interpolate between two angles and return the resulting rotation as
+// a quaternion (around the kRotationAxis).
+quatd qlerp(double angle1, double angle2, double t) {
+  return quatd(
+      Eigen::AngleAxis<double>((angle2 - angle1) * t + angle1, kRotationAxis));
+}
+
+// Compare two positions.
+void TestPosition(const vec3d& expected, const float32x4_t& actual) {
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_NEAR(expected[i], static_cast<double>(actual[i]),
+                kAbsErrorTolerance);
+  }
+}
+
+// Compare two orientations.
+void TestOrientation(const quatd& expected, const float32x4_t& actual) {
+  // abs(expected.dot(actual)) > 1-eps
+  EXPECT_GE(std::abs(vec4d(actual[0], actual[1], actual[2], actual[3])
+                         .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 vec3d position1(0, 0, 0);
+  const vec3d position2(1, 2, 3);
+  const double angle1 = M_PI * 0.3;
+  const double angle2 = M_PI * 0.5;
+  const quatd orientation1(Eigen::AngleAxis<double>(angle1, kRotationAxis));
+  const quatd orientation2(Eigen::AngleAxis<double>(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
+  DvrPoseAsync start_pose, end_pose, extrapolated_pose;
+
+  predictor.Add(
+      PosePredictor::Sample{
+          .position = position1, .orientation = orientation1, .time_ns = t1_ns},
+      &start_pose);
+
+  // The start pose is passthough.
+  TestPosition(position1, start_pose.translation);
+  TestPosition(position1, start_pose.right_translation);
+  TestOrientation(orientation1, start_pose.orientation);
+  TestOrientation(orientation1, start_pose.right_orientation);
+  EXPECT_EQ(t1_ns, start_pose.timestamp_ns);
+
+  predictor.Add(
+      PosePredictor::Sample{
+          .position = position2, .orientation = orientation2, .time_ns = t2_ns},
+      &end_pose);
+
+  TestPosition(position2, end_pose.translation);
+  TestPosition(position2, end_pose.right_translation);
+  TestOrientation(orientation2, end_pose.orientation);
+  TestOrientation(orientation2, end_pose.right_orientation);
+  EXPECT_EQ(t2_ns, end_pose.timestamp_ns);
+
+  // Extrapolate from t1 - t2 to eval_[left/right].
+  predictor.Predict(eval_left_ns, eval_right_ns, &extrapolated_pose);
+
+  // The interpolation factors for left and right.
+  const auto left_t =
+      (eval_left_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+  EXPECT_EQ(2.3, left_t);
+
+  const auto right_t =
+      (eval_right_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+  EXPECT_EQ(3.1, right_t);
+
+  TestPosition(lerp(position1, position2, left_t),
+               extrapolated_pose.translation);
+  TestPosition(lerp(position1, position2, right_t),
+               extrapolated_pose.right_translation);
+  TestOrientation(qlerp(angle1, angle2, left_t), extrapolated_pose.orientation);
+  TestOrientation(qlerp(angle1, angle2, right_t),
+                  extrapolated_pose.right_orientation);
+}
+
+// Test three samples, where the last two samples have the same timestamp.
+TEST(LinearPosePredictorTest, DuplicateSamples) {
+  LinearPosePredictor predictor;
+
+  const vec3d position1(0, 0, 0);
+  const vec3d position2(1, 2, 3);
+  const vec3d position3(2, 2, 3);
+  const double angle1 = M_PI * 0.3;
+  const double angle2 = M_PI * 0.5;
+  const double angle3 = M_PI * 0.65;
+  const quatd orientation1(Eigen::AngleAxis<double>(angle1, kRotationAxis));
+  const quatd orientation2(Eigen::AngleAxis<double>(angle2, kRotationAxis));
+  const quatd orientation3(Eigen::AngleAxis<double>(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;
+  DvrPoseAsync start_pose, end_pose, extrapolated_pose;
+
+  predictor.Add(
+      PosePredictor::Sample{
+          .position = position1, .orientation = orientation1, .time_ns = t1_ns},
+      &start_pose);
+
+  predictor.Add(
+      PosePredictor::Sample{
+          .position = position2, .orientation = orientation2, .time_ns = t2_ns},
+      &end_pose);
+
+  {
+    // Extrapolate from t1 - t2 to eval_[left/right].
+    predictor.Predict(eval_left_ns, eval_right_ns, &extrapolated_pose);
+
+    // The interpolation factors for left and right.
+    const auto left_t =
+        (eval_left_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+    const auto right_t =
+        (eval_right_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+
+    // Test the result.
+    TestPosition(lerp(position1, position2, left_t),
+                 extrapolated_pose.translation);
+    TestPosition(lerp(position1, position2, right_t),
+                 extrapolated_pose.right_translation);
+    TestOrientation(qlerp(angle1, angle2, left_t),
+                    extrapolated_pose.orientation);
+    TestOrientation(qlerp(angle1, angle2, right_t),
+                    extrapolated_pose.right_orientation);
+  }
+
+  // Sending a duplicate sample here.
+  predictor.Add(
+      PosePredictor::Sample{
+          .position = position3, .orientation = orientation3, .time_ns = t2_ns},
+      &end_pose);
+
+  {
+    // Extrapolate from t1 - t2 to eval_[left/right].
+    predictor.Predict(eval_left_ns, eval_right_ns, &extrapolated_pose);
+
+    // The interpolation factors for left and right.
+    const auto left_t =
+        (eval_left_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+    const auto right_t =
+        (eval_right_ns - t1_ns) / static_cast<double>(t2_ns - t1_ns);
+
+    // Test the result.
+    TestPosition(lerp(position1, position3, left_t),
+                 extrapolated_pose.translation);
+    TestPosition(lerp(position1, position3, right_t),
+                 extrapolated_pose.right_translation);
+    TestOrientation(qlerp(angle1, angle3, left_t),
+                    extrapolated_pose.orientation);
+    TestOrientation(qlerp(angle1, angle3, right_t),
+                    extrapolated_pose.right_orientation);
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libsensor/Android.mk b/libs/vr/libsensor/Android.mk
new file mode 100644
index 0000000..db1514d
--- /dev/null
+++ b/libs/vr/libsensor/Android.mk
@@ -0,0 +1,78 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	pose_client.cpp \
+	sensor_client.cpp
+
+includeFiles := \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	libbufferhub \
+	libchrome \
+	libdvrcommon \
+	libpdx_default_transport \
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	libhardware \
+	liblog \
+	libutils \
+	libevent
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE := libsensor
+include $(BUILD_STATIC_LIBRARY)
+
+
+testFiles := \
+  tests/sensor_app_tests.cpp
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := sensor_app_tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  $(testFiles) \
+
+LOCAL_C_INCLUDES := \
+  $(includeFiles) \
+
+LOCAL_SHARED_LIBRARIES := \
+  libEGL \
+  libGLESv1_CM \
+  libGLESv2 \
+  libvulkan \
+  libsync \
+  $(sharedLibraries) \
+
+LOCAL_STATIC_LIBRARIES := \
+  libgmock_main \
+  libgmock \
+  libdisplay \
+  libeds \
+  libsensor \
+  libdvrgraphics \
+  $(staticLibraries) \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/vr/libsensor/include/CPPLINT.cfg b/libs/vr/libsensor/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libsensor/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libsensor/include/dvr/pose_client.h b/libs/vr/libsensor/include/dvr/pose_client.h
new file mode 100644
index 0000000..ed75f84
--- /dev/null
+++ b/libs/vr/libsensor/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/libsensor/include/private/dvr/pose-ipc.h b/libs/vr/libsensor/include/private/dvr/pose-ipc.h
new file mode 100644
index 0000000..908030e
--- /dev/null
+++ b/libs/vr/libsensor/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/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/libsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libsensor/include/private/dvr/pose_client_internal.h
new file mode 100644
index 0000000..66c4c7c
--- /dev/null
+++ b/libs/vr/libsensor/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/libsensor/include/private/dvr/sensor-ipc.h b/libs/vr/libsensor/include/private/dvr/sensor-ipc.h
new file mode 100644
index 0000000..7e8b9e5
--- /dev/null
+++ b/libs/vr/libsensor/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/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/libsensor/include/private/dvr/sensor_client.h b/libs/vr/libsensor/include/private/dvr/sensor_client.h
new file mode 100644
index 0000000..15a9b8f
--- /dev/null
+++ b/libs/vr/libsensor/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/libsensor/include/private/dvr/sensor_constants.h b/libs/vr/libsensor/include/private/dvr/sensor_constants.h
new file mode 100644
index 0000000..8fa87b3
--- /dev/null
+++ b/libs/vr/libsensor/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/libsensor/pose_client.cpp b/libs/vr/libsensor/pose_client.cpp
new file mode 100644
index 0000000..a65d668
--- /dev/null
+++ b/libs/vr/libsensor/pose_client.cpp
@@ -0,0 +1,339 @@
+#define LOG_TAG "PoseClient"
+#include <dvr/pose_client.h>
+
+#include <stdint.h>
+
+#include <base/logging.h>
+#include <cutils/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;
+
+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 >= static_cast<int32_t>(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);
+    LOG(INFO) << "Mapped pose data translation "
+              << mapped_pose_buffer_->ring[0].translation[0] << ','
+              << mapped_pose_buffer_->ring[0].translation[1] << ','
+              << mapped_pose_buffer_->ring[0].translation[2] << ", quat "
+              << 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 >= static_cast<int32_t>(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);
+    LOG(INFO) << "Mapped controller " << controller_id
+              << " pose data translation "
+              << client_state.mapped_pose_buffer[0].translation[0] << ','
+              << client_state.mapped_pose_buffer[0].translation[1] << ','
+              << client_state.mapped_pose_buffer[0].translation[2] << ", quat "
+              << 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/libsensor/sensor_client.cpp b/libs/vr/libsensor/sensor_client.cpp
new file mode 100644
index 0000000..1c240f5
--- /dev/null
+++ b/libs/vr/libsensor/sensor_client.cpp
@@ -0,0 +1,79 @@
+#define LOG_TAG "SensorClient"
+#include <private/dvr/sensor_client.h>
+
+#include <cutils/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/libsensor/tests/sensor_app_tests.cpp b/libs/vr/libsensor/tests/sensor_app_tests.cpp
new file mode 100644
index 0000000..8cd6f79
--- /dev/null
+++ b/libs/vr/libsensor/tests/sensor_app_tests.cpp
@@ -0,0 +1,131 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <math.h>
+
+#include <base/logging.h>
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.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)
+        LOG(ERROR) << "vsync did not increment: " << schedule.vsync_count;
+      if (pose.timestamp_ns == last_pose.timestamp_ns)
+        LOG(ERROR) << "timestamp did not change: " << 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)
+        LOG(ERROR) << "vsync did not increment: " << schedule.vsync_count;
+      if (pose.timestamp_ns == last_pose.timestamp_ns)
+        LOG(ERROR) << "timestamp did not change: " << 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..027c18d 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -607,6 +607,7 @@
 
 #ifndef EGL_ANDROID_create_native_client_buffer
 #define EGL_ANDROID_create_native_client_buffer 1
+#define EGL_LAYER_COUNT_ANDROID 0x3434
 #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
@@ -631,12 +632,16 @@
 #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_REQUESTED_PRESENT_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_COMPOSITION_LATCH_TIME_ANDROID 0x3150
+#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151
+#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152
+#define EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153
+#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155
+#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3156
+#define EGL_READS_DONE_TIME_ANDROID 0x3157
 #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);
@@ -646,6 +651,13 @@
 #endif
 #endif
 
+#ifndef EGL_KHR_pixel_format_float
+#define EGL_KHR_pixel_format_float 1
+#define EGL_COLOR_COMPONENT_TYPE_EXT 0x3339  // eglChooseConfig and eglGetConfigAttrib attribute
+#define EGL_COLOR_COMPONENT_TYPE_FIXED_EXT 0x333A  // Attribute value for COLOR_COMPONENT_TYPE
+#define EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT 0x333B  // Attribute value for COLOR_COMPONENT_TYPE
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 69e3c13..01d7bbb 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -102,24 +102,6 @@
     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);
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index f8e25b4..1672397 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -118,6 +118,7 @@
         "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 "
@@ -477,26 +478,50 @@
         // modify the EGLconfig's format before setting the native window's
         // format.
 
-        // by default, just pick RGBA_8888
+        EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_COLOR_COMPONENT_TYPE_EXT,
+                                    &componentType);
+
+        // by default, just pick appropriate RGBA
         EGLint format = HAL_PIXEL_FORMAT_RGBA_8888;
+        if (dp->haveExtension("EGL_EXT_pixel_format_float") &&
+            (componentType == EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT)) {
+            format = HAL_PIXEL_FORMAT_RGBA_FP16;
+        }
         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;
             }
         }
 
@@ -1141,6 +1166,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, nullptr);
+
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return (const char *) NULL;
 
@@ -1196,6 +1231,9 @@
     egl_surface_t * const s = get_surface(surface);
 
     if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
+        if (!s->win.get()) {
+            setError(EGL_BAD_SURFACE, EGL_FALSE);
+        }
         int err = native_window_set_auto_refresh(s->win.get(),
             value ? true : false);
         return (err == NO_ERROR) ? EGL_TRUE :
@@ -1204,8 +1242,13 @@
 
 #if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
     if (attribute == EGL_TIMESTAMPS_ANDROID) {
-        s->enableTimestamps = value;
-        return EGL_TRUE;
+        if (!s->win.get()) {
+            return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        }
+        int err = native_window_enable_frame_timestamps(
+                s->win.get(), value ? true : false);
+        return (err == NO_ERROR) ? EGL_TRUE :
+            setError(EGL_BAD_SURFACE, EGL_FALSE);
     }
 #endif
 
@@ -1794,6 +1837,7 @@
     uint32_t width = 0;
     uint32_t height = 0;
     uint32_t format = 0;
+    uint32_t layer_count = 1;
     uint32_t red_size = 0;
     uint32_t green_size = 0;
     uint32_t blue_size = 0;
@@ -1819,6 +1863,7 @@
                 GET_NONNEGATIVE_VALUE(EGL_GREEN_SIZE, green_size);
                 GET_NONNEGATIVE_VALUE(EGL_BLUE_SIZE, blue_size);
                 GET_NONNEGATIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
+                GET_NONNEGATIVE_VALUE(EGL_LAYER_COUNT_ANDROID, layer_count);
                 case EGL_NATIVE_BUFFER_USAGE_ANDROID:
                     if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
                         usage |= GRALLOC_USAGE_PROTECTED;
@@ -1848,7 +1893,7 @@
                alpha_size == 0) {
         format = HAL_PIXEL_FORMAT_RGB_565;
     } else {
-        ALOGE("Invalid native pixel format { r=%d, g=%d, b=%d, a=%d }",
+        ALOGE("Invalid native pixel format { r=%u, g=%u, b=%u, a=%u }",
                 red_size, green_size, blue_size, alpha_size);
         return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
     }
@@ -1893,7 +1938,9 @@
     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);
+    err = data.writeUint32(layer_count);
+    CHECK_ERROR_CONDITION("Unable to write layer count");
+     err = data.writeUint32(usage);
     CHECK_ERROR_CONDITION("Unable to write usage");
     err = data.writeUtf8AsUtf16(
             std::string("[eglCreateNativeClientBufferANDROID pid ") +
@@ -1910,12 +1957,13 @@
 
     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);
+        ALOGE("Unable to create native buffer "
+                "{ w=%u, h=%u, f=%u, u=%#x, lc=%u}: %#x", width, height, format,
+                usage, layer_count, err);
         goto error_condition;
     }
-    ALOGD("Created new native buffer %p { w=%d, h=%d, f=%d, u=%#x }",
-            gBuffer, width, height, format, usage);
+    ALOGV("Created new native buffer %p { w=%u, h=%u, f=%u, u=%#x, lc=%u}",
+            gBuffer, width, height, format, usage, layer_count);
     return static_cast<EGLClientBuffer>(gBuffer->getNativeBuffer());
 
 #undef CHECK_ERROR_CONDITION
@@ -2002,66 +2050,87 @@
 
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) {
-        setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
     }
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_SURFACE, 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->win.get()) {
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
     }
 
-    nsecs_t* postedTime = nullptr;
+    nsecs_t* requestedPresentTime = nullptr;
     nsecs_t* acquireTime = nullptr;
-    nsecs_t* refreshStartTime = nullptr;
+    nsecs_t* latchTime = nullptr;
+    nsecs_t* firstRefreshStartTime = nullptr;
     nsecs_t* GLCompositionDoneTime = nullptr;
+    nsecs_t* lastRefreshStartTime = nullptr;
+    nsecs_t* displayPresentTime = nullptr;
     nsecs_t* displayRetireTime = 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:
+            case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+                firstRefreshStartTime = &values[i];
+                break;
+            case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+                lastRefreshStartTime = &values[i];
+                break;
+            case EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID:
                 GLCompositionDoneTime = &values[i];
                 break;
+            case EGL_DISPLAY_PRESENT_TIME_ANDROID:
+                displayPresentTime = &values[i];
+                break;
             case EGL_DISPLAY_RETIRE_TIME_ANDROID:
                 displayRetireTime = &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, EGL_FALSE);
         }
     }
 
     status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
-            postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime,
-            displayRetireTime, releaseTime);
+            requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+            lastRefreshStartTime, GLCompositionDoneTime, displayPresentTime,
+            displayRetireTime, dequeueReadyTime, releaseTime);
 
-    if (ret != NO_ERROR) {
-        setError(EGL_BAD_ACCESS, EGL_FALSE);
-        return EGL_FALSE;
+    switch (ret) {
+      case NO_ERROR:
+        return EGL_TRUE;
+      case NAME_NOT_FOUND:
+        return setError(EGL_BAD_ACCESS, EGL_FALSE);
+      case INVALID_OPERATION:
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+      case BAD_VALUE:
+        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+      default:
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
     }
-
-    return EGL_TRUE;
 }
 
 EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
@@ -2071,25 +2140,44 @@
 
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) {
-        setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
     }
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->win.get();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, EGL_FALSE);
     }
 
     switch (timestamp) {
 #if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
-        case EGL_QUEUE_TIME_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_FINISHED_TIME_ANDROID:
+        case EGL_DEQUEUE_READY_TIME_ANDROID:
         case EGL_READS_DONE_TIME_ANDROID:
             return EGL_TRUE;
+        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;
+        }
+        case EGL_DISPLAY_RETIRE_TIME_ANDROID: {
+            int value = 0;
+            window->query(window,
+                    NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_RETIRE, &value);
+            return value == 0 ? EGL_FALSE : EGL_TRUE;
+        }
 #endif
         default:
             return EGL_FALSE;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 6a76737..7fc5609 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -68,7 +68,7 @@
         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)
 {}
 
 egl_surface_t::~egl_surface_t() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 3150ba6..8ceba1d 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -139,7 +139,6 @@
     EGLConfig config;
     sp<ANativeWindow> win;
     egl_connection_t const* cnx;
-    bool enableTimestamps;
 private:
     bool connected;
     void disconnect();
diff --git a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
index 51c6c61..4c5551c 100644
--- a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
+++ b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
@@ -20,7 +20,7 @@
 
 Version
 
-    Version 1, January 19, 2016
+    Version 1.1, October 26, 2016
 
 Number
 
@@ -53,6 +53,7 @@
 
 New Tokens
 
+    EGL_NATIVE_BUFFER_LAYER_COUNT_ANDROID 0x3434
     EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143
     EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001
     EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002
@@ -103,6 +104,8 @@
       | EGL_ALPHA_SIZE                  | The bits of Alpha in | 0             |
       |                                 | the color buffer     |               |
       |                                 | buffer data          |               |
+      | EGL_LAYER_COUNT_ANDROID         | The number of image  | 1             |
+      |                                 | layers in the buffer |               |
       | EGL_NATIVE_BUFFER_USAGE_ANDROID | The usage bits of    | 0             |
       |                                 | the buffer data      |               |
       +---------------------------------+----------------------+---------------+
@@ -114,8 +117,10 @@
     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:
+    valid pixel format for the implementation. The value of
+    EGL_LAYER_COUNT_ANDROID must be a valid number of image layers 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
@@ -182,6 +187,10 @@
 
 Revision History
 
+#3 (Craig Donner, October 26, 2016)
+    - Added EGL_LAYER_COUNT_ANDROID for creating buffers that back texture
+    arrays.
+
 #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.
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
index 30337ad..7aa0d30 100644
--- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -38,8 +38,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 or retire 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
@@ -67,12 +67,16 @@
 New Tokens
 
     EGL_TIMESTAMPS_ANDROID 0x314D
-    EGL_QUEUE_TIME_ANDROID 0x314E
+    EGL_REQUESTED_PRESENT_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_COMPOSITION_LATCH_TIME_ANDROID 0x3150
+    EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151
+    EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152
+    EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153
+    EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154
+    EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155
+    EGL_DEQUEUE_READY_TIME_ANDROID 0x3156
+    EGL_READS_DONE_TIME_ANDROID 0x3157
 
 Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
 "Surface Attributes", page 43:
@@ -98,9 +102,9 @@
     allows querying various timestamps related to the composition and display of
     a window surface.
 
-    The framesAgo parameter indicates how many frames before the last posted
+    The framesAgo parameter indicates how many frames before the last queued
     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
+    last queued 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.
@@ -112,22 +116,41 @@
     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
+        - 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_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_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was
           replaced by the next frame on-screen.
+        - 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
+    Not all implementations may support all of the above timestamp queries. The
     function
 
         EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
@@ -143,3 +166,12 @@
 
 #1 (Pablo Ceballos, May 31, 2016)
     - Initial draft.
+
+#2 (Brian Anderson, July 22, 2016)
+    - Replace EGL_QUEUE_TIME_ANDROID with EGL_REQUESTED_PRESENT_TIME_ANDROID.
+    - Add DISPLAY_PRESENT_TIME_ANDROID.
+
+#3 (Brian Anderson, November 30, 2016)
+    - Add EGL_COMPOSITION_LATCH_TIME_ANDROID,
+      EGL_LAST_COMPOSITION_START_TIME_ANDROID, and
+      EGL_DEQUEUE_READY_TIME_ANDROID.
diff --git a/opengl/specs/README b/opengl/specs/README
index f0c024e..8a3a7aa 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -20,10 +20,14 @@
 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)
+0x314E               EGL_REQUESTED_PRESENT_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)
+0x3150               EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3151               EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3152               EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3153               EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3154               EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3155               EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3156               EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3157               EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3158 - 0x315F      (unused)
diff --git a/opengl/tests/EGLTest/Android.mk b/opengl/tests/EGLTest/Android.mk
index 80e4867..b772450 100644
--- a/opengl/tests/EGLTest/Android.mk
+++ b/opengl/tests/EGLTest/Android.mk
@@ -17,6 +17,7 @@
 	libbinder \
 	libutils \
 	libgui \
+	libbase \
 
 LOCAL_C_INCLUDES := \
     bionic/libc/private \
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index d69a275..2b9c38e 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -24,6 +24,8 @@
 
 namespace android {
 
+#define EGL_UNSIGNED_TRUE static_cast<EGLBoolean>(EGL_TRUE)
+
 class EGLTest : public ::testing::Test {
 protected:
     EGLDisplay mEglDisplay;
@@ -48,7 +50,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 +67,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);
@@ -139,23 +141,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);
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/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/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..518c369 100755
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -720,6 +720,9 @@
         <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"/>
     </enums>
 
     <enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)">
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..3b0b4e9
--- /dev/null
+++ b/services/audiomanager/IPlayer.cpp
@@ -0,0 +1,109 @@
+/*
+**
+** 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,
+};
+
+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);
+    }
+};
+
+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;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+} // namespace android
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/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 89475e9..b8ca812 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));
                 }
             }
         }
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/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 7b10319..c41630a 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -10,7 +10,7 @@
     OrientationSensor.cpp \
     RecentEventLogger.cpp \
     RotationVectorSensor.cpp \
-    SensorDevice.cpp \
+    SensorDirectConnection.cpp \
     SensorEventConnection.cpp \
     SensorFusion.cpp \
     SensorInterface.cpp \
@@ -19,13 +19,19 @@
     SensorService.cpp \
     SensorServiceUtils.cpp \
 
-
 LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
 
 LOCAL_CFLAGS += -Wall -Werror -Wextra
 
 LOCAL_CFLAGS += -fvisibility=hidden
 
+ifeq ($(ENABLE_TREBLE), true)
+LOCAL_SRC_FILES += SensorDeviceTreble.cpp
+LOCAL_CFLAGS += -DENABLE_TREBLE=1
+else
+LOCAL_SRC_FILES += SensorDevice.cpp
+endif
+
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libhardware \
@@ -35,7 +41,21 @@
     libbinder \
     libui \
     libgui \
-    libcrypto
+    libcrypto \
+
+ifeq ($(ENABLE_TREBLE), true)
+
+LOCAL_SHARED_LIBRARIES += \
+    libbase \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
+    android.hardware.sensors@1.0
+
+LOCAL_STATIC_LIBRARIES := \
+    android.hardware.sensors@1.0-convert
+
+endif  # ENABLE_TREBLE
 
 LOCAL_MODULE:= libsensorservice
 
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index ac03742..de0321d 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -14,23 +14,27 @@
  * limitations under the License.
  */
 
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <sys/types.h>
 
-#include <utils/Atomic.h>
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
+#include "SensorDevice.h"
+#include "SensorService.h"
+
 
 #include <binder/BinderService.h>
 #include <binder/Parcel.h>
 #include <binder/IServiceManager.h>
-
+#include <cutils/ashmem.h>
 #include <hardware/sensors.h>
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
 
-#include "SensorDevice.h"
-#include "SensorService.h"
+#include <inttypes.h>
+#include <math.h>
+#include <sys/mman.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sstream>
+#include <unistd.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -53,13 +57,15 @@
                 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);
+
+            if (mSensorDevice->common.version < SENSORS_DEVICE_API_VERSION_1_3) {
+                ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3, ignoring sensors reported by this device");
+                count = 0;
+            }
+
             mActivationCount.setCapacity(count);
             Info model;
             for (size_t i=0 ; i<size_t(count) ; i++) {
@@ -164,6 +170,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;
         }
 
@@ -347,6 +355,7 @@
 void SensorDevice::enableAllSensors() {
     Mutex::Autolock _l(mLock);
     mDisabledClients.clear();
+    ALOGI("cleared mDisabledClients");
     const int halVersion = getHalDeviceVersion();
     for (size_t i = 0; i< mActivationCount.size(); ++i) {
         Info& info = mActivationCount.editValueAt(i);
@@ -381,7 +390,7 @@
 
 void SensorDevice::disableAllSensors() {
     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) {
@@ -395,6 +404,7 @@
            // 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));
            }
         }
     }
@@ -480,6 +490,45 @@
     mDisabledClients.remove(ident);
 }
 
+int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
+
+    if (!isDirectReportSupported()) {
+        return INVALID_OPERATION;
+    }
+
+    Mutex::Autolock _l(mLock);
+
+    int32_t channelHandle = mSensorDevice->register_direct_channel(
+            mSensorDevice, memory, -1 /*channel_handle*/);
+    return channelHandle;
+}
+
+void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
+    Mutex::Autolock _l(mLock);
+
+    mSensorDevice->register_direct_channel(mSensorDevice, nullptr, channelHandle);
+}
+
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+        const struct sensors_direct_cfg_t *config) {
+
+    if (!isDirectReportSupported()) {
+        return INVALID_OPERATION;
+    }
+
+    Mutex::Autolock _l(mLock);
+
+    int32_t ret = mSensorDevice->config_direct_report(
+            mSensorDevice, sensorHandle, channelHandle, config);
+    ALOGE_IF(ret < 0, "SensorDevice::configureDirectChannel ret %d", ret);
+    return ret;
+}
+
+bool SensorDevice::isDirectReportSupported() const {
+    bool ret = mSensorDevice->register_direct_channel != nullptr
+            && mSensorDevice->config_direct_report != nullptr;
+    return ret;
+}
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index d340da3..7dd256a 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -26,20 +26,31 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <string>
+
+#ifdef ENABLE_TREBLE
+#include <map>
+
+#include "android/hardware/sensors/1.0/ISensors.h"
+#endif
 
 // ---------------------------------------------------------------------------
 
 namespace android {
+
 // ---------------------------------------------------------------------------
 using SensorServiceUtil::Dumpable;
 
 class SensorDevice : public Singleton<SensorDevice>, public Dumpable {
 public:
     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 +58,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 +76,15 @@
     virtual std::string dump() const;
 private:
     friend class Singleton<SensorDevice>;
+#ifdef ENABLE_TREBLE
+    sp<android::hardware::sensors::V1_0::ISensors> mSensors;
+    Vector<sensor_t> mSensorList;
+    std::map<int32_t, sensor_t*> mConnectedDynamicSensors;
+#else
     sensors_poll_device_1_t* mSensorDevice;
     struct sensors_module_t* mSensorModule;
+#endif
+
     static const nsecs_t MINIMUM_EVENTS_PERIOD =   1000000; // 1000 Hz
     mutable Mutex mLock; // protect mActivationCount[].batchParams
     // fixed-size array after construction
@@ -111,6 +137,20 @@
 
     bool isClientDisabled(void* ident);
     bool isClientDisabledLocked(void* ident);
+
+#ifdef ENABLE_TREBLE
+    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;
+#endif  // ENABLE_TREBLE
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDeviceTreble.cpp b/services/sensorservice/SensorDeviceTreble.cpp
new file mode 100644
index 0000000..3edd50b
--- /dev/null
+++ b/services/sensorservice/SensorDeviceTreble.cpp
@@ -0,0 +1,639 @@
+/*
+ * 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 <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <android-base/logging.h>
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+
+#include "SensorDevice.h"
+#include "SensorService.h"
+
+#include <sensors/convert.h>
+
+using android::hardware::hidl_vec;
+
+using namespace android::hardware::sensors::V1_0;
+using namespace android::hardware::sensors::V1_0::implementation;
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SensorDevice)
+
+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() {
+    mSensors = ISensors::getService();
+
+    if (mSensors == NULL) {
+        return;
+    }
+
+    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);
+                    mSensorList.push_back(sensor);
+
+                    mActivationCount.add(list[i].sensorHandle, model);
+
+                    mSensors->activate(list[i].sensorHandle, 0 /* enabled */);
+                }
+            });
+
+    mIsDirectReportSupported =
+           (mSensors->unregisterDirectChannel(-1) != Result::INVALID_OPERATION);
+}
+
+void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) {
+    if (connected) {
+        Info model;
+        mActivationCount.add(handle, model);
+        mSensors->activate(handle, 0 /* enabled */);
+    } else {
+        mActivationCount.removeItem(handle);
+    }
+}
+
+std::string SensorDevice::dump() const {
+    if (mSensors == NULL) return "HAL not initialized\n";
+
+    String8 result;
+    mSensors->getSensorsList([&](const auto &list) {
+            const size_t count = list.size();
+
+            result.appendFormat(
+                "Total %zu h/w sensors, %zu running:\n",
+                count,
+                mActivationCount.size());
+
+            Mutex::Autolock _l(mLock);
+            for (size_t i = 0 ; i < count ; i++) {
+                const Info& info = mActivationCount.valueFor(
+                    list[i].sensorHandle);
+
+                if (info.batchParams.isEmpty()) continue;
+                result.appendFormat(
+                    "0x%08x) active-count = %zu; ",
+                    list[i].sensorHandle,
+                    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 ? ", " : "");
+                }
+                result.appendFormat(
+                        "}, selected = %.1f ms; ",
+                        info.bestBatchParams.batchDelay / 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 ? ", " : "");
+                }
+
+                result.appendFormat(
+                        "}, selected = %.1f ms\n",
+                        info.bestBatchParams.batchTimeout / 1e6f);
+            }
+        });
+
+    return result.string();
+}
+
+ssize_t SensorDevice::getSensorList(sensor_t const** list) {
+    *list = &mSensorList[0];
+
+    return mSensorList.size();
+}
+
+status_t SensorDevice::initCheck() const {
+    return mSensors != NULL ? NO_ERROR : NO_INIT;
+}
+
+ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
+    if (mSensors == NULL) return NO_INIT;
+
+    ssize_t err;
+
+    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);
+                }
+            });
+
+    return err;
+}
+
+void SensorDevice::autoDisable(void *ident, int handle) {
+    Info& info( mActivationCount.editValueFor(handle) );
+    Mutex::Autolock _l(mLock);
+    info.removeBatchParamsForIdent(ident);
+}
+
+status_t SensorDevice::activate(void* ident, int handle, int enabled) {
+    if (mSensors == NULL) return NO_INIT;
+
+    status_t err(NO_ERROR);
+    bool actuateHardware = false;
+
+    Mutex::Autolock _l(mLock);
+    Info& info( mActivationCount.editValueFor(handle) );
+
+    ALOGD_IF(DEBUG_CONNECTIONS,
+             "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu",
+             ident, handle, enabled, info.batchParams.size());
+
+    if (enabled) {
+        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;
+        }
+
+        if (info.batchParams.indexOfKey(ident) >= 0) {
+          if (info.numActiveClients() == 1) {
+              // This is the first connection, we need to activate the underlying h/w sensor.
+              actuateHardware = true;
+          }
+        } else {
+            // Log error. Every activate call should be preceded by a batch() call.
+            ALOGE("\t >>>ERROR: activate called without batch");
+        }
+    } 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 {
+                // 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);
+                mSensors->batch(
+                        handle,
+                        info.bestBatchParams.batchDelay,
+                        info.bestBatchParams.batchTimeout);
+            }
+        } else {
+            // sensor wasn't enabled for this ident
+        }
+
+        if (isClientDisabledLocked(ident)) {
+            return NO_ERROR;
+        }
+    }
+
+    if (actuateHardware) {
+        ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
+                 enabled);
+        err = StatusFromResult(mSensors->activate(handle, enabled));
+        ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
+                 strerror(-err));
+
+        if (err != NO_ERROR && enabled) {
+            // Failure when enabling the sensor. Clean up on failure.
+            info.removeBatchParamsForIdent(ident);
+        }
+    }
+
+    return err;
+}
+
+status_t SensorDevice::batch(
+        void* ident,
+        int handle,
+        int flags,
+        int64_t samplingPeriodNs,
+        int64_t maxBatchReportLatencyNs) {
+    if (mSensors == NULL) return NO_INIT;
+
+    if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
+        samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
+    }
+
+    ALOGD_IF(DEBUG_CONNECTIONS,
+             "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64,
+             ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
+
+    Mutex::Autolock _l(mLock);
+    Info& info(mActivationCount.editValueFor(handle));
+
+    if (info.batchParams.indexOfKey(ident) < 0) {
+        BatchParams params(flags, samplingPeriodNs, maxBatchReportLatencyNs);
+        info.batchParams.add(ident, params);
+    } else {
+        // A batch has already been called with this ident. Update the batch parameters.
+        info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs);
+    }
+
+    BatchParams prevBestBatchParams = info.bestBatchParams;
+    // Find the minimum of all timeouts and batch_rates for this sensor.
+    info.selectBatchParams();
+
+    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);
+
+    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) {
+        ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %" PRId64 " %" PRId64, handle,
+                 info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+                 info.bestBatchParams.batchTimeout);
+        err = StatusFromResult(
+                mSensors->batch(
+                    handle,
+                    info.bestBatchParams.batchDelay,
+                    info.bestBatchParams.batchTimeout));
+        if (err != NO_ERROR) {
+            ALOGE("sensor batch failed %p %d %d %" PRId64 " %" PRId64 " err=%s",
+                  mSensors.get(), handle,
+                  info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+                  info.bestBatchParams.batchTimeout, strerror(-err));
+            info.removeBatchParamsForIdent(ident);
+        }
+    }
+    return err;
+}
+
+status_t SensorDevice::setDelay(void* ident, int handle, int64_t samplingPeriodNs) {
+    if (mSensors == NULL) 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 StatusFromResult(
+            mSensors->batch(handle, info.bestBatchParams.batchDelay, 0));
+}
+
+int SensorDevice::getHalDeviceVersion() const {
+    if (mSensors == NULL) return -1;
+    return SENSORS_DEVICE_API_VERSION_1_4;
+}
+
+status_t SensorDevice::flush(void* ident, int handle) {
+    if (isClientDisabled(ident)) return INVALID_OPERATION;
+    ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
+    return StatusFromResult(mSensors->flush(handle));
+}
+
+bool SensorDevice::isClientDisabled(void* ident) {
+    Mutex::Autolock _l(mLock);
+    return isClientDisabledLocked(ident);
+}
+
+bool SensorDevice::isClientDisabledLocked(void* ident) {
+    return mDisabledClients.indexOf(ident) >= 0;
+}
+
+void SensorDevice::enableAllSensors() {
+    Mutex::Autolock _l(mLock);
+    mDisabledClients.clear();
+    ALOGI("cleared mDisabledClients");
+    for (size_t i = 0; i< mActivationCount.size(); ++i) {
+        Info& info = mActivationCount.editValueAt(i);
+        if (info.batchParams.isEmpty()) continue;
+        info.selectBatchParams();
+        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 = StatusFromResult(
+                mSensors->batch(
+                    sensor_handle,
+                    info.bestBatchParams.batchDelay,
+                    info.bestBatchParams.batchTimeout));
+        ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
+
+        if (err == NO_ERROR) {
+            err = StatusFromResult(
+                    mSensors->activate(sensor_handle, 1 /* enabled */));
+            ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
+        }
+    }
+}
+
+void SensorDevice::disableAllSensors() {
+    Mutex::Autolock _l(mLock);
+   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);
+           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]);
+
+    Event ev;
+    convertFromSensorEvent(*injected_sensor_event, &ev);
+
+    return StatusFromResult(mSensors->injectSensorData(ev));
+}
+
+status_t SensorDevice::setMode(uint32_t mode) {
+
+     return StatusFromResult(
+             mSensors->setOperationMode(
+                 static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
+}
+
+// ---------------------------------------------------------------------------
+
+int SensorDevice::Info::numActiveClients() {
+    SensorDevice& device(SensorDevice::getInstance());
+    int num = 0;
+    for (size_t i = 0; i < batchParams.size(); ++i) {
+        if (!device.isClientDisabledLocked(batchParams.keyAt(i))) {
+            ++num;
+        }
+    }
+    return num;
+}
+
+status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int flags,
+                                                    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)",
+              ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index));
+        return BAD_INDEX;
+    }
+    BatchParams& params = batchParams.editValueAt(index);
+    params.flags = flags;
+    params.batchDelay = samplingPeriodNs;
+    params.batchTimeout = maxBatchReportLatencyNs;
+    return NO_ERROR;
+}
+
+void SensorDevice::Info::selectBatchParams() {
+    BatchParams bestParams(0, -1, -1);
+    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 (bestParams.batchTimeout == -1 || params.batchTimeout < bestParams.batchTimeout) {
+            bestParams.batchTimeout = params.batchTimeout;
+        }
+    }
+    bestBatchParams = bestParams;
+}
+
+ssize_t SensorDevice::Info::removeBatchParamsForIdent(void* ident) {
+    ssize_t idx = batchParams.removeItem(ident);
+    if (idx >= 0) {
+        selectBatchParams();
+    }
+    return idx;
+}
+
+void SensorDevice::notifyConnectionDestroyed(void* ident) {
+    Mutex::Autolock _l(mLock);
+    mDisabledClients.remove(ident);
+}
+
+int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
+    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;
+    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) {
+    Mutex::Autolock _l(mLock);
+    mSensors->unregisterDirectChannel(channelHandle);
+}
+
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
+        int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
+    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;
+    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;
+}
+
+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]);
+    }
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
new file mode 100644
index 0000000..662f320
--- /dev/null
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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[1];
+                if (fstat(fd1, &s1) < 0 || fstat(fd2, &s2) < 0 || s1.st_ino == s2.st_ino) {
+                    ret = true;
+                }
+            }
+            case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+                LOG_FATAL("%s: Implement GRALLOC or remove", __FUNCTION__);
+                ret = true;
+            default:
+                ALOGE("Unexpected mem type %d", mMem.type);
+                ret = true;
+        }
+    }
+    return ret;
+}
+
+} // namespace android
+
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
new file mode 100644
index 0000000..692ef0d
--- /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 <gui/Sensor.h>
+#include <gui/BitTube.h>
+#include <gui/ISensorServer.h>
+#include <gui/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..d84d36e 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -23,6 +23,8 @@
 #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..cd81ddd 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.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/SensorList.cpp b/services/sensorservice/SensorList.cpp
index e0101c1..31c8251 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,19 @@
             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.append("\n");
+            }
             return true;
         });
     return std::string(result.string());
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 7b47709..143a3c5 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -21,6 +21,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
 
+#include <cutils/ashmem.h>
 #include <gui/SensorEventQueue.h>
 
 #include <hardware/sensors.h>
@@ -40,6 +41,7 @@
 #include "SensorInterface.h"
 
 #include "SensorService.h"
+#include "SensorDirectConnection.h"
 #include "SensorEventAckReceiver.h"
 #include "SensorEventConnection.h"
 #include "SensorRecord.h"
@@ -337,7 +339,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 +363,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 +448,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 +458,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) %
@@ -868,7 +895,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 +903,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 +963,85 @@
     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)) {
+            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 mismatch, %" PRIu32 " vs %d", size, size2);
+                return nullptr;
+            }
+            break;
+        }
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            LOG_FATAL("%s: Finish implementation of ION and GRALLOC or remove", __FUNCTION__);
+            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;
+}
+
 status_t SensorService::resetToNormalMode() {
     Mutex::Autolock _l(mLock);
     return resetToNormalModeLocked();
@@ -1002,11 +1101,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 +1126,7 @@
     }
 
     Mutex::Autolock _l(mLock);
-    if ((mCurrentOperatingMode == RESTRICTED || mCurrentOperatingMode == DATA_INJECTION)
+    if (mCurrentOperatingMode != NORMAL
            && !isWhiteListedPackage(connection->getPackageName())) {
         return INVALID_OPERATION;
     }
@@ -1338,5 +1444,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..eeedd4a 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.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,
@@ -154,6 +156,8 @@
             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 status_t dump(int fd, const Vector<String16>& args);
 
     String8 getSensorName(int handle) const;
@@ -203,6 +207,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 +244,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/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 170faa8..6f5947a 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -10,15 +10,18 @@
     DispSync.cpp \
     EventControlThread.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 \
@@ -34,23 +37,31 @@
     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
 
+ifeq ($(TARGET_IN_VR_MODE),true)
+    LOCAL_CFLAGS += -DIN_VR_MODE
+endif
+
 ifeq ($(TARGET_USES_HWC2),true)
     LOCAL_CFLAGS += -DUSE_HWC2
     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
@@ -63,10 +74,6 @@
     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
@@ -99,6 +106,7 @@
 # [1] https://developer.android.com/studio/profile/systrace.html
 # [2] https://developer.android.com/training/testing/performance.html
 
+# These are left just for non-treble devices
 ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),)
     LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS)
 else
@@ -125,12 +133,20 @@
 
 LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
 
-LOCAL_STATIC_LIBRARIES := libvkjson
+LOCAL_STATIC_LIBRARIES := libhwcomposer-command-buffer libtrace_proto libvkjson
 LOCAL_SHARED_LIBRARIES := \
+    android.dvr.composer@1.0 \
+    android.hardware.graphics.allocator@2.0 \
+    android.hardware.graphics.composer@2.1 \
+    android.hardware.configstore@1.0 \
     libcutils \
     liblog \
     libdl \
+    libfmq \
     libhardware \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
     libutils \
     libEGL \
     libGLESv1_CM \
@@ -139,9 +155,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
 
@@ -179,6 +204,7 @@
     libdl
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_STATIC_LIBRARIES := libtrace_proto
 
 LOCAL_MODULE := surfaceflinger
 
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index e14a59b..2b4f4cb 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)
 {
 }
 
@@ -47,6 +53,10 @@
     }
 }
 
+void Client::setParentLayer(const sp<Layer>& parentLayer) {
+    mParentLayer = parentLayer;
+}
+
 status_t Client::initCheck() const {
     return NO_ERROR;
 }
@@ -90,12 +100,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 +121,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 +155,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();
 }
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 9c7d050..141f6c7 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);
 
@@ -75,6 +79,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..86cf17d 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -63,7 +63,7 @@
 class DispSyncThread: public Thread {
 public:
 
-    DispSyncThread(const char* name):
+    explicit DispSyncThread(const char* name):
             mName(name),
             mStop(false),
             mPeriod(0),
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5c2c0ad..6c18ef7 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -613,3 +613,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..92ede08 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -263,6 +263,28 @@
 #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..1bd9616
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -0,0 +1,987 @@
+/*
+ * 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 <android/dvr/composer/1.0/IVrComposerClient.h>
+#include <inttypes.h>
+#include <log/log.h>
+
+#include "ComposerHal.h"
+
+namespace android {
+
+using dvr::composer::V1_0::IVrComposerClient;
+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();
+}
+
+Composer::Composer() : mWriter(kWriterInitialSize)
+{
+#if defined(IN_VR_MODE)
+    mIsInVrMode = true;
+#endif
+
+    if (mIsInVrMode) {
+        mComposer = IComposer::getService("vr_hwcomposer");
+    } else {
+        mComposer = IComposer::getService("hwcomposer");
+    }
+
+    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)
+{
+    const uint32_t bufferSlotCount = 1;
+    Error error = kDefaultError;
+    mClient->createLayer(display, bufferSlotCount,
+            [&](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, const native_handle_t* target,
+        int acquireFence, Dataspace dataspace,
+        const std::vector<IComposerClient::Rect>& damage)
+{
+    mWriter.selectDisplay(display);
+    mWriter.setClientTarget(0, target, 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 = 1;
+    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,
+        const native_handle_t* buffer, int acquireFence)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerBuffer(0, buffer, 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 (mIsInVrMode)
+    {
+        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..329d787
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -0,0 +1,250 @@
+/*
+ * 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/hardware/graphics/composer/2.1/IComposer.h>
+#include <utils/StrongPointer.h>
+#include <IComposerCommandBuffer.h>
+#include <MessageQueue.h>
+
+namespace android {
+
+namespace Hwc2 {
+
+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();
+
+    std::vector<IComposer::Capability> getCapabilities();
+    std::string dumpDebugInfo();
+
+    void registerCallback(const sp<IComposerCallback>& callback);
+
+    uint32_t getMaxVirtualDisplayCount();
+    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);
+    Error setClientTarget(Display display, const native_handle_t* 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);
+    Error setLayerBuffer(Display display, Layer layer,
+            const native_handle_t* 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);
+    };
+
+    // 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;
+
+    bool mIsInVrMode = false;
+};
+
+} // namespace Hwc2
+
+} // namespace android
+
+#endif // ANDROID_SF_COMPOSER_HAL_H
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..1998edf 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -204,7 +204,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/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 4fe3cfd..6ff5ea1 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -21,8 +21,9 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "HWC2.h"
+#include "ComposerHal.h"
 
-#include "FloatRect.h"
+#include <gfx/FloatRect.h>
 
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
@@ -73,17 +74,22 @@
 }
 
 using android::Fence;
-using android::FloatRect;
 using android::GraphicBuffer;
 using android::HdrCapabilities;
 using android::Rect;
 using android::Region;
 using android::sp;
+using android::gfx::FloatRect;
+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 +134,10 @@
     mSetLayerTransform(nullptr),
     mSetLayerVisibleRegion(nullptr),
     mSetLayerZOrder(nullptr),
+#else
+Device::Device()
+  : mComposer(std::make_unique<Hwc2::Composer>()),
+#endif // BYPASS_IHWC
     mCapabilities(),
     mDisplays(),
     mHotplug(),
@@ -144,9 +154,11 @@
 
 Device::~Device()
 {
+#ifdef BYPASS_IHWC
     if (mHwcDevice == nullptr) {
         return;
     }
+#endif
 
     for (auto element : mDisplays) {
         auto display = element.second.lock();
@@ -175,13 +187,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 +204,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 +224,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;
@@ -258,6 +286,9 @@
 {
     if (connected == Connection::Connected) {
         if (!display->isConnected()) {
+#ifndef BYPASS_IHWC
+            mComposer->setClientTargetSlotCount(display->getId());
+#endif
             display->loadConfigs();
             display->setConnected(true);
         }
@@ -315,6 +346,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 +355,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 +371,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 +465,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 +515,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);
@@ -498,14 +576,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 +608,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 +638,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 +651,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 +684,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 +697,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 +725,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 +744,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 +770,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,9 +803,15 @@
 
 Error Display::getType(DisplayType* outType) const
 {
+#ifdef BYPASS_IHWC
     int32_t intType = 0;
     int32_t intError = mDevice.mGetDisplayType(mDevice.mHwcDevice, mId,
             &intType);
+#else
+    Hwc2::IComposerClient::DisplayType intType =
+        Hwc2::IComposerClient::DisplayType::INVALID;
+    auto intError = mDevice.mComposer->getDisplayType(mId, &intType);
+#endif
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -694,9 +823,14 @@
 
 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 +846,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 +859,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 +883,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 +897,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 +927,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,8 +953,12 @@
                 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);
 }
 
@@ -803,22 +967,38 @@
 {
     // TODO: Properly encode client target surface damage
     int32_t fenceFd = acquireFence->dup();
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, target,
             fenceFd, static_cast<int32_t>(dataspace), {0, nullptr});
+#else
+    auto intError = mDevice.mComposer->setClientTarget(mId, 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 +1007,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 +1046,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 +1068,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 +1104,7 @@
 {
     ALOGV("[%" PRIu64 "] loadConfigs", mId);
 
+#ifdef BYPASS_IHWC
     uint32_t numConfigs = 0;
     int32_t intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId,
             &numConfigs, nullptr);
@@ -913,6 +1119,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 +1139,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,8 +1185,13 @@
 
 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);
 }
 
@@ -979,8 +1199,13 @@
         const sp<Fence>& acquireFence)
 {
     int32_t fenceFd = acquireFence->dup();
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId,
             mId, buffer, fenceFd);
+#else
+    auto intError = mDevice.mComposer->setLayerBuffer(mDisplayId,
+            mId, buffer, fenceFd);
+#endif
     return static_cast<Error>(intError);
 }
 
@@ -988,26 +1213,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 +1258,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 +1345,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 +1389,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..256b05d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,10 +38,15 @@
 
 namespace android {
     class Fence;
-    class FloatRect;
     class GraphicBuffer;
     class Rect;
     class Region;
+    namespace gfx {
+        class FloatRect;
+    }
+    namespace Hwc2 {
+        class Composer;
+    }
 }
 
 namespace HWC2 {
@@ -54,10 +59,16 @@
 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
+    Device();
+#endif
     ~Device();
 
     friend class HWC2::Display;
@@ -98,6 +109,7 @@
 private:
     // Initialization methods
 
+#ifdef BYPASS_IHWC
     template <typename PFN>
     [[clang::warn_unused_result]] bool loadFunctionPointer(
             FunctionDescriptor desc, PFN& outPFN) {
@@ -121,6 +133,7 @@
         auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
         mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
     }
+#endif
 
     void loadCapabilities();
     void loadFunctionPointers();
@@ -132,6 +145,7 @@
 
     // Member variables
 
+#ifdef BYPASS_IHWC
     hwc2_device_t* mHwcDevice;
 
     // Device function pointers
@@ -181,6 +195,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 +211,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,7 +318,7 @@
             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(
@@ -355,6 +373,7 @@
     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:
@@ -381,11 +400,12 @@
     [[clang::warn_unused_result]] Error setSidebandStream(
             const native_handle_t* stream);
     [[clang::warn_unused_result]] Error setSourceCrop(
-            const android::FloatRect& crop);
+            const android::gfx::FloatRect& crop);
     [[clang::warn_unused_result]] Error setTransform(Transform transform);
     [[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
index 617ea9f..37de7a2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -87,6 +87,7 @@
         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(const_cast<hwc_rect_t*>(layer.surfaceDamage.rects));
         }
     }
     std::free(contents);
@@ -94,7 +95,7 @@
 
 class HWC2On1Adapter::Callbacks : public hwc_procs_t {
     public:
-        Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
+        explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
             invalidate = &invalidateHook;
             vsync = &vsyncHook;
             hotplug = &hotplugHook;
@@ -133,6 +134,7 @@
     mHwc1Device(hwc1Device),
     mHwc1MinorVersion(getMinorVersion(hwc1Device)),
     mHwc1SupportsVirtualDisplays(false),
+    mHwc1SupportsBackgroundColor(false),
     mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
     mCapabilities(),
     mLayers(),
@@ -561,6 +563,7 @@
     mHwc1Id(-1),
     mConfigs(),
     mActiveConfig(nullptr),
+    mActiveColorMode(static_cast<android_color_mode_t>(-1)),
     mName(),
     mType(type),
     mPowerMode(PowerMode::Off),
@@ -604,6 +607,7 @@
     mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer));
     *outLayerId = layer->getId();
     ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId);
+    mZIsDirty = true;
     return Error::None;
 }
 
@@ -627,6 +631,7 @@
         }
     }
     ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId);
+    mZIsDirty = true;
     return Error::None;
 }
 
@@ -807,7 +812,10 @@
         return Error::None;
     }
 
-    *outDisplayRequests = mChanges->getDisplayRequests();
+    // 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) {
@@ -1289,6 +1297,7 @@
     for (auto& layer : mLayers) {
         auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
         hwc1Layer.releaseFenceFd = -1;
+        hwc1Layer.acquireFenceFd = -1;
         layer->applyState(hwc1Layer, applyAllState);
     }
 
@@ -1841,29 +1850,39 @@
 
     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;
+
+    // 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;
             }
-        }
-        if (!mActiveConfig) {
-            ALOGV("Unable to find active HWC1 config %u, defaulting to "
-                    "config 0", activeConfig);
-            mActiveConfig = mConfigs[0];
-            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()
@@ -2262,7 +2281,18 @@
         bool applyAllState)
 {
     if (applyAllState || mColor.isDirty()) {
-        hwc1Layer.backgroundColor = mColor.getPendingValue();
+        // 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 (mDisplay.getDevice().supportsBackgroundColor()) {
+            hwc1Layer.backgroundColor = mColor.getPendingValue();
+            mHasUnsupportedBackgroundColor = false;
+        } else {
+            hwc1Layer.handle = nullptr;
+            mHasUnsupportedBackgroundColor = true;
+        }
         mColor.latch();
     }
 }
@@ -2289,7 +2319,7 @@
     // supports plane alpha (depending on the version). These require us to drop
     // some or all layers to client composition.
     if (mHasUnsupportedDataspace || mHasUnsupportedPlaneAlpha ||
-            mDisplay.hasColorTransform()) {
+            mDisplay.hasColorTransform() || mHasUnsupportedBackgroundColor) {
         hwc1Layer.compositionType = HWC_FRAMEBUFFER;
         hwc1Layer.flags = HWC_SKIP_LAYER;
         return;
@@ -2359,6 +2389,18 @@
     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;
+        }
+    }
 }
 
 HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id)
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index 962361e..9abdc38 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -40,6 +40,10 @@
 
 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:
@@ -63,6 +67,10 @@
         getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
     }
 
+    bool supportsBackgroundColor() {
+        return mHwc1SupportsBackgroundColor;
+    }
+
     // getFunction
 
     hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor);
@@ -131,11 +139,28 @@
             void operator()(struct hwc_display_contents_1* contents);
     };
 
+    // 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()
-              : mMutex(),
-                mFences({Fence::NO_FENCE, Fence::NO_FENCE}) {}
+              : mFences({Fence::NO_FENCE, Fence::NO_FENCE}) {}
 
             void add(int32_t fenceFd) {
                 mFences.emplace(new Fence(fenceFd));
@@ -147,7 +172,7 @@
             }
 
         private:
-            mutable std::mutex mMutex;
+            // There are always two fences in this queue.
             std::queue<sp<Fence>> mFences;
     };
 
@@ -232,7 +257,10 @@
 
             bool prepare();
             HWC1Contents cloneRequestedContents() const;
+
+            // Called after hwc.prepare() with responses from the device.
             void setReceivedContents(HWC1Contents contents);
+
             bool hasChanges() const;
             HWC2::Error set(hwc_display_contents_1& hwcContents);
             void addRetireFence(int fenceFd);
@@ -247,6 +275,7 @@
                 public:
                     Config(Display& display)
                       : mDisplay(display),
+                        mId(0),
                         mAttributes() {}
 
                     bool isOnDisplay(const Display& display) const {
@@ -285,6 +314,10 @@
                     std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids;
             };
 
+            // Store changes requested from the device upon calling prepare().
+            // Handles change request to:
+            //   - Layer composition type.
+            //   - Layer hints.
             class Changes {
                 public:
                     uint32_t getNumTypes() const {
@@ -305,14 +338,6 @@
                         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});
@@ -330,7 +355,6 @@
                             mTypeChanges;
                     std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
                             mLayerRequests;
-                    std::unordered_set<HWC2::DisplayRequest> mDisplayRequests;
             };
 
             std::shared_ptr<const Config>
@@ -342,8 +366,13 @@
             void reallocateHwc1Contents();
             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);
 
@@ -367,8 +396,11 @@
             mutable std::recursive_mutex mStateMutex;
 
             bool mZIsDirty;
-            HWC1Contents mHwc1RequestedContents;
-            HWC1Contents mHwc1ReceivedContents;
+
+            // Array of structs exchanged between client and hwc1 device.
+            HWC1Contents mHwc1RequestedContents; // Sent to device upon calling prepare().
+            HWC1Contents mHwc1ReceivedContents;  // Returned by device after prepare().
+
             DeferredFence mRetireFence;
 
             // Will only be non-null after the layer has been validated but
@@ -575,6 +607,7 @@
             size_t mHwc1Id;
             bool mHasUnsupportedDataspace;
             bool mHasUnsupportedPlaneAlpha;
+            bool mHasUnsupportedBackgroundColor;
     };
 
     template <typename ...Args>
@@ -655,6 +688,7 @@
     struct hwc_composer_device_1* const mHwc1Device;
     const uint8_t mHwc1MinorVersion;
     bool mHwc1SupportsVirtualDisplays;
+    bool mHwc1SupportsBackgroundColor;
 
     class Callbacks;
     const std::unique_ptr<Callbacks> mHwc1Callbacks;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index f0ded39..7914770 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -109,6 +109,7 @@
 {
     ALOGV("loadHwcModule");
 
+#ifdef BYPASS_IHWC
     hw_module_t const* module;
 
     if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
@@ -140,6 +141,9 @@
         mHwcDevice = std::make_unique<HWC2::Device>(
                 static_cast<hwc2_device_t*>(mAdapter.get()));
     }
+#else
+    mHwcDevice = std::make_unique<HWC2::Device>();
+#endif
 
     mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
 }
@@ -305,22 +309,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 +346,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 +408,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 +425,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,12 +433,12 @@
             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));
         }
     }
@@ -589,12 +594,16 @@
     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;
+}
+
+bool HWComposer::presentFenceRepresentsStartOfScanout() const {
+    return mAdapter ? false : true;
 }
 
 sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId,
@@ -611,7 +620,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 +629,18 @@
 
     auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
-    auto error = hwcDisplay->present(&displayData.lastRetireFence);
+    auto error = hwcDisplay->present(&displayData.lastPresentFence);
     if (error != HWC2::Error::None) {
-        ALOGE("commit: present failed for display %d: %s (%d)", displayId,
-                to_string(error).c_str(), static_cast<int32_t>(error));
+        ALOGE("presentAndGetReleaseFences: failed for display %d: %s (%d)",
+              displayId, to_string(error).c_str(), static_cast<int32_t>(error));
         return UNKNOWN_ERROR;
     }
 
     std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences;
     error = hwcDisplay->getReleaseFences(&releaseFences);
     if (error != HWC2::Error::None) {
-        ALOGE("commit: Failed to get release fences for display %d: %s (%d)",
+        ALOGE("presentAndGetReleaseFences: Failed to get release fences "
+              "for display %d: %s (%d)",
                 displayId, to_string(error).c_str(),
                 static_cast<int32_t>(error));
         return UNKNOWN_ERROR;
@@ -869,7 +879,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..2713505 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -97,8 +97,8 @@
     status_t setClientTarget(int32_t displayId, 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 +118,13 @@
     // 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;
+
+    // Returns true if the present fence represents the start of the display
+    // controller's scan out. This should be true for all HWC2 implementations,
+    // except for the wrapper around HWC1 implementations.
+    bool presentFenceRepresentsStartOfScanout() 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>>
@@ -186,7 +190,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;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index 52cbb34..cc5578d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -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; }
@@ -1031,7 +1031,7 @@
     virtual void setFrame(const Rect& frame) {
         getLayer()->displayFrame = reinterpret_cast<hwc_rect_t const&>(frame);
     }
-    virtual void setCrop(const FloatRect& crop) {
+    virtual void setCrop(const gfx::FloatRect& crop) {
         if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
             getLayer()->sourceCropf = reinterpret_cast<hwc_frect_t const&>(crop);
         } else {
@@ -1159,6 +1159,7 @@
     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_RGB_888:      return String8("RGB_888");
     case PIXEL_FORMAT_RGB_565:      return String8("RGB_565");
     case PIXEL_FORMAT_BGRA_8888:    return String8("BGRA_8888");
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
index 170e382..bca25ac 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
@@ -48,12 +48,14 @@
 // ---------------------------------------------------------------------------
 
 class Fence;
-class FloatRect;
 class GraphicBuffer;
 class NativeHandle;
 class Region;
 class String8;
 class SurfaceFlinger;
+namespace gfx {
+    class FloatRect;
+}
 
 class HWComposer
 {
@@ -168,7 +170,7 @@
         virtual void setBlending(uint32_t blending) = 0;
         virtual void setTransform(uint32_t transform) = 0;
         virtual void setFrame(const Rect& frame) = 0;
-        virtual void setCrop(const FloatRect& crop) = 0;
+        virtual void setCrop(const gfx::FloatRect& crop) = 0;
         virtual void setVisibleRegionScreen(const Region& reg) = 0;
         virtual void setSurfaceDamage(const Region& reg) = 0;
         virtual void setSidebandStream(const sp<NativeHandle>& stream) = 0;
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..6a98f03 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -241,7 +241,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 +281,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 +303,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 +340,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 +379,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 +516,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 +556,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 +617,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..d37dc0a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -104,7 +104,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 +136,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();
 
@@ -180,6 +181,8 @@
     // 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
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index dd88adb..d1bc7eb 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -44,13 +44,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 +227,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;
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index b635115..3f1d0bb 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -77,7 +77,7 @@
 
 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 +132,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 1b90678..e57c19a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -44,6 +44,7 @@
 #include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "Layer.h"
+#include "LayerRejecter.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 
@@ -79,7 +80,9 @@
         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
         mOverrideScalingMode(-1),
         mCurrentOpacity(true),
+        mBufferLatched(false),
         mCurrentFrameNumber(0),
+        mPreviousFrameNumber(-1U),
         mRefreshPending(false),
         mFrameLatencyNeeded(false),
         mFiltering(false),
@@ -136,6 +139,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;
@@ -155,17 +161,16 @@
     // 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, nullptr, 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 +217,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) {
@@ -276,6 +282,9 @@
 // it's removed from the drawing state list)
 void Layer::onRemoved() {
     mSurfaceFlingerConsumer->abandon();
+    for (const auto& child : mCurrentChildren) {
+        child->onRemoved();
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -312,22 +321,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 +367,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 +413,25 @@
     if (!s.crop.isEmpty()) {
         win.intersect(s.crop, &win);
     }
+
+    Rect bounds = win;
+    const auto& p = getParent();
+    if (p != nullptr) {
+        bounds = p->computeScreenBounds();
+    }
+
+    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 +448,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 +458,27 @@
             activeCrop.clear();
         }
     }
-    activeCrop = s.active.transform.inverse().transform(activeCrop);
+    return activeCrop;
+}
+
+gfx::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.
+    gfx::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
@@ -494,7 +552,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,
@@ -547,9 +605,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 +616,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 +635,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();
@@ -598,7 +658,7 @@
         hwcInfo.displayFrame = transformedFrame;
     }
 
-    FloatRect sourceCrop = computeCrop(displayDevice);
+    gfx::FloatRect sourceCrop = computeCrop(displayDevice);
     error = hwcLayer->setSourceCrop(sourceCrop);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
@@ -614,10 +674,14 @@
             "%s (%d)", mName.string(), s.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();
@@ -637,7 +701,7 @@
      */
 
     const Transform bufferOrientation(mCurrentTransform);
-    Transform transform(tr * s.active.transform * bufferOrientation);
+    Transform transform(tr * t * bufferOrientation);
 
     if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
         /*
@@ -764,6 +828,14 @@
         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));
+    }
+
     auto acquireFence = mSurfaceFlingerConsumer->getCurrentFence();
     error = hwcLayer->setBuffer(mActiveBuffer->handle, acquireFence);
     if (error != HWC2::Error::None) {
@@ -815,7 +887,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 +936,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);
@@ -907,19 +979,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;
     }
@@ -985,13 +1056,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 +1072,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 +1098,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();
         }
@@ -1202,6 +1274,7 @@
     switch (format) {
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_BGRA_8888:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
             return false;
     }
     // in all other case, we have no blending (also for unknown formats)
@@ -1231,25 +1304,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 +1329,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;
     }
@@ -1545,11 +1614,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,7 +1653,19 @@
     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++;
@@ -1597,6 +1674,7 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
+
 bool Layer::setSize(uint32_t w, uint32_t h) {
     if (mCurrentState.requested.w == w && mCurrentState.requested.h == h)
         return false;
@@ -1675,6 +1753,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,6 +1777,24 @@
     return true;
 }
 
+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<IBinder>& handle,
         uint64_t frameNumber) {
     mCurrentState.handle = handle;
@@ -1744,65 +1847,129 @@
     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 std::shared_ptr<FenceTime>& retireFence) {
+    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);
+        mFrameEventHistory.addRetire(mPreviousFrameNumber,
+                retireFence);
     }
-    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 if (retireFence->isValid()) {
+        mFrameTracker.setActualPresentFence(
+                std::shared_ptr<FenceTime>(retireFence));
+    } 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() {
+void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     mSurfaceFlingerConsumer->releasePendingBuffer();
+    auto releaseFenceTime = std::make_shared<FenceTime>(
+            mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+    mReleaseTimeline.push(releaseFenceTime);
+
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    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()) && s.alpha > 0.0f
             && (mActiveBuffer != NULL || mSidebandStream != NULL);
 #else
-    return !(s.flags & layer_state_t::eLayerHidden) && s.alpha
+    return !(isHiddenByPolicy()) && s.alpha
             && (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 +1983,197 @@
         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();
+    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);
+        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 +2235,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,
@@ -2279,7 +2307,7 @@
     const Rect& frame = hwcInfo.displayFrame;
     result.appendFormat("%4d %4d %4d %4d | ", frame.left, frame.top,
             frame.right, frame.bottom);
-    const FloatRect& crop = hwcInfo.sourceCrop;
+    const gfx::FloatRect& crop = hwcInfo.sourceCrop;
     result.appendFormat("%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top,
             crop.right, crop.bottom);
 
@@ -2304,22 +2332,25 @@
     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::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 +2370,118 @@
     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;
+}
+
+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;
+}
+
+/**
+ * Negatively signed children are before 'this' in Z-order.
+ */
+void Layer::traverseInZOrder(const std::function<void(Layer*)>& exec) {
+    size_t i = 0;
+    for (; i < mDrawingChildren.size(); i++) {
+        const auto& child = mDrawingChildren[i];
+        if (child->getZ() >= 0)
+            break;
+        child->traverseInZOrder(exec);
+    }
+    exec(this);
+    for (; i < mDrawingChildren.size(); i++) {
+        const auto& child = mDrawingChildren[i];
+        child->traverseInZOrder(exec);
+    }
+}
+
+/**
+ * Positively signed children are before 'this' in reverse Z-order.
+ */
+void Layer::traverseInReverseZOrder(const std::function<void(Layer*)>& exec) {
+    int32_t i = 0;
+    for (i = mDrawingChildren.size()-1; i>=0; i--) {
+        const auto& child = mDrawingChildren[i];
+        if (child->getZ() < 0) {
+            break;
+        }
+        child->traverseInReverseZOrder(exec);
+    }
+    exec(this);
+    for (; i>=0; i--) {
+        const auto& child = mDrawingChildren[i];
+        child->traverseInReverseZOrder(exec);
+    }
+}
+
+Transform Layer::getTransform() const {
+    Transform t;
+    const auto& p = getParent();
+    if (p != nullptr) {
+        t = p->getTransform();
+    }
+    return t * getDrawingState().active.transform;
+}
+
+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..7335be7 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -27,6 +27,8 @@
 #include <utils/String8.h>
 #include <utils/Timers.h>
 
+#include <gfx/FloatRect.h>
+
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
@@ -40,13 +42,13 @@
 
 #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 "RenderEngine/Mesh.h"
 #include "RenderEngine/Texture.h"
 
@@ -108,7 +110,7 @@
     struct State {
         Geometry active;
         Geometry requested;
-        uint32_t z;
+        int32_t z;
         uint32_t layerStack;
 #ifdef USE_HWC2
         float alpha;
@@ -121,9 +123,11 @@
         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;
 
         // If set, defers this state update until the Layer identified by handle
@@ -136,6 +140,10 @@
         // dependent.
         Region activeTransparentRegion;
         Region requestedTransparentRegion;
+        android_dataspace dataSpace;
+
+        uint32_t appId;
+        uint32_t type;
     };
 
     // -----------------------------------------------------------------------
@@ -150,7 +158,7 @@
 
     // modify current state
     bool setPosition(float x, float y, bool immediate);
-    bool setLayer(uint32_t z);
+    bool setLayer(int32_t z);
     bool setSize(uint32_t w, uint32_t h);
 #ifdef USE_HWC2
     bool setAlpha(float alpha);
@@ -163,8 +171,12 @@
     bool setCrop(const Rect& crop, bool immediate);
     bool setFinalCrop(const Rect& crop);
     bool setLayerStack(uint32_t layerStack);
+    bool setDataSpace(android_dataspace dataSpace);
+    uint32_t getLayerStack() const;
     void deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
     bool setOverrideScalingMode(int32_t overrideScalingMode);
+    void setInfo(uint32_t type, uint32_t appId);
+    bool reparentChildren(const sp<IBinder>& layer);
 
     // 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 +192,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 +226,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,7 +249,7 @@
     // -----------------------------------------------------------------------
 
 #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);
 
@@ -275,17 +290,20 @@
      * 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 std::shared_ptr<FenceTime>& retireFence);
 
 #ifdef USE_HWC2
     // If a buffer was replaced this frame, release the former buffer
-    void releasePendingBuffer();
+    void releasePendingBuffer(nsecs_t dequeueReadyTime);
 #endif
 
     /*
@@ -328,7 +346,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;}
 
@@ -388,7 +406,7 @@
 #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 +425,41 @@
     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 addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
+            FrameEventHistoryDelta* outDelta);
 
     bool getTransformToDisplayInverse() const;
 
+    Transform getTransform() const;
+
+    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 +468,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 +497,19 @@
     bool needsFiltering(const sp<const DisplayDevice>& hw) const;
 
     uint32_t getEffectiveUsage(uint32_t usage) const;
-    FloatRect computeCrop(const sp<const DisplayDevice>& hw) const;
+
+    gfx::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 +518,8 @@
     // Loads the corresponding system property once per process
     static bool latchUnsignaledBuffers();
 
+    void setParent(const sp<Layer>& layer);
+
     // -----------------------------------------------------------------------
 
     class SyncPoint
@@ -526,16 +574,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,8 +630,17 @@
     // 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
     sp<GraphicBuffer> mActiveBuffer;
     sp<NativeHandle> mSidebandStream;
@@ -567,7 +650,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
@@ -593,7 +678,7 @@
         HWC2::Composition compositionType;
         bool clearClientTarget;
         Rect displayFrame;
-        FloatRect sourceCrop;
+        gfx::FloatRect sourceCrop;
     };
     std::unordered_map<int32_t, HWCInfo> mHwcLayers;
 #else
@@ -617,10 +702,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..2bc0605
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 "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;
+    }
+    mFreezePositionUpdates = false;
+
+    return false;
+}
+
+}  // namespace android
\ No newline at end of file
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..7ba6ad3
--- /dev/null
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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++) {
+        (*this)[i]->traverseInZOrder(consume);
+    }
+}
+
+void LayerVector::traverseInReverseZOrder(const std::function<void(Layer*)>& consume) const {
+    for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+        (*this)[i]->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/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/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 2aec9e9..9909bf9 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -19,8 +19,6 @@
 #include <ui/Region.h>
 
 #include "RenderEngine.h"
-#include "GLES10RenderEngine.h"
-#include "GLES11RenderEngine.h"
 #include "GLES20RenderEngine.h"
 #include "GLExtensions.h"
 #include "Mesh.h"
@@ -122,10 +120,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 +313,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 +334,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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 76d518b..c9445e7 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>
@@ -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,8 @@
 #include "RenderEngine/RenderEngine.h"
 #include <cutils/compiler.h>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+
 #define DISPLAY_COUNT       1
 
 /*
@@ -92,6 +97,22 @@
 
 namespace android {
 
+using namespace android::hardware::configstore::V1_0;
+
+static sp<ISurfaceFlingerConfigs> getConfigs() {
+    static sp<ISurfaceFlingerConfigs> configs
+            = ISurfaceFlingerConfigs::getService();
+    return configs;
+}
+
+static int64_t getVsyncEventPhaseOffsetNs() {
+    int64_t ret = 1000000; // default value
+    getConfigs()->vsyncEventPhaseOffsetNs([&](OptionalInt64 value) {
+          if (value.specified) ret = value.value;
+    });
+    return ret;
+}
+
 // 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
@@ -112,7 +133,7 @@
 // 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;
+static int64_t vsyncPhaseOffsetNs = getVsyncEventPhaseOffsetNs();
 
 // This is the phase offset at which SurfaceFlinger's composition runs.
 static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
@@ -132,6 +153,7 @@
         mTransactionPending(false),
         mAnimTransactionPending(false),
         mLayersRemoved(false),
+        mLayersAdded(false),
         mRepaintEverything(0),
         mRenderEngine(NULL),
         mBootTime(systemTime()),
@@ -149,6 +171,7 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
+        mInterceptor(),
         mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
@@ -156,7 +179,8 @@
         mHasPoweredOff(false),
         mFrameBuckets(),
         mTotalTime(0),
-        mLastSwapTime(0)
+        mLastSwapTime(0),
+        mNumLayers(0)
 {
     ALOGI("SurfaceFlinger is starting");
 
@@ -187,6 +211,10 @@
     property_get("debug.sf.disable_hwc_vds", value, "0");
     mUseHwcVirtualDisplays = !atoi(value);
     ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+
+    property_get("ro.sf.disable_triple_buffer", value, "0");
+    mLayerTripleBufferingDisabled = !atoi(value);
+    ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -212,15 +240,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 +277,7 @@
              flinger->setTransactionFlags(eDisplayTransactionNeeded);
          }
      public:
-        DisplayToken(const sp<SurfaceFlinger>& flinger)
+        explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
             : flinger(flinger) {
         }
     };
@@ -246,7 +288,7 @@
     DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
     info.displayName = displayName;
     mCurrentState.displays.add(token, info);
-
+    mInterceptor.saveDisplayCreation(info);
     return token;
 }
 
@@ -264,7 +306,7 @@
         ALOGE("destroyDisplay called for non-virtual display");
         return;
     }
-
+    mInterceptor.saveDisplayDeletion(info.displayId);
     mCurrentState.displays.removeItemsAt(idx);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -277,6 +319,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) {
@@ -449,10 +492,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 +532,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
@@ -539,6 +608,23 @@
     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::GL_COMPOSITION_DONE,
+        getHwComposer().presentFenceRepresentsStartOfScanout() ?
+                FrameEvent::DISPLAY_PRESENT : FrameEvent::DISPLAY_RETIRE,
+        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)) {
@@ -616,7 +702,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
@@ -850,6 +936,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() {
@@ -1042,7 +1162,7 @@
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
-    uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+    uint32_t transactionFlags = peekTransactionFlags();
     if (transactionFlags) {
         handleTransaction(transactionFlags);
         return true;
@@ -1060,14 +1180,14 @@
 
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    preComposition();
+    preComposition(refreshStartTime);
     rebuildLayerStacks();
     setUpHWComposer();
     doDebugFlashRegions();
     doComposition();
-    postComposition(refreshStartTime);
+    postComposition();
 
-    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 +1196,6 @@
                 mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
     }
 
-    // Release any buffers which were replaced this frame
-    for (auto& layer : mLayersWithQueuedFrames) {
-        layer->releasePendingBuffer();
-    }
     mLayersWithQueuedFrames.clear();
 }
 
@@ -1127,64 +1243,87 @@
     }
 }
 
-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::postComposition(nsecs_t refreshStartTime)
+void SurfaceFlinger::postComposition()
 {
     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);
+    const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
 
-    if (presentFence->isValid()) {
-        if (mPrimaryDispSync.addPresentFence(presentFence)) {
+    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> displayFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
+    auto displayFenceTime = std::make_shared<FenceTime>(displayFence);
+    mDisplayTimeline.push(displayFenceTime);
+    mDisplayTimeline.updateSignalTimes();
+
+    const std::shared_ptr<FenceTime>* presentFenceTime = &FenceTime::NO_FENCE;
+    const std::shared_ptr<FenceTime>* retireFenceTime = &FenceTime::NO_FENCE;
+    if (mHwc->presentFenceRepresentsStartOfScanout()) {
+        presentFenceTime = &displayFenceTime;
+    } else {
+        retireFenceTime = &displayFenceTime;
+    }
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime,
+                *presentFenceTime, *retireFenceTime);
+        if (frameLatched) {
+            recordBufferingStats(layer->getName().string(),
+                    layer->getOccupancyHistory(false));
+        }
+    });
+
+    if (displayFence->isValid()) {
+        if (mPrimaryDispSync.addPresentFence(displayFence)) {
             enableHardwareVsync();
         } else {
             disableHardwareVsync(false);
         }
     }
 
-    const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
     if (kIgnorePresentFences) {
         if (hw->isDisplayOn()) {
             enableHardwareVsync();
         }
     }
 
-    mFenceTracker.addFrame(refreshStartTime, presentFence,
-            hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
-        if (presentFence->isValid()) {
-            mAnimFrameTracker.setActualPresentFence(presentFence);
+        if (displayFenceTime->isValid()) {
+            mAnimFrameTracker.setActualPresentFence(
+                    std::move(displayFenceTime));
         } else {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
@@ -1226,7 +1365,6 @@
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        const LayerVector& layers(mDrawingState.layersSortedByZ);
         for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
             Region opaqueRegion;
             Region dirtyRegion;
@@ -1235,15 +1373,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);
@@ -1256,7 +1391,7 @@
                                     nullptr);
                         }
                     }
-                }
+                });
             }
             displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
             displayDevice->undefinedRegion.set(bounds);
@@ -1310,7 +1445,8 @@
                 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) {
@@ -1322,7 +1458,7 @@
                         }
                     }
 
-                    layer->setGeometry(displayDevice);
+                    layer->setGeometry(displayDevice, i);
                     if (mDebugDisableHWC || mDebugRegion) {
                         layer->forceClientComposition(hwcId);
                     }
@@ -1402,16 +1538,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) {
@@ -1467,13 +1597,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 +1608,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;
-        }
+        });
     }
 
     /*
@@ -1682,13 +1808,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,
@@ -1716,7 +1842,9 @@
                 disp = getDefaultDisplayDevice();
             }
             layer->updateTransformHint(disp);
-        }
+
+            first = false;
+        });
     }
 
 
@@ -1724,9 +1852,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 +1863,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 +1899,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 +1912,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 +1932,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 +1973,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 +1990,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 +2047,7 @@
         layer->setCoveredRegion(coveredRegion);
         layer->setVisibleNonTransparentRegion(
                 visibleRegion.subtract(transparentRegion));
-    }
+    });
 
     outOpaqueRegion = aboveOpaqueLayers;
 }
@@ -1942,10 +2066,9 @@
 {
     ALOGV("handlePageFlip");
 
-    Region dirtyRegion;
+    nsecs_t latchTime = systemTime();
 
     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
@@ -1957,24 +2080,23 @@
     // 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);
     }
 
     mVisibleRegionsDirty |= visibleRegions;
@@ -1996,14 +2118,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 +2137,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(
@@ -2150,7 +2273,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;
                     }
@@ -2186,8 +2309,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);
 }
@@ -2195,16 +2318,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
@@ -2221,17 +2351,25 @@
         return NO_ERROR;
     }
 
-    ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
-    if (index >= 0) {
-        mLayersPendingRemoval.push(layer);
-        mLayersRemoved = true;
-        setTransactionFlags(eTransactionNeeded);
-        return NO_ERROR;
+    const auto& p = layer->getParent();
+    const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
+        mCurrentState.layersSortedByZ.remove(layer);
+
+    if (index < 0) {
+        ALOGE("Failed to find layer (%s) in layer parent (%s).",
+                layer->getName().string(),
+                (p != nullptr) ? p->getName().string() : "no-parent");
+        return BAD_VALUE;
     }
-    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);
 }
 
@@ -2307,6 +2445,10 @@
     }
 
     if (transactionFlags) {
+        if (mInterceptor.isEnabled()) {
+            mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+        }
+
         // this triggers the transaction
         setTransactionFlags(transactionFlags);
 
@@ -2398,13 +2540,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) {
@@ -2437,9 +2586,17 @@
                 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)
@@ -2452,6 +2609,11 @@
             // 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::eOverrideScalingModeChanged) {
             layer->setOverrideScalingMode(s.overrideScalingMode);
             // We don't trigger a traversal here because if no other state is
@@ -2465,9 +2627,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));
@@ -2498,10 +2660,13 @@
         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;
@@ -2549,6 +2714,7 @@
     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));
@@ -2592,7 +2758,7 @@
     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;
@@ -2620,6 +2786,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);
@@ -2752,9 +2928,9 @@
             }
 
             if ((index < numArgs) &&
-                    (args[index] == String16("--fences"))) {
+                    (args[index] == String16("--frame-events"))) {
                 index++;
-                mFenceTracker.dump(&result);
+                dumpFrameEventsLocked(result);
                 dumpAll = false;
             }
         }
@@ -2774,12 +2950,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,
@@ -2798,14 +2971,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);
             }
-        }
+        });
     }
 }
 
@@ -2818,14 +2988,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();
 }
@@ -2833,31 +3000,25 @@
 // 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"
+    result.append(" [sf");
 #ifdef HAS_CONTEXT_PRIORITY
-            " HAS_CONTEXT_PRIORITY"
+    result.append(" HAS_CONTEXT_PRIORITY");
 #endif
 #ifdef NEVER_DEFAULT_TO_ASYNC_MODE
-            " NEVER_DEFAULT_TO_ASYNC_MODE"
+    result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
 #endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
-            " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
-            "]";
-    result.append(config);
+    if (isLayerTripleBufferingDisabled())
+        result.append(" DISABLE_TRIPLE_BUFFERING");
+    result.append("]");
 }
 
 void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
@@ -2895,6 +3056,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> "
@@ -2988,15 +3159,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
@@ -3074,10 +3242,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");
     }
 
@@ -3135,13 +3302,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:
@@ -3154,12 +3318,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
@@ -3168,13 +3342,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) {
@@ -3305,6 +3488,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;
@@ -3406,7 +3601,7 @@
     }
 
 public:
-    GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+    explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
     :   impl(impl),
         looper(new Looper(true)),
         result(NO_ERROR),
@@ -3440,7 +3635,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))
@@ -3491,7 +3686,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)
@@ -3540,7 +3735,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();
@@ -3584,20 +3779,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();
@@ -3608,7 +3807,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)
 {
@@ -3632,16 +3831,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) {
@@ -3769,7 +3968,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;
@@ -3780,81 +3979,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..75c1920 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -37,6 +37,7 @@
 
 #include <binder/IMemory.h>
 
+#include <ui/FenceTime.h>
 #include <ui/PixelFormat.h>
 #include <ui/mat4.h>
 
@@ -53,9 +54,10 @@
 #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 "DisplayHardware/HWComposer.h"
 #include "Effects/Daltonizer.h"
@@ -76,6 +78,8 @@
 class Surface;
 class RenderEngine;
 class EventControlThread;
+class VSyncSource;
+class InjectVSyncSource;
 
 // ---------------------------------------------------------------------------
 
@@ -148,6 +152,7 @@
 private:
     friend class Client;
     friend class DisplayEventConnection;
+    friend class EventThread;
     friend class Layer;
     friend class MonitoredProducer;
 
@@ -164,33 +169,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,6 +189,7 @@
      * ISurfaceComposer interface
      */
     virtual sp<ISurfaceComposerClient> createConnection();
+    virtual sp<ISurfaceComposerClient> createScopedConnection(const sp<IGraphicBufferProducer>& gbp);
     virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc();
     virtual sp<IBinder> createDisplay(const String8& displayName, bool secure);
     virtual void destroyDisplay(const sp<IBinder>& display);
@@ -213,11 +199,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 +222,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
@@ -292,7 +283,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 +294,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,
@@ -330,7 +322,8 @@
     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,14 +334,14 @@
     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);
 
@@ -405,24 +398,23 @@
      * Compositing
      */
     void invalidateHwcGeometry();
-    static void computeVisibleRegions(
-            const LayerVector& currentLayers, uint32_t layerStack,
+    void computeVisibleRegions(uint32_t layerStack,
             Region& dirtyRegion, Region& opaqueRegion);
 
-    void preComposition();
-    void postComposition(nsecs_t refreshStartTime);
+    void preComposition(nsecs_t refreshStartTime);
+    void postComposition();
     void rebuildLayerStacks();
     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
@@ -434,6 +426,7 @@
      void enableHardwareVsync();
      void resyncToHardwareVsync(bool makeAvailable);
      void disableHardwareVsync(bool makeUnavailable);
+
 public:
      void resyncWithRateLimit();
 private:
@@ -446,22 +439,24 @@
     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;
 
-    bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
-            FrameTimestamps* outTimestamps);
-
+    bool isLayerTripleBufferingDisabled() const {
+        return this->mLayerTripleBufferingDisabled;
+    }
     /* ------------------------------------------------------------------------
      * Attributes
      */
@@ -473,11 +468,12 @@
     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;
@@ -489,6 +485,8 @@
     bool mGpuToCpuSupported;
     sp<EventThread> mEventThread;
     sp<EventThread> mSFEventThread;
+    sp<EventThread> mInjectorEventThread;
+    sp<InjectVSyncSource> mVSyncInjector;
     sp<EventControlThread> mEventControlThread;
     EGLContext mEGLContext;
     EGLDisplay mEGLDisplay;
@@ -509,6 +507,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,12 +525,15 @@
     nsecs_t mLastTransactionTime;
     bool mBootFinished;
     bool mForceFullDamage;
-    FenceTracker mFenceTracker;
 #ifdef USE_HWC2
     bool mPropagateBackpressure = true;
 #endif
+    SurfaceInterceptor mInterceptor;
     bool mUseHwcVirtualDisplays = true;
 
+    // Restrict layers to use two buffers in their bufferqueues.
+    bool mLayerTripleBufferingDisabled = false;
+
     // these are thread safe
     mutable MessageQueue mEventQueue;
     FrameTracker mAnimFrameTracker;
@@ -549,6 +552,8 @@
      * Feature prototyping
      */
 
+    bool mInjectVSyncs;
+
     Daltonizer mDaltonizer;
 #ifndef USE_HWC2
     bool mDaltonize;
@@ -565,6 +570,8 @@
     nsecs_t mTotalTime;
     std::atomic<nsecs_t> mLastSwapTime;
 
+    size_t mNumLayers;
+
     // Double- vs. triple-buffering stats
     struct BufferingStats {
         BufferingStats()
@@ -587,7 +594,11 @@
     };
     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);
+    };
 
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 6f2520b..029937a 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -189,10 +189,14 @@
     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;
@@ -222,17 +226,8 @@
             strerror(-result), result);
     mPendingRelease = PendingRelease();
 }
-#else
-void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence) {
-    mPrevReleaseFence = fence;
-    GLConsumer::setReleaseFence(fence);
-}
 #endif
 
-sp<Fence> SurfaceFlingerConsumer::getPrevReleaseFence() const {
-    return mPrevReleaseFence;
-}
-
 void SurfaceFlingerConsumer::setContentsChangedListener(
         const wp<ContentsChangedListener>& listener) {
     setFrameAvailableListener(listener);
@@ -253,10 +248,13 @@
     }
 }
 
-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::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..d3f0070 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -37,10 +37,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 +60,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 +78,15 @@
 
     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
+    virtual void setReleaseFence(const sp<Fence>& fence) override;
     void releasePendingBuffer();
 #endif
 
-    virtual bool getFrameTimestamps(uint64_t frameNumber,
-            FrameTimestamps* outTimestamps) const override;
+    virtual void addAndGetFrameTimestamps(
+            const NewFrameEventsEntry* newTimestamps,
+            FrameEventHistoryDelta* outDelta) override;
 
 private:
     virtual void onSidebandStreamChanged();
@@ -107,11 +107,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 2d36b54..7b83834 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>
@@ -70,7 +72,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"
@@ -134,6 +138,7 @@
         mTransactionPending(false),
         mAnimTransactionPending(false),
         mLayersRemoved(false),
+        mLayersAdded(false),
         mRepaintEverything(0),
         mRenderEngine(NULL),
         mBootTime(systemTime()),
@@ -150,6 +155,7 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
+        mInterceptor(),
         mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
@@ -158,11 +164,11 @@
         mHasPoweredOff(false),
         mFrameBuckets(),
         mTotalTime(0),
-        mLastSwapTime(0)
+        mLastSwapTime(0),
+        mNumLayers(0)
 {
     ALOGI("SurfaceFlinger is starting");
 
-    // debugging stuff...
     char value[PROPERTY_VALUE_MAX];
 
     property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
@@ -185,6 +191,10 @@
     property_get("debug.sf.disable_hwc_vds", value, "0");
     mUseHwcVirtualDisplays = !atoi(value);
     ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+
+    property_get("ro.sf.disable_triple_buffer", value, "0");
+    mLayerTripleBufferingDisabled = !atoi(value);
+    ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -210,15 +220,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 +257,7 @@
              flinger->setTransactionFlags(eDisplayTransactionNeeded);
          }
      public:
-        DisplayToken(const sp<SurfaceFlinger>& flinger)
+        explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
             : flinger(flinger) {
         }
     };
@@ -244,7 +268,7 @@
     DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
     info.displayName = displayName;
     mCurrentState.displays.add(token, info);
-
+    mInterceptor.saveDisplayCreation(info);
     return token;
 }
 
@@ -262,7 +286,7 @@
         ALOGE("destroyDisplay called for non-virtual display");
         return;
     }
-
+    mInterceptor.saveDisplayDeletion(info.displayId);
     mCurrentState.displays.removeItemsAt(idx);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -274,6 +298,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) {
@@ -446,6 +471,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 +508,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
@@ -572,6 +621,22 @@
     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::GL_COMPOSITION_DONE,
+        FrameEvent::DISPLAY_RETIRE,
+        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)) {
@@ -809,6 +874,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() {
@@ -976,7 +1075,7 @@
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
-    uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+    uint32_t transactionFlags = peekTransactionFlags();
     if (transactionFlags) {
         handleTransaction(transactionFlags);
         return true;
@@ -994,12 +1093,12 @@
 
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    preComposition();
+    preComposition(refreshStartTime);
     rebuildLayerStacks();
     setUpHWComposer();
     doDebugFlashRegions();
     doComposition();
-    postComposition(refreshStartTime);
+    postComposition();
 }
 
 void SurfaceFlinger::doDebugFlashRegions()
@@ -1042,59 +1141,70 @@
     }
 }
 
-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)
+void SurfaceFlinger::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));
-        }
-    }
-
     const HWComposer& hwc = getHwComposer();
-    sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+    const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
 
-    if (presentFence->isValid()) {
-        if (mPrimaryDispSync.addPresentFence(presentFence)) {
+    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> displayFence = mHwc->getDisplayFence(HWC_DISPLAY_PRIMARY);
+    const std::shared_ptr<FenceTime>& presentFenceTime = FenceTime::NO_FENCE;
+    auto retireFenceTime = std::make_shared<FenceTime>(displayFence);
+    mDisplayTimeline.push(retireFenceTime);
+    mDisplayTimeline.updateSignalTimes();
+
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime,
+                presentFenceTime, retireFenceTime);
+
+        if (frameLatched) {
+            recordBufferingStats(layer->getName().string(),
+                    layer->getOccupancyHistory(false));
+        }
+    });
+
+    if (displayFence->isValid()) {
+        if (mPrimaryDispSync.addPresentFence(displayFence)) {
             enableHardwareVsync();
         } else {
             disableHardwareVsync(false);
         }
     }
 
-    const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
     if (kIgnorePresentFences) {
         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 +1242,6 @@
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        const LayerVector& layers(mDrawingState.layersSortedByZ);
         for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
             Region opaqueRegion;
             Region dirtyRegion;
@@ -1141,14 +1250,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 +1262,7 @@
                             layersSortedByZ.add(layer);
                         }
                     }
-                }
+                });
             }
             hw->setVisibleLayersSortedByZ(layersSortedByZ);
             hw->undefinedRegion.set(bounds);
@@ -1379,13 +1485,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 +1496,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;
-        }
+        });
     }
 
     /*
@@ -1588,13 +1690,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,
@@ -1622,7 +1724,9 @@
                 disp = getDefaultDisplayDevice();
             }
             layer->updateTransformHint(disp);
-        }
+
+            first = false;
+        });
     }
 
 
@@ -1630,9 +1734,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 +1745,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 +1793,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 +1806,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 +1825,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 +1866,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 +1883,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 +1940,7 @@
         layer->setCoveredRegion(coveredRegion);
         layer->setVisibleNonTransparentRegion(
                 visibleRegion.subtract(transparentRegion));
-    }
+    });
 
     outOpaqueRegion = aboveOpaqueLayers;
 }
@@ -1857,10 +1957,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 +1973,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 +2161,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 +2205,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
@@ -2133,17 +2238,25 @@
         return NO_ERROR;
     }
 
-    ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
-    if (index >= 0) {
-        mLayersPendingRemoval.push(layer);
-        mLayersRemoved = true;
-        setTransactionFlags(eTransactionNeeded);
-        return NO_ERROR;
+    const auto& p = layer->getParent();
+    const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
+             mCurrentState.layersSortedByZ.remove(layer);
+
+    if (index < 0) {
+        ALOGE("Failed to find layer (%s) in layer parent (%s).",
+                layer->getName().string(),
+                (p != nullptr) ? p->getName().string() : "no-parent");
+        return BAD_VALUE;
     }
-    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 +2332,10 @@
     }
 
     if (transactionFlags) {
+        if (mInterceptor.isEnabled()) {
+            mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+        }
+
         // this triggers the transaction
         setTransactionFlags(transactionFlags);
 
@@ -2310,13 +2427,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) {
@@ -2349,9 +2473,17 @@
                 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)
@@ -2364,6 +2496,11 @@
             // 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::eOverrideScalingModeChanged) {
             layer->setOverrideScalingMode(s.overrideScalingMode);
             // We don't trigger a traversal here because if no other state is
@@ -2377,9 +2514,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));
@@ -2410,10 +2547,13 @@
         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;
@@ -2461,6 +2601,7 @@
     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));
@@ -2504,7 +2645,7 @@
     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 +2673,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);
@@ -2664,9 +2815,9 @@
             }
 
             if ((index < numArgs) &&
-                    (args[index] == String16("--fences"))) {
+                    (args[index] == String16("--frame-events"))) {
                 index++;
-                mFenceTracker.dump(&result);
+                dumpFrameEventsLocked(result);
                 dumpAll = false;
             }
         }
@@ -2686,12 +2837,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,
@@ -2710,14 +2858,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);
             }
-        }
+        });
     }
 }
 
@@ -2730,14 +2875,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();
 }
@@ -2745,31 +2887,25 @@
 // 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"
+    result.append(" [sf");
 #ifdef HAS_CONTEXT_PRIORITY
-            " HAS_CONTEXT_PRIORITY"
+    result.append(" HAS_CONTEXT_PRIORITY");
 #endif
 #ifdef NEVER_DEFAULT_TO_ASYNC_MODE
-            " NEVER_DEFAULT_TO_ASYNC_MODE"
+    result.append(" NEVER_DEFAULT_TO_ASYNC_MODE");
 #endif
-#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
-            " TARGET_DISABLE_TRIPLE_BUFFERING"
-#endif
-            "]";
-    result.append(config);
+    if (isLayerTripleBufferingDisabled())
+        result.append(" DISABLE_TRIPLE_BUFFERING");
+    result.append("]");
 }
 
 void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
@@ -2789,6 +2925,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);
@@ -2897,15 +3043,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
@@ -3031,7 +3174,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:
@@ -3050,6 +3192,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
@@ -3194,6 +3347,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;
@@ -3295,7 +3460,7 @@
     }
 
 public:
-    GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+    explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
     :   impl(impl),
         looper(new Looper(true)),
         result(NO_ERROR),
@@ -3329,7 +3494,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))
@@ -3370,7 +3535,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;
@@ -3380,7 +3545,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)
@@ -3429,7 +3594,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();
@@ -3473,20 +3638,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
@@ -3499,7 +3668,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)
 {
@@ -3523,16 +3692,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) {
@@ -3651,13 +3820,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;
@@ -3668,76 +3832,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..2d6472a
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -0,0 +1,582 @@
+/*
+ * 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 {
+
+// ----------------------------------------------------------------------------
+
+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.handle != nullptr) {
+        addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.handle,
+                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 wp<const IBinder>& weakHandle, uint64_t frameNumber)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    const sp<const Layer> layer(getLayer(weakHandle));
+    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) {
+        addDeferTransactionLocked(transaction, layerId, state.handle, 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..9af6e61
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -0,0 +1,136 @@
+/*
+ * 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;
+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:
+    // 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 wp<const IBinder>& weakHandle, 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 {};
+};
+
+}
+
+#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk
index 979062e..f93d918 100644
--- a/services/surfaceflinger/tests/Android.mk
+++ b/services/surfaceflinger/tests/Android.mk
@@ -9,15 +9,22 @@
 
 LOCAL_SRC_FILES := \
     Transaction_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 \
+    liblog
+
+LOCAL_STATIC_LIBRARIES := libtrace_proto
+
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
 # Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
 # to integrate with auto-test framework.
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
new file mode 100644
index 0000000..1a3f89f
--- /dev/null
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -0,0 +1,856 @@
+/*
+ * 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 <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) {
+    std::ifstream input(DEFAULT_FILENAME, std::ios::in | std::ios::binary);
+    if (input && !trace->ParseFromIstream(&input)) {
+        return PERMISSION_DENIED;
+    }
+    return NO_ERROR;
+}
+
+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..d9f1438 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -61,7 +61,6 @@
         sp<IGraphicBufferProducer> producer;
         sp<IGraphicBufferConsumer> consumer;
         BufferQueue::createBufferQueue(&producer, &consumer);
-        IGraphicBufferProducer::QueueBufferOutput bufferOutput;
         sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         sp<IBinder> display(sf->getBuiltInDisplay(
@@ -85,6 +84,18 @@
         }
     }
 
+    void expectFGColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 195, 63, 63);
+    }
+
+    void expectBGColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 63, 63, 195);
+    }
+
+    void expectChildColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 200, 200, 200);
+    }
+
 private:
     ScreenCapture(const sp<CpuConsumer>& cc) :
         mCC(cc) {
@@ -141,14 +152,14 @@
 
         mComposerClient->setDisplayLayerStack(display, 0);
 
-        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-2));
+        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX-2));
         ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
 
-        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX-1));
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX-1));
         ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
         ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
 
-        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT_MAX-1));
+        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT32_MAX-1));
         ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setPosition(displayWidth-2,
                 displayHeight-2));
         ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->show());
@@ -518,4 +529,105 @@
     }
 }
 
+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);
+    }
+}
 }
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..af8212a
--- /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..492acb2
--- /dev/null
+++ b/services/vr/bufferhubd/Android.mk
@@ -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.
+
+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 := \
+	libchrome \
+	libperformance \
+	libpdx_default_transport \
+	libbufferhub
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	libhardware \
+	liblog \
+	libsync \
+	libutils
+
+include $(CLEAR_VARS)
+# Don't strip symbols so we see stack traces in logcat.
+LOCAL_STRIP_MODULE := false
+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..a0c7439
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -0,0 +1,474 @@
+#include "buffer_hub.h"
+
+#include <cutils/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::Message;
+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() && IonBuffer::GetGrallocModule();
+}
+
+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(10) << "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.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 << 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 << " ";
+  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_set_mask;
+      stream << std::setw(7) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_clear_mask;
+      stream << std::setw(9) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_deny_set_mask;
+      stream << std::setw(11) << std::setfill(' ') << "0x";
+      stream << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage_deny_clear_mask;
+    }
+  }
+
+  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;
+    }
+  }
+
+  return stream.str();
+}
+
+void BufferHubService::HandleImpulse(Message& message) {
+  ATRACE_NAME("BufferHubService::HandleImpulse");
+  if (auto channel = message.GetChannel<BufferHubChannel>())
+    channel->HandleImpulse(message);
+}
+
+int 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 0;
+
+    case BufferHubRPC::CreatePersistentBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::CreatePersistentBuffer>(
+          *this, &BufferHubService::OnCreatePersistentBuffer, message);
+      return 0;
+
+    case BufferHubRPC::GetPersistentBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetPersistentBuffer>(
+          *this, &BufferHubService::OnGetPersistentBuffer, message);
+      return 0;
+
+    case BufferHubRPC::CreateProducerQueue::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::CreateProducerQueue>(
+          *this, &BufferHubService::OnCreateProducerQueue, message);
+      return 0;
+
+    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();
+}
+
+int BufferHubService::OnCreateBuffer(Message& message, int width, int height,
+                                     int format, int 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=%d height=%d "
+           "format=%d usage=%d meta_size_bytes=%zu slice_count=%zu",
+           buffer_id, width, height, format, 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 -EALREADY;
+  }
+
+  int error;
+  if (const auto producer_channel =
+          ProducerChannel::Create(this, buffer_id, width, height, format, usage,
+                                  meta_size_bytes, slice_count, &error)) {
+    message.SetChannel(producer_channel);
+    return 0;
+  } else {
+    ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+    return error;
+  }
+}
+
+int BufferHubService::OnCreatePersistentBuffer(
+    Message& message, const std::string& name, int user_id, int group_id,
+    int width, int height, int format, int usage, size_t meta_size_bytes,
+    size_t slice_count) {
+  const int channel_id = message.GetChannelId();
+  ALOGD_IF(TRACE,
+           "BufferHubService::OnCreatePersistentBuffer: channel_id=%d name=%s "
+           "user_id=%d group_id=%d width=%d height=%d format=%d usage=%d "
+           "meta_size_bytes=%zu slice_count=%zu",
+           channel_id, name.c_str(), user_id, group_id, width, height, format,
+           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 -EALREADY;
+  }
+
+  const int euid = message.GetEffectiveUserId();
+  const int egid = message.GetEffectiveGroupId();
+  int error;
+
+  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 -EPERM;
+    } else if (!buffer->CheckParameters(width, height, format, usage,
+                                        meta_size_bytes, slice_count)) {
+      ALOGE(
+          "BufferHubService::OnCreatePersistentBuffer: Requested an existing "
+          "buffer with different parameters: name=%s",
+          name.c_str());
+      return -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 -EINVAL;
+    } else {
+      buffer->Attach(channel_id);
+      message.SetChannel(buffer);
+      return 0;
+    }
+  } else if (auto buffer = ProducerChannel::Create(
+                 this, channel_id, width, height, format, usage,
+                 meta_size_bytes, slice_count, &error)) {
+    const int ret =
+        buffer->OnProducerMakePersistent(message, name, user_id, group_id);
+    if (!ret)
+      message.SetChannel(buffer);
+    return ret;
+  } else {
+    ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+    return error;
+  }
+}
+
+int 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 -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 -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 -EINVAL;
+    } else {
+      buffer->Attach(channel_id);
+      message.SetChannel(buffer);
+      return 0;
+    }
+  } else {
+    ALOGE("BufferHubService::OnGetPersistentBuffer: Buffer \"%s\" not found!",
+          name.c_str());
+    return -ENOENT;
+  }
+}
+
+int BufferHubService::OnCreateProducerQueue(
+    pdx::Message& message, size_t meta_size_bytes, int usage_set_mask,
+    int usage_clear_mask, int usage_deny_set_mask, int usage_deny_clear_mask) {
+  // 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 -EALREADY;
+  }
+
+  int error;
+  if (const auto producer_channel = ProducerQueueChannel::Create(
+          this, queue_id, meta_size_bytes, usage_set_mask, usage_clear_mask,
+          usage_deny_set_mask, usage_deny_clear_mask, &error)) {
+    message.SetChannel(producer_channel);
+    return 0;
+  } else {
+    ALOGE("BufferHubService::OnCreateBuffer: Failed to create producer!!");
+    return error;
+  }
+}
+
+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");
+  if (!IsDetached()) {
+    const int ret = service_->ModifyChannelEvents(channel_id_, 0, POLLIN);
+    ALOGE_IF(ret < 0,
+             "BufferHubChannel::SignalAvailable: failed to signal availability "
+             "channel_id=%d: %s",
+             channel_id_, strerror(-ret));
+  } else {
+    ALOGD_IF(TRACE, "BufferHubChannel::SignalAvailable: detached buffer.");
+  }
+}
+
+void BufferHubChannel::ClearAvailable() {
+  ATRACE_NAME("BufferHubChannel::ClearAvailable");
+  if (!IsDetached()) {
+    const int ret = service_->ModifyChannelEvents(channel_id_, POLLIN, 0);
+    ALOGE_IF(ret < 0,
+             "BufferHubChannel::ClearAvailable: failed to clear availability "
+             "channel_id=%d: %s",
+             channel_id_, strerror(-ret));
+  } else {
+    ALOGD_IF(TRACE, "BufferHubChannel::ClearAvailable: detached buffer.");
+  }
+}
+
+void BufferHubChannel::Hangup() {
+  ATRACE_NAME("BufferHubChannel::Hangup");
+  if (!IsDetached()) {
+    const int ret = service_->ModifyChannelEvents(channel_id_, 0, POLLHUP);
+    ALOGE_IF(
+        ret < 0,
+        "BufferHubChannel::Hangup: failed to signal hangup channel_id=%d: %s",
+        channel_id_, strerror(-ret));
+  } 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..28cb468
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.h
@@ -0,0 +1,182 @@
+#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>
+
+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.
+    int width = 0;
+    int height = 0;
+    int format = 0;
+    int usage = 0;
+    size_t slice_count = 0;
+    std::string name;
+
+    // Data filed for producer queue.
+    size_t capacity = 0;
+    int usage_set_mask = 0;
+    int usage_clear_mask = 0;
+    int usage_deny_set_mask = 0;
+    int usage_deny_clear_mask = 0;
+
+    BufferInfo(int id, size_t consumer_count, int width, int height, int format,
+               int usage, size_t slice_count, const std::string& name)
+        : id(id),
+          type(kProducerType),
+          consumer_count(consumer_count),
+          width(width),
+          height(height),
+          format(format),
+          usage(usage),
+          slice_count(slice_count),
+          name(name) {}
+
+    BufferInfo(int id, size_t consumer_count, size_t capacity, int usage_set_mask,
+               int usage_clear_mask, int usage_deny_set_mask,
+               int usage_deny_clear_mask)
+        : id(id),
+          type(kProducerQueueType),
+          consumer_count(consumer_count),
+          capacity(capacity),
+          usage_set_mask(usage_set_mask),
+          usage_clear_mask(usage_clear_mask),
+          usage_deny_set_mask(usage_deny_set_mask),
+          usage_deny_clear_mask(usage_deny_clear_mask) {}
+
+    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;
+
+  int 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_;
+
+  int OnCreateBuffer(pdx::Message& message, int width, int height, int format,
+                     int usage, size_t meta_size_bytes, size_t slice_count);
+  int OnCreatePersistentBuffer(pdx::Message& message, const std::string& name,
+                               int user_id, int group_id, int width, int height,
+                               int format, int usage, size_t meta_size_bytes,
+                               size_t slice_count);
+  int OnGetPersistentBuffer(pdx::Message& message, const std::string& name);
+  int OnCreateProducerQueue(pdx::Message& message, size_t meta_size_bytes,
+                            int usage_set_mask, int usage_clear_mask,
+                            int usage_deny_set_mask, int usage_deny_clear_mask);
+
+  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..a8e2ddf
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -0,0 +1,37 @@
+#include <sched.h>
+#include <unistd.h>
+
+#include <cutils/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..ceedf1a
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.rc
@@ -0,0 +1,6 @@
+service bufferhubd /system/bin/bufferhubd
+  class core
+  user system
+  group system
+  cpuset /
+
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
new file mode 100644
index 0000000..8db92a3
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -0,0 +1,182 @@
+#include "consumer_channel.h"
+
+#include <cutils/log.h>
+#include <utils/Trace.h>
+
+#include <thread>
+
+#include <private/dvr/bufferhub_rpc.h>
+#include "producer_channel.h"
+
+using android::pdx::BorrowedHandle;
+using android::pdx::Channel;
+using android::pdx::Message;
+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",
+           channel_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;
+  }
+}
+
+std::pair<BorrowedFence, ConsumerChannel::MetaData>
+ConsumerChannel::OnConsumerAcquire(Message& message,
+                                   std::size_t metadata_size) {
+  ATRACE_NAME("ConsumerChannel::OnConsumerAcquire");
+  auto producer = GetProducer();
+  if (!producer)
+    REPLY_ERROR_RETURN(message, 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());
+    REPLY_ERROR_RETURN(message, EBUSY, {});
+  } else {
+    ClearAvailable();
+    return producer->OnConsumerAcquire(message, metadata_size);
+  }
+}
+
+int ConsumerChannel::OnConsumerRelease(Message& message,
+                                       LocalFence release_fence) {
+  ATRACE_NAME("ConsumerChannel::OnConsumerRelease");
+  auto producer = GetProducer();
+  if (!producer)
+    return -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 -EBUSY;
+  } else {
+    ClearAvailable();
+    const int ret =
+        producer->OnConsumerRelease(message, std::move(release_fence));
+    handled_ = ret == 0;
+    return ret;
+  }
+}
+
+int ConsumerChannel::OnConsumerSetIgnore(Message&, bool ignored) {
+  ATRACE_NAME("ConsumerChannel::OnConsumerSetIgnore");
+  auto producer = GetProducer();
+  if (!producer)
+    return -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 0;
+}
+
+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..d2a078f
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.h
@@ -0,0 +1,51 @@
+#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;
+
+  std::pair<BorrowedFence, MetaData> OnConsumerAcquire(
+      Message& message, std::size_t metadata_size);
+  int OnConsumerRelease(Message& message, LocalFence release_fence);
+  int 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..39d6bc8
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -0,0 +1,122 @@
+#include "consumer_queue_channel.h"
+
+#include <pdx/channel_handle.h>
+
+#include "producer_channel.h"
+
+using android::pdx::RemoteChannelHandle;
+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::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();
+}
+
+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.
+    if (producer_channel == nullptr) {
+      ALOGE(
+          "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
+          "channel has already been expired.");
+      REPLY_ERROR_RETURN(message, ENOENT, {});
+    }
+
+    RemoteChannelHandle consumer_handle(
+        producer_channel->CreateConsumer(message));
+
+    // All buffer imports should succeed together.
+    if (!consumer_handle.valid()) {
+      ALOGE(
+          "ConsumerQueueChannel::OnConsumerQueueImportBuffers: imported "
+          "consumer handle is invalid.");
+      REPLY_ERROR_RETURN(message, EIO, {});
+    }
+
+    // Move consumer_handle into buffer_handles.
+    buffer_handles.emplace_back(std::move(consumer_handle), producer_slot);
+  }
+
+  return 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..b345595
--- /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.
+  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..b87b709
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -0,0 +1,377 @@
+#include "producer_channel.h"
+
+#include <cutils/log.h>
+#include <sync/sync.h>
+#include <sys/poll.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <atomic>
+#include <thread>
+
+#include <base/logging.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+
+using android::pdx::BorrowedHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+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,
+                                 int width, int height, int format, int 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, usage);
+    if (ret < 0) {
+      ALOGE("ProducerChannel::ProducerChannel: Failed to allocate buffer: %s",
+            strerror(-ret));
+      *error = ret;
+      return;
+    }
+  }
+
+  // Success.
+  *error = 0;
+}
+
+std::shared_ptr<ProducerChannel> ProducerChannel::Create(
+    BufferHubService* service, int channel_id, int width, int height,
+    int format, int 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, usage,
+                          meta_size_bytes, slice_count, error));
+  if (*error < 0)
+    return nullptr;
+  else
+    return producer;
+}
+
+ProducerChannel::~ProducerChannel() {
+  ALOGD_IF(TRACE, "ProducerChannel::~ProducerChannel: channel_id=%d",
+           channel_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].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;
+  }
+}
+
+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 {
+    REPLY_ERROR_RETURN(message, EINVAL, NativeBufferHandle<BorrowedHandle>());
+  }
+}
+
+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 buffer_handles;
+}
+
+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 RemoteChannelHandle();
+  }
+
+  auto consumer = std::make_shared<ConsumerChannel>(
+      service(), buffer_id(), channel_id, shared_from_this());
+  const int ret = service()->SetChannel(channel_id, consumer);
+  if (ret < 0) {
+    ALOGE(
+        "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
+        "%s",
+        strerror(-ret));
+    return RemoteChannelHandle();
+  }
+
+  if (!producer_owns_) {
+    // Signal the new consumer when adding it to a posted producer.
+    if (consumer->OnProducerPosted())
+      pending_consumers_++;
+  }
+
+  return status.take();
+}
+
+RemoteChannelHandle ProducerChannel::OnNewConsumer(Message& message) {
+  ATRACE_NAME("ProducerChannel::OnNewConsumer");
+  ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id());
+
+  RemoteChannelHandle consumer_handle(CreateConsumer(message));
+
+  if (consumer_handle.valid())
+    return consumer_handle;
+  else
+    REPLY_ERROR_RETURN(message, ENOMEM, RemoteChannelHandle());
+}
+
+int 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 -EBUSY;
+  }
+
+  if (meta_size_bytes_ != metadata.size())
+    return -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 0;
+}
+
+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());
+    REPLY_ERROR_RETURN(message, EALREADY, {});
+  }
+
+  // There are still pending consumers, return busy.
+  if (pending_consumers_ > 0)
+    REPLY_ERROR_RETURN(message, EBUSY, {});
+
+  ClearAvailable();
+  producer_owns_ = true;
+  post_fence_.get_fd();
+  return std::move(returned_fence_);
+}
+
+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!");
+    REPLY_ERROR_RETURN(message, 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_));
+}
+
+int 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 -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 -error;
+      }
+      returned_fence_ = std::move(merged_fence);
+    } else {
+      returned_fence_ = std::move(release_fence);
+    }
+  }
+
+  OnConsumerIgnored();
+  return 0;
+}
+
+void ProducerChannel::OnConsumerIgnored() {
+  if (!--pending_consumers_)
+    SignalAvailable();
+  ALOGD_IF(TRACE,
+           "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left",
+           buffer_id(), pending_consumers_);
+}
+
+int 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 -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 0;
+  } else {
+    // Otherwise a buffer with that name already exists.
+    return -EALREADY;
+  }
+}
+
+int ProducerChannel::OnRemovePersistence(Message&) {
+  if (service()->RemoveNamedBuffer(*this))
+    return 0;
+  else
+    return -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(int width, int height, int format,
+                                      int 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].usage() == usage;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h
new file mode 100644
index 0000000..e7ca459
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -0,0 +1,109 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <pdx/channel_handle.h>
+#include <pdx/file_handle.h>
+#include <pdx/rpc/buffer_wrapper.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+// The buffer changes ownership according to the following sequence:
+// POST -> ACQUIRE/RELEASE (all consumers) -> GAIN (producer acquires) -> POST
+
+// The producer channel is owned by a single app that writes into buffers and
+// calls POST when drawing is complete. This channel has a set of consumer
+// channels associated with it that are waiting for notifications.
+class ProducerChannel : public BufferHubChannel {
+ public:
+  using Message = pdx::Message;
+  using BorrowedHandle = pdx::BorrowedHandle;
+  using RemoteChannelHandle = pdx::RemoteChannelHandle;
+  template <typename T>
+  using BufferWrapper = pdx::rpc::BufferWrapper<T>;
+
+  static std::shared_ptr<ProducerChannel> Create(
+      BufferHubService* service, int channel_id, int width, int height,
+      int format, int usage, size_t meta_size_bytes, size_t slice_count,
+      int* error);
+
+  ~ProducerChannel() override;
+
+  bool HandleMessage(Message& message) override;
+  void HandleImpulse(Message& message) override;
+
+  BufferInfo GetBufferInfo() const override;
+
+  NativeBufferHandle<BorrowedHandle> OnGetBuffer(Message& message,
+                                                 unsigned index);
+  std::vector<NativeBufferHandle<BorrowedHandle>> OnGetBuffers(
+      Message& message);
+
+  RemoteChannelHandle CreateConsumer(Message& message);
+  RemoteChannelHandle OnNewConsumer(Message& message);
+
+  std::pair<BorrowedFence, BufferWrapper<std::uint8_t*>> OnConsumerAcquire(
+      Message& message, std::size_t metadata_size);
+  int OnConsumerRelease(Message& message, LocalFence release_fence);
+
+  void OnConsumerIgnored();
+
+  void AddConsumer(ConsumerChannel* channel);
+  void RemoveConsumer(ConsumerChannel* channel);
+
+  bool CheckAccess(int euid, int egid);
+  bool CheckParameters(int width, int height, int format, int usage,
+                       size_t meta_size_bytes, size_t slice_count);
+
+  int OnProducerMakePersistent(Message& message, const std::string& name,
+                               int user_id, int group_id);
+  int 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, int width, int height,
+                  int format, int usage, size_t meta_size_bytes,
+                  size_t slice_count, int* error);
+
+  int OnProducerPost(Message& message, LocalFence acquire_fence,
+                     BufferWrapper<std::vector<std::uint8_t>> metadata);
+  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..08f1e9d
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -0,0 +1,287 @@
+#include "producer_queue_channel.h"
+
+#include "consumer_queue_channel.h"
+#include "producer_channel.h"
+
+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,
+    int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+    int usage_deny_clear_mask, int* error)
+    : BufferHubChannel(service, channel_id, channel_id, kProducerQueueType),
+      meta_size_bytes_(meta_size_bytes),
+      usage_set_mask_(usage_set_mask),
+      usage_clear_mask_(usage_clear_mask),
+      usage_deny_set_mask_(usage_deny_set_mask),
+      usage_deny_clear_mask_(usage_deny_clear_mask),
+      capacity_(0) {
+  *error = 0;
+}
+
+ProducerQueueChannel::~ProducerQueueChannel() {}
+
+/* static */
+std::shared_ptr<ProducerQueueChannel> ProducerQueueChannel::Create(
+    BufferHubService* service, int channel_id, size_t meta_size_bytes,
+    int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+    int usage_deny_clear_mask, int* error) {
+  // Configuration between |usage_deny_set_mask| and |usage_deny_clear_mask|
+  // should be mutually exclusive.
+  if (usage_deny_set_mask & usage_deny_clear_mask) {
+    ALOGE(
+        "BufferHubService::OnCreateProducerQueue: illegal usage mask "
+        "configuration: usage_deny_set_mask=%d, usage_deny_clear_mask=%d",
+        usage_deny_set_mask, usage_deny_clear_mask);
+    *error = -EINVAL;
+    return nullptr;
+  }
+
+  std::shared_ptr<ProducerQueueChannel> producer(new ProducerQueueChannel(
+      service, channel_id, meta_size_bytes, usage_set_mask, usage_clear_mask,
+      usage_deny_set_mask, usage_deny_clear_mask, error));
+  if (*error < 0)
+    return nullptr;
+  else
+    return 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::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_set_mask_, usage_clear_mask_, usage_deny_set_mask_,
+                    usage_deny_clear_mask_);
+}
+
+std::pair<RemoteChannelHandle, size_t>
+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());
+    REPLY_ERROR_RETURN(message, ENOMEM, {});
+  }
+
+  const int ret = service()->SetChannel(
+      channel_id, std::make_shared<ConsumerQueueChannel>(
+                      service(), buffer_id(), channel_id, shared_from_this()));
+  if (ret < 0) {
+    ALOGE(
+        "ProducerQueueChannel::OnCreateConsumerQueue: failed to set new "
+        "consumer channel: %s",
+        strerror(-ret));
+    REPLY_ERROR_RETURN(message, ENOMEM, {});
+  }
+
+  return std::make_pair(status.take(), meta_size_bytes_);
+}
+
+std::vector<std::pair<RemoteChannelHandle, size_t>>
+ProducerQueueChannel::OnProducerQueueAllocateBuffers(Message& message,
+                                                     int width, int height,
+                                                     int format, int 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 (usage & usage_deny_set_mask_) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueAllocateBuffers: usage: %d is "
+        "not permitted. Violating usage_deny_set_mask, the following bits "
+        "shall not be set: %d.",
+        usage, usage_deny_set_mask_);
+    REPLY_ERROR_RETURN(message, EINVAL, buffer_handles);
+  }
+
+  if (~usage & usage_deny_clear_mask_) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueAllocateBuffers: usage: %d is "
+        "not permitted. Violating usage_deny_clear_mask, the following bits "
+        "must be set: %d.",
+        usage, usage_deny_clear_mask_);
+    REPLY_ERROR_RETURN(message, EINVAL, buffer_handles);
+  }
+
+  // Force set mask and clear mask. Note that |usage_set_mask_| takes precedence
+  // and will overwrite |usage_clear_mask_|.
+  int effective_usage = (usage & ~usage_clear_mask_) | usage_set_mask_;
+
+  for (size_t i = 0; i < buffer_count; i++) {
+    auto buffer_handle_slot = AllocateBuffer(message, width, height, format,
+                                             effective_usage, slice_count);
+    if (!buffer_handle_slot.first) {
+      ALOGE(
+          "ProducerQueueChannel::OnProducerQueueAllocateBuffers: failed to "
+          "allocate new buffer.");
+      REPLY_ERROR_RETURN(message, ENOMEM, buffer_handles);
+    }
+    buffer_handles.emplace_back(std::move(buffer_handle_slot.first),
+                                buffer_handle_slot.second);
+  }
+
+  return buffer_handles;
+}
+
+std::pair<RemoteChannelHandle, size_t> ProducerQueueChannel::AllocateBuffer(
+    Message& message, int width, int height, int format, int 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 {};
+  }
+
+  // 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 {};
+  }
+
+  ALOGD_IF(TRACE,
+           "ProducerQueueChannel::AllocateBuffer: buffer_id=%d width=%d "
+           "height=%d format=%d usage=%d slice_count=%zu",
+           buffer_id, width, height, format, usage, slice_count);
+  auto buffer_handle = status.take();
+
+  int error;
+  const auto producer_channel = ProducerChannel::Create(
+      service(), buffer_id, width, height, format, usage,
+      meta_size_bytes_, slice_count, &error);
+  if (!producer_channel) {
+    ALOGE(
+        "ProducerQueueChannel::AllocateBuffer: Failed to create "
+        "BufferHubBuffer producer!!");
+    return {};
+  }
+
+  ALOGD_IF(
+      TRACE,
+      "ProducerQueueChannel::AllocateBuffer: buffer_id=%d, buffer_handle=%d",
+      buffer_id, buffer_handle.value());
+
+  const int ret = service()->SetChannel(buffer_id, producer_channel);
+  if (ret < 0) {
+    ALOGE(
+        "ProducerQueueChannel::AllocateBuffer: failed to set prodcuer channel "
+        "for new BufferHubBuffer: %s",
+        strerror(-ret));
+    return {};
+  }
+
+  // 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 {};
+  }
+
+  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};
+}
+
+int 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 -EINVAL;
+  }
+
+  if (capacity_ == 0) {
+    ALOGE(
+        "ProducerQueueChannel::OnProducerQueueDetachBuffer: trying to detach a "
+        "buffer producer while the queue's capacity is already zero.");
+    return -EINVAL;
+  }
+
+  buffers_[slot].reset();
+  capacity_--;
+  return 0;
+}
+
+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..49611d4
--- /dev/null
+++ b/services/vr/bufferhubd/producer_queue_channel.h
@@ -0,0 +1,96 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+#include <private/dvr/bufferhub_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class ProducerQueueChannel : public BufferHubChannel {
+ public:
+  using Message = pdx::Message;
+  using RemoteChannelHandle = pdx::RemoteChannelHandle;
+
+  static std::shared_ptr<ProducerQueueChannel> Create(
+      BufferHubService* service, int channel_id, size_t meta_size_bytes,
+      int usage_set_mask, int usage_clear_mask, int usage_deny_set_mask,
+      int usage_deny_clear_mask, int* error);
+  ~ProducerQueueChannel() override;
+
+  bool HandleMessage(Message& message) override;
+  void HandleImpulse(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.
+  std::pair<RemoteChannelHandle, size_t> OnCreateConsumerQueue(
+      Message& message);
+
+  // Allocate a new BufferHubProducer according to the input spec. Client may
+  // handle this as if a new producer is created through kOpCreateBuffer.
+  std::vector<std::pair<RemoteChannelHandle, size_t>>
+  OnProducerQueueAllocateBuffers(Message& message, int width, int height,
+                                 int format, int 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.
+  int OnProducerQueueDetachBuffer(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, int usage_set_mask,
+                       int usage_clear_mask, int usage_deny_set_mask,
+                       int usage_deny_clear_mask, 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 channdel handle and the slot number for the newly
+  // allocated buffer.
+  std::pair<RemoteChannelHandle, size_t> AllocateBuffer(Message& message,
+                                                        int width, int height,
+                                                        int format, int 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.
+  int usage_set_mask_;
+  int usage_clear_mask_;
+  int usage_deny_set_mask_;
+  int usage_deny_clear_mask_;
+
+  // 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/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..916226e
--- /dev/null
+++ b/services/vr/performanced/cpu_set.cpp
@@ -0,0 +1,287 @@
+#include "cpu_set.h"
+
+#include <cutils/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..114413d
--- /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/log.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.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..c99c8d4
--- /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();
+}
+
+int PerformanceService::HandleMessage(Message& message) {
+  switch (message.GetOp()) {
+    case PerformanceRPC::SetCpuPartition::Opcode:
+      DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+          *this, &PerformanceService::OnSetCpuPartition, message);
+      return 0;
+
+    case PerformanceRPC::SetSchedulerClass::Opcode:
+      DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+          *this, &PerformanceService::OnSetSchedulerClass, message);
+      return 0;
+
+    case PerformanceRPC::GetCpuPartition::Opcode:
+      DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>(
+          *this, &PerformanceService::OnGetCpuPartition, message);
+      return 0;
+
+    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..e32d834
--- /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:
+  int 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..754c97f
--- /dev/null
+++ b/services/vr/performanced/performanced.rc
@@ -0,0 +1,5 @@
+service performanced /system/bin/performanced
+  class core
+  user root
+  group system readproc
+  cpuset /
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..ad12858
--- /dev/null
+++ b/services/vr/performanced/task.cpp
@@ -0,0 +1,163 @@
+#include "task.h"
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <fcntl.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..907c3d6
--- /dev/null
+++ b/services/vr/sensord/Android.mk
@@ -0,0 +1,81 @@
+# 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_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 \
+	libsensor \
+	libperformance \
+	libbufferhub \
+	libpdx_default_transport \
+	libchrome \
+	libposepredictor \
+
+sharedLibraries := \
+	libandroid \
+	libbase \
+	libbinder \
+	libcutils \
+	liblog \
+	libhardware \
+	libutils \
+
+cFlags := -DLOG_TAG=\"sensord\" \
+          -DTRACE=0
+
+ifeq ($(TARGET_USES_QCOM_BSP), true)
+ifneq ($(TARGET_QCOM_DISPLAY_VARIANT),)
+    platform := .
+else
+    platform := $(TARGET_BOARD_PLATFORM)
+endif
+    cFlags += -DQCOM_B_FAMILY \
+              -DQCOM_BSP
+endif
+
+include $(CLEAR_VARS)
+# Don't strip symbols so we see stack traces in logcat.
+LOCAL_STRIP_MODULE := false
+LOCAL_SRC_FILES := $(sourceFiles)
+PLATFORM := $(platform)
+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)
diff --git a/services/vr/sensord/pose_service.cpp b/services/vr/sensord/pose_service.cpp
new file mode 100644
index 0000000..75919d8
--- /dev/null
+++ b/services/vr/sensord/pose_service.cpp
@@ -0,0 +1,676 @@
+#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/log.h>
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <hardware/sensors.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>
+
+#define arraysize(x) (static_cast<ssize_t>(std::extent<decltype(x)>::value))
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+using Vector3d = vec3d;
+using Vector3f = vec3f;
+using Rotationd = quatd;
+using Rotationf = quatf;
+using AngleAxisd = Eigen::AngleAxis<double>;
+using AngleAxisf = Eigen::AngleAxis<float>;
+
+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.dreamos.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";
+
+// Persistent buffer names.
+static constexpr char kPoseRingBufferName[] = "PoseService:RingBuffer";
+
+static constexpr int kDatasetIdLength = 36;
+static constexpr char kDatasetIdChars[] = "0123456789abcdef-";
+static constexpr char kDatasetLocation[] = "/data/sdcard/datasets/";
+
+// 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;
+
+// Extract yaw angle from a given quaternion rotation.
+// Y-axis is considered to be vertical. Result is in rad.
+template <typename T>
+T ExtractYaw(Eigen::Quaternion<T> rotation) {
+  const Eigen::Vector3<T> yaw_axis = rotation * vec3::UnitZ();
+  return std::atan2(yaw_axis.z(), yaw_axis.x());
+}
+
+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";
+  }
+}
+
+inline std::string GetVector3dString(const Vector3d& vector) {
+  std::ostringstream stream;
+  stream << "[" << vector[0] << "," << vector[1] << "," << vector[2] << "]";
+  return stream.str();
+}
+
+inline std::string GetRotationdString(const Rotationd& rotation) {
+  std::ostringstream stream;
+  stream << "[" << rotation.w() << ", " << GetVector3dString(rotation.vec())
+         << "]";
+  return stream.str();
+}
+
+}  // 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) {
+  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 dreamos 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());
+    }
+  }
+
+  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
+  pose_predictor_.Add(PosePredictor::Sample{.position = start_t_head,
+                                            .orientation = start_q_head,
+                                            .time_ns = 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_) {
+      pose_predictor_.Predict(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 {
+        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);
+
+      // IMU driver gives timestamps on its own clock, but we need monotonic
+      // clock. Subtract 5ms to account for estimated IMU sample latency.
+      WriteAsyncPoses(position, start_from_head_rotation,
+                      pose_state.timestamp_ns + 5000000);
+      break;
+    }
+    default:
+    case DVR_POSE_MODE_6DOF:
+      ALOGE("ERROR: invalid pose mode");
+      break;
+  }
+}
+
+int PoseService::HandleMessage(pdx::Message& msg) {
+  int ret = 0;
+  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_)},
+      };
+      constexpr int expected_size =
+          sizeof(vsync_count_) + sizeof(photon_timestamp_) +
+          sizeof(display_period_ns_) + sizeof(right_eye_photon_offset_ns_);
+      ret = msg.ReadVector(data, sizeof(data) / sizeof(data[0]));
+      if (ret < expected_size) {
+        ALOGI("error: msg.Read read too little (%d < %d)", ret, expected_size);
+        REPLY_ERROR(msg, EIO, error);
+      }
+
+      if (!enable_external_pose_) {
+        mapped_pose_buffer_->vsync_count = vsync_count_;
+      }
+
+      // TODO(jbates, eieio): make this async, no need to reply.
+      REPLY_SUCCESS(msg, 0, 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.Write(&client_state, sizeof(client_state));
+      const int expected_size = sizeof(client_state);
+      if (ret < expected_size) {
+        ALOGI("error: msg.Write wrote too little (%d < %d)", ret,
+              expected_size);
+        REPLY_ERROR(msg, EIO, error);
+      }
+      REPLY_SUCCESS(msg, 0, error);
+    }
+    case DVR_POSE_FREEZE: {
+      {
+        std::lock_guard<std::mutex> guard(mutex_);
+
+        DvrPoseState frozen_state;
+        const int expected_size = sizeof(frozen_state);
+        ret = msg.Read(&frozen_state, expected_size);
+        if (ret < expected_size) {
+          ALOGI("error: msg.Read read too little (%d < %d)", ret,
+                expected_size);
+          REPLY_ERROR(msg, EIO, error);
+        }
+        frozen_state_ = frozen_state;
+      }
+      SetPoseMode(DVR_POSE_MODE_MOCK_FROZEN);
+      REPLY_SUCCESS(msg, 0, error);
+    }
+    case DVR_POSE_SET_MODE: {
+      int mode;
+      {
+        std::lock_guard<std::mutex> guard(mutex_);
+        const int expected_size = sizeof(mode);
+        ret = msg.Read(&mode, expected_size);
+        if (ret < expected_size) {
+          ALOGI("error: msg.Read read too little (%d < %d)", ret,
+                expected_size);
+          REPLY_ERROR(msg, EIO, error);
+        }
+        if (mode < 0 || mode >= DVR_POSE_MODE_COUNT) {
+          REPLY_ERROR(msg, EINVAL, error);
+        }
+      }
+      SetPoseMode(DvrPoseMode(mode));
+      REPLY_SUCCESS(msg, 0, error);
+    }
+    case DVR_POSE_GET_MODE: {
+      std::lock_guard<std::mutex> guard(mutex_);
+      int mode = pose_mode_;
+      ret = msg.Write(&mode, sizeof(mode));
+      const int expected_size = sizeof(mode);
+      if (ret < expected_size) {
+        ALOGI("error: msg.Write wrote too little (%d < %d)", ret,
+              expected_size);
+        REPLY_ERROR(msg, EIO, error);
+      }
+      REPLY_SUCCESS(msg, 0, 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..300737c
--- /dev/null
+++ b/services/vr/sensord/pose_service.h
@@ -0,0 +1,143 @@
+#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/pose_client_internal.h>
+#include <private/dvr/ring_buffer.h>
+#include <private/dvr/linear_pose_predictor.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;
+  int 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,
+  };
+
+  // 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.
+  LinearPosePredictor 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;
+
+  // 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..59b433f
--- /dev/null
+++ b/services/vr/sensord/sensor_hal_thread.cpp
@@ -0,0 +1,158 @@
+#include "sensor_hal_thread.h"
+
+#include <cutils/log.h>
+#include <dvr/performance_client_api.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..b5e16e7
--- /dev/null
+++ b/services/vr/sensord/sensor_ndk_thread.cpp
@@ -0,0 +1,257 @@
+#include "sensor_ndk_thread.h"
+
+#include <cutils/log.h>
+#include <dvr/performance_client_api.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_);
+      initialization_result_ = InitializeSensors();
+      thread_started_ = true;
+      init_condition_.notify_one();
+      if (!initialization_result_)
+        return;
+    }
+
+    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 (looper_ && sensor_manager_) {
+        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();
+          InitializeSensors();
+        }
+      } 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.
+  *out_success = initialization_result_;
+}
+
+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);
+    }
+  }
+
+  return true;
+}
+
+void SensorNdkThread::DestroySensors() {
+  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_);
+}
+
+void SensorNdkThread::UpdateSensorUse() {
+  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..4396851
--- /dev/null
+++ b/services/vr/sensord/sensor_service.cpp
@@ -0,0 +1,187 @@
+#include "sensor_service.h"
+
+#include <cutils/log.h>
+#include <hardware/sensors.h>
+#include <poll.h>
+#include <pdx/default_transport/service_endpoint.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();
+}
+
+int SensorService::HandleMessage(pdx::Message& msg) {
+  int ret = 0;
+  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.Read(&sensor_type, sizeof(sensor_type)) <
+          (ssize_t)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.WriteVector(svec, 2);
+      int expected_size = sizeof(int) + num_events * sizeof(sensors_event_t);
+      if (ret < expected_size) {
+        ALOGI("error: msg.WriteVector wrote too little.");
+        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..c35fada
--- /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:
+  int 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..0a75318
--- /dev/null
+++ b/services/vr/sensord/sensord.cpp
@@ -0,0 +1,87 @@
+#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"
+
+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;
+
+int main(int, char**) {
+  ALOGI("Starting up...");
+
+  // 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..0311474
--- /dev/null
+++ b/services/vr/sensord/sensord.rc
@@ -0,0 +1,5 @@
+service sensord /system/bin/sensord
+  class core
+  user system
+  group system camera sdcard_rw
+  cpuset /system
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.mk b/services/vr/virtual_touchpad/Android.mk
new file mode 100644
index 0000000..4224aaa
--- /dev/null
+++ b/services/vr/virtual_touchpad/Android.mk
@@ -0,0 +1,76 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+
+# Touchpad implementation.
+
+src := \
+  EvdevInjector.cpp \
+  VirtualTouchpad.cpp
+
+shared_libs := \
+  libbase
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src)
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+LOCAL_CPPFLAGS += -std=c++11
+LOCAL_CFLAGS += -DLOG_TAG=\"VrVirtualTouchpad\"
+LOCAL_MODULE := libvirtualtouchpad
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
+
+
+# Touchpad unit tests.
+
+test_src_files := \
+  tests/VirtualTouchpad_test.cpp
+
+static_libs := \
+  libbase \
+  libcutils \
+  libvirtualtouchpad
+
+$(foreach file,$(test_src_files), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(static_libs)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libs)) \
+    $(eval LOCAL_CPPFLAGS += -std=c++11) \
+    $(eval LOCAL_LDLIBS := -llog) \
+    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+    $(eval LOCAL_MODULE_TAGS := optional) \
+    $(eval LOCAL_CXX_STL := libc++_static) \
+    $(eval include $(BUILD_NATIVE_TEST)) \
+)
+
+
+# Service.
+
+src := \
+  main.cpp \
+  VirtualTouchpadService.cpp \
+  aidl/android/dvr/VirtualTouchpadService.aidl
+
+static_libs := \
+  libcutils \
+  libvirtualtouchpad
+
+shared_libs := \
+  libbase \
+  libbinder \
+  libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src)
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+LOCAL_CPPFLAGS += -std=c++11
+LOCAL_CFLAGS += -DLOG_TAG=\"VrVirtualTouchpad\"
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE := virtual_touchpad
+LOCAL_MODULE_TAGS := optional
+LOCAL_INIT_RC := virtual_touchpad.rc
+LOCAL_MULTILIB := 64
+LOCAL_CXX_STL := libc++_static
+include $(BUILD_EXECUTABLE)
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
new file mode 100644
index 0000000..be20c6c
--- /dev/null
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -0,0 +1,311 @@
+#include "EvdevInjector.h"
+
+#include <cutils/log.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/input.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;
+}
+
+}  // 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..1b1c4da
--- /dev/null
+++ b/services/vr/virtual_touchpad/EvdevInjector.h
@@ -0,0 +1,139 @@
+#ifndef ANDROID_DVR_EVDEV_INJECTOR_H
+#define ANDROID_DVR_EVDEV_INJECTOR_H
+
+#include <android-base/unique_fd.h>
+#include <linux/uinput.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);
+
+ 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/VirtualTouchpad.cpp b/services/vr/virtual_touchpad/VirtualTouchpad.cpp
new file mode 100644
index 0000000..f3936fc
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpad.cpp
@@ -0,0 +1,120 @@
+#include "VirtualTouchpad.h"
+
+#include <android/input.h>
+#include <cutils/log.h>
+#include <inttypes.h>
+#include <linux/input.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 kDeviceName = "vr window manager virtual touchpad";
+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
+
+int VirtualTouchpad::Initialize() {
+  if (!injector_) {
+    owned_injector_.reset(new EvdevInjector());
+    injector_ = owned_injector_.get();
+  }
+  injector_->ConfigureBegin(kDeviceName, kDeviceBusType, kDeviceVendor,
+                            kDeviceProduct, kDeviceVersion);
+  injector_->ConfigureInputProperty(INPUT_PROP_DIRECT);
+  injector_->ConfigureMultiTouchXY(0, 0, kWidth - 1, kHeight - 1);
+  injector_->ConfigureAbsSlots(kSlots);
+  injector_->ConfigureKey(BTN_TOUCH);
+  injector_->ConfigureKey(BTN_BACK);
+  injector_->ConfigureEnd();
+  return injector_->GetError();
+}
+
+int VirtualTouchpad::Touch(float x, float y, float pressure) {
+  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;
+  touches_ = ((touches_ & 1) << 1) | (pressure > 0);
+  ALOGV("(%f,%f) %f -> (%" PRId32 ",%" PRId32 ") %d",
+        x, y, pressure, device_x, device_y, touches_);
+
+  if (!injector_) {
+    return EvdevInjector::ERROR_SEQUENCING;
+  }
+  injector_->ResetError();
+  switch (touches_) {
+    case 0b00:  // Hover continues.
+      if (device_x != last_device_x_ || device_y != last_device_y_) {
+        injector_->SendMultiTouchXY(0, 0, device_x, device_y);
+        injector_->SendSynReport();
+      }
+      break;
+    case 0b01:  // Touch begins.
+      // Press.
+      injector_->SendMultiTouchXY(0, 0, device_x, device_y);
+      injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_PRESS);
+      injector_->SendSynReport();
+      break;
+    case 0b10:  // Touch ends.
+      injector_->SendKey(BTN_TOUCH, EvdevInjector::KEY_RELEASE);
+      injector_->SendMultiTouchLift(0);
+      injector_->SendSynReport();
+      break;
+    case 0b11:  // Touch continues.
+      if (device_x != last_device_x_ || device_y != last_device_y_) {
+        injector_->SendMultiTouchXY(0, 0, device_x, device_y);
+        injector_->SendSynReport();
+      }
+      break;
+  }
+  last_device_x_ = device_x;
+  last_device_y_ = device_y;
+
+  return injector_->GetError();
+}
+
+int VirtualTouchpad::ButtonState(int buttons) {
+  const int changes = 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, last_motion_event_buttons_,
+        buttons);
+
+  if (!injector_) {
+    return EvdevInjector::ERROR_SEQUENCING;
+  }
+  injector_->ResetError();
+  if (changes & AMOTION_EVENT_BUTTON_BACK) {
+    injector_->SendKey(BTN_BACK,
+                       (buttons & AMOTION_EVENT_BUTTON_BACK)
+                           ? EvdevInjector::KEY_PRESS
+                           : EvdevInjector::KEY_RELEASE);
+  }
+  last_motion_event_buttons_ = buttons;
+  return injector_->GetError();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpad.h b/services/vr/virtual_touchpad/VirtualTouchpad.h
new file mode 100644
index 0000000..17aeb35
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpad.h
@@ -0,0 +1,77 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_H
+
+#include <memory>
+
+#include "EvdevInjector.h"
+
+namespace android {
+namespace dvr {
+
+class EvdevInjector;
+
+// Provides a virtual touchpad for injecting events into the input system.
+//
+class VirtualTouchpad {
+ public:
+  VirtualTouchpad() {}
+  ~VirtualTouchpad() {}
+
+  // |Intialize()| must be called once on a VirtualTouchpad before
+  // and other public method. Returns zero on success.
+  int Initialize();
+
+  // Generate a simulated touch event.
+  //
+  // @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 Zero on success.
+  //
+  int Touch(float x, float y, float pressure);
+
+  // Generate a simulated touchpad button state.
+  //
+  // @param buttons A union of MotionEvent BUTTON_* values.
+  // @returns Zero on success.
+  //
+  // Currently only BUTTON_BACK is supported, as the implementation
+  // restricts itself to operations actually required by VrWindowManager.
+  //
+  int ButtonState(int buttons);
+
+ protected:
+  // Must be called only between construction and Initialize().
+  inline void SetEvdevInjectorForTesting(EvdevInjector* injector) {
+    injector_ = injector;
+  }
+
+ private:
+  // 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_MIN;
+  int32_t last_device_y_ = INT32_MIN;
+
+  // Records current touch state (0=up 1=down) in bit 0, and previous state
+  // in bit 1, to track transitions.
+  int touches_ = 0;
+
+  // Previous injected button state, to detect changes.
+  int32_t last_motion_event_buttons_ = 0;
+
+  VirtualTouchpad(const VirtualTouchpad&) = delete;
+  void operator=(const VirtualTouchpad&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_H
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
new file mode 100644
index 0000000..5e3321f
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -0,0 +1,28 @@
+#include "VirtualTouchpadService.h"
+
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <linux/input.h>
+#include <utils/Errors.h>
+
+namespace android {
+namespace dvr {
+
+int VirtualTouchpadService::Initialize() {
+  return touchpad_.Initialize();
+}
+
+binder::Status VirtualTouchpadService::touch(float x, float y, float pressure) {
+  const int error = touchpad_.Touch(x, y, pressure);
+  return error ? binder::Status::fromServiceSpecificError(error)
+               : binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::buttonState(int buttons) {
+  const int error = touchpad_.ButtonState(buttons);
+  return error ? binder::Status::fromServiceSpecificError(error)
+               : binder::Status::ok();
+}
+
+}  // 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..e2426e3
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.h
@@ -0,0 +1,40 @@
+#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(VirtualTouchpad& touchpad)
+      : touchpad_(touchpad) {}
+
+  // Must be called before clients can connect.
+  // Returns 0 if initialization is successful.
+  int Initialize();
+
+  static char const* getServiceName() { return "virtual_touchpad"; }
+
+ protected:
+  // Implements IVirtualTouchpadService.
+  ::android::binder::Status touch(float x, float y, float pressure) override;
+  ::android::binder::Status buttonState(int buttons) override;
+
+ private:
+  VirtualTouchpad& touchpad_;
+
+  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..e048837
--- /dev/null
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
@@ -0,0 +1,23 @@
+package android.dvr;
+
+/** @hide */
+interface VirtualTouchpadService
+{
+  /**
+   * Generate a simulated touch event.
+   *
+   * @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(float x, float y, float pressure);
+
+  /**
+   * Generate a simulated touchpad button state event.
+   *
+   * @param buttons A union of MotionEvent BUTTON_* values.
+   */
+  void buttonState(int buttons);
+}
diff --git a/services/vr/virtual_touchpad/main.cpp b/services/vr/virtual_touchpad/main.cpp
new file mode 100644
index 0000000..57471c5
--- /dev/null
+++ b/services/vr/virtual_touchpad/main.cpp
@@ -0,0 +1,36 @@
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/log.h>
+
+#include "VirtualTouchpadService.h"
+
+int main() {
+  ALOGI("Starting");
+  android::dvr::VirtualTouchpad touchpad;
+  android::dvr::VirtualTouchpadService touchpad_service(touchpad);
+  const int touchpad_status = touchpad_service.Initialize();
+  if (touchpad_status) {
+    ALOGE("virtual touchpad initialization failed: %d", touchpad_status);
+    exit(1);
+  }
+
+  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.getServiceName()),
+                     &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..256c6bc
--- /dev/null
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -0,0 +1,286 @@
+#include <android/input.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <gtest/gtest.h>
+#include <linux/input.h>
+
+#include "EvdevInjector.h"
+#include "VirtualTouchpad.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class UInputForTesting : public EvdevInjector::UInput {
+ public:
+  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() {}
+  virtual ~UInputRecorder() {}
+
+  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(UInput& uinput) {
+    SetUInputForTesting(&uinput);
+  }
+  const uinput_user_dev* GetUiDev() const { return GetUiDevForTesting(); }
+};
+
+class VirtualTouchpadForTesting : public VirtualTouchpad {
+ public:
+  VirtualTouchpadForTesting(EvdevInjector& injector) {
+    SetEvdevInjectorForTesting(&injector);
+  }
+};
+
+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) {
+  UInputRecorder expect;
+  UInputRecorder record;
+  EvdevInjectorForTesting injector(record);
+  VirtualTouchpadForTesting touchpad(injector);
+
+  const int initialization_status = touchpad.Initialize();
+  EXPECT_EQ(0, initialization_status);
+
+  // Check some aspects of uinput_user_dev.
+  const uinput_user_dev* uidev = injector.GetUiDev();
+  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];
+
+  // Check the system calls performed by initialization.
+  // 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);
+  // From ConfigureEnd():
+  expect.Write(uidev, sizeof (uinput_user_dev));
+  expect.IoctlVoid(UI_DEV_CREATE);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  int touch_status = touchpad.Touch(0, 0, 0);
+  EXPECT_EQ(0, touch_status);
+  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);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.Touch(0.25f, 0.75f, 0.5f);
+  EXPECT_EQ(0, touch_status);
+  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);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.Touch(1.0f, 1.0f, 1.0f);
+  EXPECT_EQ(0, touch_status);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_TRACKING_ID, 0);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_X, width);
+  expect.WriteInputEvent(EV_ABS, ABS_MT_POSITION_Y, height);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.Touch(0.25f, 0.75f, -0.01f);
+  EXPECT_EQ(0, touch_status);
+  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);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.ButtonState(AMOTION_EVENT_BUTTON_BACK);
+  EXPECT_EQ(0, touch_status);
+  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_PRESS);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.ButtonState(AMOTION_EVENT_BUTTON_BACK);
+  EXPECT_EQ(0, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.ButtonState(0);
+  EXPECT_EQ(0, touch_status);
+  expect.WriteInputEvent(EV_KEY, BTN_BACK, EvdevInjector::KEY_RELEASE);
+  expect.WriteInputEvent(EV_SYN, SYN_REPORT, 0);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+}
+
+TEST_F(VirtualTouchpadTest, Badness) {
+  UInputRecorder expect;
+  UInputRecorder record;
+  EvdevInjectorForTesting injector(record);
+  VirtualTouchpadForTesting touchpad(injector);
+
+  // Touch before initialization should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  int touch_status = touchpad.Touch(0.25f, 0.75f, -0.01f);
+  EXPECT_NE(0, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Button change before initialization should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.ButtonState(AMOTION_EVENT_BUTTON_BACK);
+  EXPECT_NE(0, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  expect.Reset();
+  record.Reset();
+  touchpad.Initialize();
+
+  // Repeated initialization should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  const int initialization_status = touchpad.Initialize();
+  EXPECT_NE(0, initialization_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Touch off-screen should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad.Touch(-0.25f, 0.75f, 1.0f);
+  EXPECT_NE(0, touch_status);
+  touch_status = touchpad.Touch(0.25f, -0.75f, 1.0f);
+  EXPECT_NE(0, touch_status);
+  touch_status = touchpad.Touch(1.25f, 0.75f, 1.0f);
+  EXPECT_NE(0, touch_status);
+  touch_status = touchpad.Touch(0.25f, 1.75f, 1.0f);
+  EXPECT_NE(0, 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(AMOTION_EVENT_BUTTON_FORWARD);
+  EXPECT_NE(0, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+}
+
+}  // 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..b4f9f00
--- /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
+  cpuset /system
diff --git a/services/vr/vr_manager/Android.mk b/services/vr/vr_manager/Android.mk
new file mode 100644
index 0000000..54b1c1a
--- /dev/null
+++ b/services/vr/vr_manager/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+
+src_files := \
+  vr_manager.cpp \
+
+inc_files := \
+  frameworks/native/include/vr/vr_manager
+
+static_libs := \
+  libutils \
+  libbinder \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src_files)
+LOCAL_C_INCLUDES := $(inc_files)
+LOCAL_CFLAGS += -Wall
+LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Wunused
+LOCAL_CFLAGS += -Wunreachable-code
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(inc_files)
+#LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_MODULE := libvr_manager
+include $(BUILD_STATIC_LIBRARY)
diff --git a/services/vr/vr_manager/vr_manager.cpp b/services/vr/vr_manager/vr_manager.cpp
new file mode 100644
index 0000000..a31fcb7
--- /dev/null
+++ b/services/vr/vr_manager/vr_manager.cpp
@@ -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.
+ */
+
+#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 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);
+  }
+
+  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");
+
+class BpVrDisplayStateService : public BpInterface<IVrDisplayStateService> {
+ public:
+  explicit BpVrDisplayStateService(const sp<IBinder>& impl)
+      : BpInterface<IVrDisplayStateService>(impl) {}
+
+  void displayAvailable(bool available) {
+    Parcel data, reply;
+    data.writeInterfaceToken(IVrDisplayStateService::getInterfaceDescriptor());
+    data.writeBool(available);
+    remote()->transact(static_cast<uint32_t>(
+                           VrDisplayStateTransaction::ON_DISPLAY_STATE_CHANGED),
+                       data, &reply);
+  }
+};
+
+status_t BnVrDisplayStateService::onTransact(uint32_t code, const Parcel& data,
+                                             Parcel* reply, uint32_t flags) {
+  switch (static_cast<VrDisplayStateTransaction>(code)) {
+    case VrDisplayStateTransaction::ON_DISPLAY_STATE_CHANGED:
+      CHECK_INTERFACE(IVrDisplayStateService, data, reply);
+      displayAvailable(data.readBool());
+      return OK;
+  }
+  return BBinder::onTransact(code, data, reply, flags);
+}
+
+IMPLEMENT_META_INTERFACE(VrDisplayStateService,
+                         "android.service.vr.IVrDisplayStateService");
+
+}  // namespace android
diff --git a/services/vr/vr_window_manager/Android.mk_disable b/services/vr/vr_window_manager/Android.mk_disable
new file mode 100644
index 0000000..d7d98b3
--- /dev/null
+++ b/services/vr/vr_window_manager/Android.mk_disable
@@ -0,0 +1,129 @@
+# 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)
+
+native_src := \
+  application.cpp \
+  controller_mesh.cpp \
+  elbow_model.cpp \
+  hwc_callback.cpp \
+  reticle.cpp \
+  render_thread.cpp \
+  shell_view.cpp \
+  surface_flinger_view.cpp \
+  texture.cpp \
+  vr_window_manager.cpp \
+  ../virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl \
+
+src := \
+  vr_window_manager_jni.cpp \
+  application.cpp \
+  controller_mesh.cpp \
+  elbow_model.cpp \
+  hwc_callback.cpp \
+  reticle.cpp \
+  render_thread.cpp \
+  shell_view.cpp \
+  surface_flinger_view.cpp \
+  texture.cpp \
+  ../virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl \
+
+static_libs := \
+  libdisplay \
+  libbufferhub \
+  libbufferhubqueue \
+  libeds \
+  libdvrgraphics \
+  libdvrcommon \
+  libhwcomposer-client \
+  libsensor \
+  libperformance \
+  libpdx_default_transport \
+  libchrome \
+  libcutils \
+
+shared_libs := \
+  android.dvr.composer@1.0 \
+  android.hardware.graphics.composer@2.1 \
+  libvrhwc \
+  libandroid \
+  libbase \
+  libbinder \
+  libinput \
+  libhardware \
+  libsync \
+  libutils \
+  libgui \
+  libEGL \
+  libGLESv2 \
+  libvulkan \
+  libsync \
+  libui \
+  libhidlbase \
+  libhidltransport
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src)
+LOCAL_C_INCLUDES := hardware/qcom/display/msm8996/libgralloc
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_SHARED_LIBRARIES := $(shared_libs) libevent
+LOCAL_SHARED_LIBRARIES += libgvr
+LOCAL_STATIC_LIBRARIES += libgvr_ext
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DLOG_TAG=\"VrWindowManager\"
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE := libvr_window_manager_jni
+LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := 64
+LOCAL_CXX_STL := libc++_static
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(native_src)
+LOCAL_C_INCLUDES := hardware/qcom/display/msm8996/libgralloc
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+LOCAL_SHARED_LIBRARIES += libgvr
+LOCAL_STATIC_LIBRARIES += libgvr_ext
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DLOG_TAG=\"VrWindowManager\"
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE := vr_wm
+LOCAL_MODULE_TAGS := optional
+LOCAL_INIT_RC := vr_wm.rc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := VrWindowManager
+
+# We need to be priveleged to run as the system user, which is necessary for
+# getting hmd input events and doing input injection.
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+LOCAL_JNI_SHARED_LIBRARIES := libvr_window_manager_jni
+LOCAL_STATIC_JAVA_AAR_LIBRARIES := gvr_common_library_aar
+# gvr_common_library_aar depends on nano version of libprotobuf
+LOCAL_STATIC_JAVA_LIBRARIES := libprotobuf-java-nano
+# Make sure that libgvr's resources are loaded
+LOCAL_AAPT_FLAGS += --auto-add-overlay
+LOCAL_AAPT_FLAGS += --extra-packages com.google.vr.cardboard
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+include $(BUILD_PACKAGE)
diff --git a/services/vr/vr_window_manager/AndroidManifest.xml b/services/vr/vr_window_manager/AndroidManifest.xml
new file mode 100644
index 0000000..5cc4b5c
--- /dev/null
+++ b/services/vr/vr_window_manager/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.google.vr.windowmanager"
+          coreApp="true"
+          android:sharedUserId="android.uid.system"
+          android:versionCode="1"
+          android:versionName="1.0" >
+
+  <!-- The GVR SDK requires API 19+ and OpenGL ES 2+. -->
+  <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="24" />
+  <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+
+  <!-- We need the DIAGNOSTIC permission to read HMD button events. DIAGNOSTIC
+       ensures our process runs with the "input" group, so we can access
+       /dev/input. See frameworks/base/data/etc/platform.xml for the permission
+       to group mappings.
+
+       TODO(steventhomas): We shouldn't use this DIAGNOSTIC permission. Figure
+       out the correct way to get access to the HMD buttons.
+       Bug: b/33253485. -->
+  <uses-permission android:name="android.permission.DIAGNOSTIC"/>
+  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+
+  <application
+      android:label="vr_window_manager"
+      android:theme="@style/AppStyle">
+    <service android:name=".VrWindowManagerService" />
+    <receiver android:name="com.google.vr.windowmanager.BootCompletedReceiver">
+      <intent-filter>
+        <action android:name="android.intent.action.BOOT_COMPLETED" />
+      </intent-filter>
+    </receiver>
+  </application>
+</manifest>
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
new file mode 100644
index 0000000..081de74
--- /dev/null
+++ b/services/vr/vr_window_manager/application.cpp
@@ -0,0 +1,327 @@
+#include "application.h"
+
+#include <binder/IServiceManager.h>
+#include <cutils/log.h>
+#include <dvr/graphics.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <gui/ISurfaceComposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+Application::Application()
+    : controller_api_status_logged_(false),
+      controller_connection_state_logged_(false) {}
+
+Application::~Application() {
+}
+
+int Application::Initialize(JNIEnv* env, jobject app_context,
+                            jobject class_loader) {
+  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();
+
+  java_env_ = env;
+  app_context_ = app_context;
+  class_loader_ = class_loader;
+
+  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]);
+
+  if (java_env_) {
+    int ret = InitializeController();
+    if (ret)
+      return ret;
+  }
+
+  return 0;
+}
+
+int Application::InitializeController() {
+  gvr_context_ = gvr::GvrApi::Create(java_env_, app_context_, class_loader_);
+  if (gvr_context_ == nullptr) {
+    ALOGE("Gvr context creation failed");
+    return 1;
+  }
+
+  int32_t options = gvr_controller_get_default_options();
+  options |= GVR_CONTROLLER_ENABLE_GYRO | GVR_CONTROLLER_ENABLE_ACCEL;
+
+  controller_.reset(new gvr::ControllerApi);
+  if (!controller_->Init(java_env_, app_context_, class_loader_, options,
+                         gvr_context_->cobj())) {
+    ALOGE("Gvr controller init failed");
+    return 1;
+  }
+
+  controller_state_.reset(new gvr::ControllerState);
+
+  return 0;
+}
+
+void Application::DeallocateResources() {
+  gvr_context_.reset();
+  controller_.reset();
+  controller_state_.reset();
+
+  if (graphics_context_)
+    dvrGraphicsContextDestroy(graphics_context_);
+
+  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_)
+          AllocateResources();
+        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;
+  dvrGraphicsWaitNextFrame(graphics_context_, 0, &schedule);
+
+  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;
+
+    quat controller_quat(controller_orientation_.qw, controller_orientation_.qx,
+        controller_orientation_.qy, controller_orientation_.qz);
+    controller_position_ = elbow_model_.Update(
+        delta, last_pose_.GetRotation(), controller_quat, false);
+
+    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);
+
+    dvrPresent(graphics_context_);
+  }
+}
+
+void Application::ProcessControllerInput() {
+  if (!controller_)
+    return;
+
+  controller_state_->Update(*controller_);
+  gvr::ControllerApiStatus new_api_status = controller_state_->GetApiStatus();
+  gvr::ControllerConnectionState new_connection_state =
+      controller_state_->GetConnectionState();
+
+  if (!controller_api_status_logged_) {
+    controller_api_status_logged_ = true;
+    ALOGI("Controller api status: %s",
+          gvr::ControllerApi::ToString(new_api_status));
+  } else if (new_api_status != controller_api_status_) {
+    ALOGI("Controller api status changed: %s --> %s",
+          gvr::ControllerApi::ToString(controller_api_status_),
+          gvr::ControllerApi::ToString(new_api_status));
+  }
+
+  if (new_api_status == gvr::kControllerApiOk) {
+    if (!controller_connection_state_logged_) {
+      controller_connection_state_logged_ = true;
+      ALOGI("Controller connection state: %s",
+            gvr::ControllerApi::ToString(new_connection_state));
+    } else if (new_connection_state != controller_connection_state_) {
+      ALOGI("Controller connection state changed: %s --> %s",
+            gvr::ControllerApi::ToString(controller_connection_state_),
+            gvr::ControllerApi::ToString(new_connection_state));
+    }
+  } else {
+    controller_connection_state_logged_ = false;
+  }
+
+  if (new_api_status == gvr::kControllerApiOk)
+    controller_orientation_ = controller_state_->GetOrientation();
+
+  controller_api_status_ = new_api_status;
+  controller_connection_state_ = new_connection_state;
+}
+
+void Application::SetVisibility(bool visible) {
+  bool changed = is_visible_ != visible;
+  if (changed) {
+    is_visible_ = visible;
+    dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
+    if (controller_) {
+      if (is_visible_)
+        controller_->Resume();
+      else
+        controller_->Pause();
+    }
+    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();
+}
+
+}  // 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..47a0927
--- /dev/null
+++ b/services/vr/vr_window_manager/application.h
@@ -0,0 +1,103 @@
+#ifndef VR_WINDOW_MANAGER_APPLICATION_H_
+#define VR_WINDOW_MANAGER_APPLICATION_H_
+
+#include <jni.h>
+#include <memory>
+#include <private/dvr/types.h>
+#include <stdint.h>
+#include <vr/gvr/capi/include/gvr.h>
+#include <vr/gvr/capi/include/gvr_controller.h>
+
+#include <chrono>
+#include <mutex>
+
+#include "elbow_model.h"
+
+struct DvrGraphicsContext;
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class Application {
+ public:
+  Application();
+  virtual ~Application();
+
+  virtual int Initialize(JNIEnv* env, jobject app_context,
+                         jobject class_loader);
+
+  virtual int AllocateResources();
+  virtual void DeallocateResources();
+
+  void DrawFrame();
+
+ protected:
+  enum class MainThreadTask {
+    EnteringVrMode,
+    ExitingVrMode,
+    EnableDebugMode,
+    DisableDebugMode,
+    Show,
+  };
+
+  virtual void OnDrawFrame() = 0;
+  virtual void DrawEye(EyeType eye, const mat4& perspective,
+                       const mat4& eye_matrix, const mat4& head_matrix) = 0;
+
+  void SetVisibility(bool visible);
+  virtual void OnVisibilityChanged(bool visible);
+
+  void ProcessControllerInput();
+
+  void ProcessTasks(const std::vector<MainThreadTask>& tasks);
+
+  void QueueTask(MainThreadTask task);
+
+  int InitializeController();
+
+  DvrGraphicsContext* graphics_context_ = nullptr;
+  DvrPose* pose_client_ = nullptr;
+
+  Range2i eye_viewport_[2];
+  mat4 eye_from_head_[2];
+  FieldOfView fov_[2];
+  Posef last_pose_;
+
+  std::unique_ptr<gvr::GvrApi> gvr_context_;
+  std::unique_ptr<gvr::ControllerApi> controller_;
+  std::unique_ptr<gvr::ControllerState> controller_state_;
+  gvr::ControllerApiStatus controller_api_status_;
+  gvr::ControllerConnectionState controller_connection_state_;
+  gvr_quatf controller_orientation_;
+  bool controller_api_status_logged_;
+  bool controller_connection_state_logged_;
+
+  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_;
+
+  // Java Resources.
+  JNIEnv* java_env_;
+  jobject app_context_;
+  jobject class_loader_;
+
+  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/composer/1.0/Android.bp b/services/vr/vr_window_manager/composer/1.0/Android.bp
new file mode 100644
index 0000000..e3e47ff
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/1.0/Android.bp
@@ -0,0 +1,72 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+genrule {
+    name: "android.dvr.composer@1.0_genc++",
+    tools: ["hidl-gen"],
+    cmd: "$(location hidl-gen) -o $(genDir) -Lc++ -randroid.hidl:system/libhidl/transport -randroid.hardware:hardware/interfaces/ -randroid.dvr:frameworks/native/services/vr/vr_window_manager android.dvr.composer@1.0",
+    srcs: [
+        "IVrComposerClient.hal",
+        "IVrComposerView.hal",
+        "IVrComposerCallback.hal",
+    ],
+    out: [
+        "android/dvr/composer/1.0/VrComposerClientAll.cpp",
+        "android/dvr/composer/1.0/VrComposerViewAll.cpp",
+        "android/dvr/composer/1.0/VrComposerCallbackAll.cpp",
+    ],
+}
+
+genrule {
+    name: "android.dvr.composer@1.0_genc++_headers",
+    tools: ["hidl-gen"],
+    cmd: "$(location hidl-gen) -o $(genDir) -Lc++ -randroid.hidl:system/libhidl/transport -randroid.hardware:hardware/interfaces/ -randroid.dvr:frameworks/native/services/vr/vr_window_manager android.dvr.composer@1.0",
+    srcs: [
+        "IVrComposerClient.hal",
+        "IVrComposerView.hal",
+        "IVrComposerCallback.hal",
+    ],
+    out: [
+        "android/dvr/composer/1.0/IVrComposerClient.h",
+        "android/dvr/composer/1.0/IHwVrComposerClient.h",
+        "android/dvr/composer/1.0/BnVrComposerClient.h",
+        "android/dvr/composer/1.0/BpVrComposerClient.h",
+        "android/dvr/composer/1.0/BsVrComposerClient.h",
+
+        "android/dvr/composer/1.0/IVrComposerView.h",
+        "android/dvr/composer/1.0/IHwVrComposerView.h",
+        "android/dvr/composer/1.0/BnVrComposerView.h",
+        "android/dvr/composer/1.0/BpVrComposerView.h",
+        "android/dvr/composer/1.0/BsVrComposerView.h",
+
+        "android/dvr/composer/1.0/IVrComposerCallback.h",
+        "android/dvr/composer/1.0/IHwVrComposerCallback.h",
+        "android/dvr/composer/1.0/BnVrComposerCallback.h",
+        "android/dvr/composer/1.0/BpVrComposerCallback.h",
+        "android/dvr/composer/1.0/BsVrComposerCallback.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.dvr.composer@1.0",
+    generated_sources: ["android.dvr.composer@1.0_genc++"],
+    generated_headers: ["android.dvr.composer@1.0_genc++_headers"],
+    export_generated_headers: ["android.dvr.composer@1.0_genc++_headers"],
+    shared_libs: [
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+        "libcutils",
+        "android.hardware.graphics.composer@2.1",
+        "android.hidl.base@1.0",
+    ],
+    export_shared_lib_headers: [
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libutils",
+        "android.hardware.graphics.composer@2.1",
+        "android.hidl.base@1.0",
+    ],
+}
diff --git a/services/vr/vr_window_manager/composer/1.0/IVrComposerCallback.hal b/services/vr/vr_window_manager/composer/1.0/IVrComposerCallback.hal
new file mode 100644
index 0000000..6e7255e
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/1.0/IVrComposerCallback.hal
@@ -0,0 +1,18 @@
+package android.dvr.composer@1.0;
+
+import android.hardware.graphics.composer@2.1::IComposerClient;
+
+interface IVrComposerCallback {
+    struct Layer {
+        handle buffer;
+        handle fence;
+        android.hardware.graphics.composer@2.1::IComposerClient.Rect display_frame;
+        android.hardware.graphics.composer@2.1::IComposerClient.FRect crop;
+        android.hardware.graphics.composer@2.1::IComposerClient.BlendMode blend_mode;
+        float alpha;
+        uint32_t type;
+        uint32_t app_id;
+    };
+
+    onNewFrame(vec<Layer> frame);
+};
diff --git a/services/vr/vr_window_manager/composer/1.0/IVrComposerClient.hal b/services/vr/vr_window_manager/composer/1.0/IVrComposerClient.hal
new file mode 100644
index 0000000..230a68a
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/1.0/IVrComposerClient.hal
@@ -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.
+ */
+package android.dvr.composer@1.0;
+
+import android.hardware.graphics.composer@2.1::IComposerClient;
+
+interface IVrComposerClient
+    extends android.hardware.graphics.composer@2.1::IComposerClient {
+    /*
+     * Used to annotate the layer with additional information, which will be
+     * used to describe the content of the layer (ie: notification, permission,
+     * etc) which allows VR window manager to treat certain layer types
+     * specially.
+     *
+     * @param display is the display on which the layer was created.
+     * @param layer is the layer affected by the change.
+     * @param layer_type the type of the layer as described by the window
+     * manager.
+     * @param application_id the application id the layer belongs to.
+     * @return error is NONE upon success. Otherwise,
+     *         BAD_DISPLAY when an invalid display handle was passed in.
+     *         BAD_LAYER when an invalid layer handle was passed in.
+     *
+     * setLayerInfo(Display display,
+     *              Layer layer,
+     *              uint32_t layer_type,
+     *              uint32_t application_id)
+     *     generates(Error error);
+     */
+
+    enum VrCommand : int32_t {
+        OPCODE_SHIFT         = android.hardware.graphics.composer@2.1::IComposerClient.Command:OPCODE_SHIFT,
+
+        SET_LAYER_INFO = 0x800 << OPCODE_SHIFT,
+    };
+};
diff --git a/services/vr/vr_window_manager/composer/1.0/IVrComposerView.hal b/services/vr/vr_window_manager/composer/1.0/IVrComposerView.hal
new file mode 100644
index 0000000..e16131a
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/1.0/IVrComposerView.hal
@@ -0,0 +1,9 @@
+package android.dvr.composer@1.0;
+
+import IVrComposerCallback;
+
+interface IVrComposerView {
+    registerCallback(IVrComposerCallback callback);
+
+    releaseFrame();
+};
diff --git a/services/vr/vr_window_manager/composer/Android.bp b/services/vr/vr_window_manager/composer/Android.bp
new file mode 100644
index 0000000..207d456
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/Android.bp
@@ -0,0 +1,47 @@
+subdirs = [
+  "1.0",
+]
+
+//cc_library_shared {
+//  name: "libvrhwc",
+//
+//  srcs: [
+//    "impl/sync_timeline.cpp",
+//    "impl/vr_composer_view.cpp",
+//    "impl/vr_hwc.cpp",
+//    "impl/vr_composer_client.cpp",
+//  ],
+//
+//  static_libs: [
+//    "libhwcomposer-client",
+//  ],
+//
+//  shared_libs: [
+//    "android.dvr.composer@1.0",
+//    "android.hardware.graphics.composer@2.1",
+//    "libbase",
+//    "libcutils",
+//    "libfmq",
+//    "libhardware",
+//    "libhidlbase",
+//    "libhidltransport",
+//    "liblog",
+//    "libsync",
+//    "libui",
+//    "libutils",
+//  ],
+//
+//  export_include_dirs: ["."],
+//
+//  include_dirs: [
+//    // Access to software sync timeline.
+//    "system/core/libsync",
+//
+//    // Access to internal gralloc implementation.
+//    "hardware/qcom/display/msm8996/libgralloc",
+//  ],
+//
+//  cflags: [
+//    "-DLOG_TAG=\"vrhwc\"",
+//  ],
+//}
diff --git a/services/vr/vr_window_manager/composer/impl/sync_timeline.cpp b/services/vr/vr_window_manager/composer/impl/sync_timeline.cpp
new file mode 100644
index 0000000..e63ed26
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/sync_timeline.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "sync_timeline.h"
+
+#include <sys/cdefs.h>
+#include <sw_sync.h>
+#include <unistd.h>
+
+namespace android {
+namespace dvr {
+
+SyncTimeline::SyncTimeline() {}
+
+SyncTimeline::~SyncTimeline() {}
+
+bool SyncTimeline::Initialize() {
+  timeline_fd_.reset(sw_sync_timeline_create());
+  return timeline_fd_ >= 0;
+}
+
+int SyncTimeline::CreateFence(int time) {
+  return sw_sync_fence_create(timeline_fd_.get(), "dummy fence", time);
+}
+
+bool SyncTimeline::IncrementTimeline() {
+  return sw_sync_timeline_inc(timeline_fd_.get(), 1) == 0;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/composer/impl/sync_timeline.h b/services/vr/vr_window_manager/composer/impl/sync_timeline.h
new file mode 100644
index 0000000..945acbd
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/sync_timeline.h
@@ -0,0 +1,45 @@
+/*
+ * 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 VR_WINDOW_MANAGER_COMPOSER_IMPL_SYNC_TIMELINE_H_
+#define VR_WINDOW_MANAGER_COMPOSER_IMPL_SYNC_TIMELINE_H_
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dvr {
+
+// TODO(dnicoara): Remove this and move to EGL based fences.
+class SyncTimeline {
+ public:
+  SyncTimeline();
+  ~SyncTimeline();
+
+  bool Initialize();
+
+  int CreateFence(int time);
+  bool IncrementTimeline();
+
+ private:
+  base::unique_fd timeline_fd_;
+
+  SyncTimeline(const SyncTimeline&) = delete;
+  void operator=(const SyncTimeline&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_COMPOSER_IMPL_SYNC_TIMELINE_H_
diff --git a/services/vr/vr_window_manager/composer/impl/vr_composer_client.cpp b/services/vr/vr_window_manager/composer/impl/vr_composer_client.cpp
new file mode 100644
index 0000000..367acb7
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_composer_client.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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/dvr/composer/1.0/IVrComposerClient.h>
+#include <hardware/gralloc.h>
+#include <hardware/gralloc1.h>
+#include <log/log.h>
+
+#include "vr_hwc.h"
+#include "vr_composer_client.h"
+
+namespace android {
+namespace dvr {
+
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::dvr::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);
+    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;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/composer/impl/vr_composer_client.h b/services/vr/vr_window_manager/composer/impl/vr_composer_client.h
new file mode 100644
index 0000000..8f0c562
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_composer_client.h
@@ -0,0 +1,66 @@
+/*
+ * 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 VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H_
+#define VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_CLIENT_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);
+
+    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  // VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H_
diff --git a/services/vr/vr_window_manager/composer/impl/vr_composer_view.cpp b/services/vr/vr_window_manager/composer/impl/vr_composer_view.cpp
new file mode 100644
index 0000000..5f8168d
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_composer_view.cpp
@@ -0,0 +1,80 @@
+#include "vr_composer_view.h"
+
+namespace android {
+namespace dvr {
+
+VrComposerView::VrComposerView() : composer_view_(nullptr) {}
+
+VrComposerView::~VrComposerView() {
+  composer_view_->UnregisterObserver(this);
+}
+
+void VrComposerView::Initialize(ComposerView* composer_view) {
+  composer_view_ = composer_view;
+  composer_view_->RegisterObserver(this);
+}
+
+Return<void> VrComposerView::registerCallback(
+    const sp<IVrComposerCallback>& callback) {
+  callback_ = callback;
+  return Void();
+}
+
+Return<void> VrComposerView::releaseFrame() {
+  composer_view_->ReleaseFrame();
+  return Void();
+}
+
+void VrComposerView::OnNewFrame(const ComposerView::Frame& frame) {
+  if (!callback_.get()) {
+    releaseFrame();
+    return;
+  }
+
+  std::vector<IVrComposerCallback::Layer> layers;
+  std::vector<native_handle_t*> fences;
+  for (size_t i = 0; i < frame.size(); ++i) {
+    native_handle_t* fence;
+    if (frame[i].fence->isValid()) {
+      fence = native_handle_create(1, 0);
+      fence->data[0] = frame[i].fence->dup();
+    } else {
+      fence = native_handle_create(0, 0);
+    }
+    fences.push_back(fence);
+
+    layers.push_back(IVrComposerCallback::Layer{
+      .buffer = hidl_handle(frame[i].buffer->getNativeBuffer()->handle),
+      .fence = hidl_handle(fence),
+      .display_frame = frame[i].display_frame,
+      .crop = frame[i].crop,
+      .blend_mode= frame[i].blend_mode,
+      .alpha = frame[i].alpha,
+      .type = frame[i].type,
+      .app_id = frame[i].app_id,
+    });
+  }
+
+  auto status =
+      callback_->onNewFrame(hidl_vec<IVrComposerCallback::Layer>(layers));
+  if (!status.isOk()) {
+    ALOGE("Failed to send onNewFrame: %s", status.description().c_str());
+    releaseFrame();
+  }
+
+  for (size_t i = 0; i < fences.size(); ++i) {
+    native_handle_close(fences[i]);
+    native_handle_delete(fences[i]);
+  }
+}
+
+VrComposerView* GetVrComposerViewFromIVrComposerView(IVrComposerView* view) {
+  return static_cast<VrComposerView*>(view);
+}
+
+IVrComposerView* HIDL_FETCH_IVrComposerView(const char* name) {
+  return new VrComposerView();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/composer/impl/vr_composer_view.h b/services/vr/vr_window_manager/composer/impl/vr_composer_view.h
new file mode 100644
index 0000000..133bbc8
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_composer_view.h
@@ -0,0 +1,42 @@
+#ifndef VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_VIEW_H_
+#define VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_VIEW_H_
+
+#include <android/dvr/composer/1.0/IVrComposerCallback.h>
+#include <android/dvr/composer/1.0/IVrComposerView.h>
+
+#include "vr_hwc.h"
+
+namespace android {
+namespace dvr {
+
+using composer::V1_0::IVrComposerView;
+using composer::V1_0::IVrComposerCallback;
+
+class VrComposerView : public IVrComposerView, public ComposerView::Observer {
+ public:
+  VrComposerView();
+  ~VrComposerView() override;
+
+  void Initialize(ComposerView* composer_view);
+
+  // IVrComposerView
+  Return<void> registerCallback(const sp<IVrComposerCallback>& callback)
+      override;
+  Return<void> releaseFrame() override;
+
+  // ComposerView::Observer
+  void OnNewFrame(const ComposerView::Frame& frame) override;
+
+ private:
+  ComposerView* composer_view_;
+  sp<IVrComposerCallback> callback_;
+};
+
+VrComposerView* GetVrComposerViewFromIVrComposerView(IVrComposerView* view);
+
+IVrComposerView* HIDL_FETCH_IVrComposerView(const char* name);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_VIEW_H_
diff --git a/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
new file mode 100644
index 0000000..53c7d8e
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
@@ -0,0 +1,642 @@
+/*
+ * 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 "vr_hwc.h"
+
+#include <gralloc_priv.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include <mutex>
+
+#include "sync_timeline.h"
+#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> GetBufferFromHandle(const native_handle_t* handle) {
+  // TODO(dnicoara): Fix this once gralloc1 is available.
+  private_handle_t* private_handle = private_handle_t::dynamicCast(handle);
+  sp<GraphicBuffer> buffer = new GraphicBuffer(
+      private_handle->width, private_handle->height, private_handle->format, 1,
+      GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE,
+      private_handle->width, native_handle_clone(handle), true);
+  if (GraphicBufferMapper::get().registerBuffer(buffer.get()) != OK) {
+    ALOGE("Failed to register buffer");
+    return nullptr;
+  }
+
+  return buffer;
+}
+
+}  // namespace
+
+HwcDisplay::HwcDisplay() {}
+
+HwcDisplay::~HwcDisplay() {}
+
+bool HwcDisplay::Initialize() { return hwc_timeline_.Initialize(); }
+
+bool HwcDisplay::SetClientTarget(const native_handle_t* handle,
+                                 base::unique_fd fence) {
+  if (handle)
+    buffer_ = GetBufferFromHandle(handle);
+
+  fence_ = new Fence(fence.release());
+  return true;
+}
+
+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].id == id) return &layers_[i];
+
+  return nullptr;
+}
+
+bool HwcDisplay::DestroyLayer(Layer id) {
+  for (auto it = layers_.begin(); it != layers_.end(); ++it) {
+    if (it->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.z_order < rhs.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].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].id);
+      types->push_back(IComposerClient::Composition::DEVICE);
+      layers_[i].composition_type = IComposerClient::Composition::DEVICE;
+    }
+  }
+}
+
+std::vector<ComposerView::ComposerLayer> HwcDisplay::GetFrame() {
+  // Increment the time the fence is signalled every time we get the
+  // presentation frame. This ensures that calling ReleaseFrame() only affects
+  // the current frame.
+  fence_time_++;
+
+  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) {
+        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 {
+      frame.push_back(layer.info);
+    }
+  }
+
+  return frame;
+}
+
+void HwcDisplay::GetReleaseFences(int* present_fence,
+                                  std::vector<Layer>* layer_ids,
+                                  std::vector<int>* fences) {
+  *present_fence = hwc_timeline_.CreateFence(fence_time_);
+  for (const auto& layer : layers_) {
+    layer_ids->push_back(layer.id);
+    fences->push_back(hwc_timeline_.CreateFence(fence_time_));
+  }
+}
+
+void HwcDisplay::ReleaseFrame() {
+  hwc_timeline_.IncrementTimeline();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VrHwcClient
+
+VrHwc::VrHwc() {}
+
+VrHwc::~VrHwc() {}
+
+bool VrHwc::Initialize() { return display_.Initialize(); }
+
+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) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (enable && client_ != nullptr) {
+    client_.promote()->onHotplug(kDefaultDisplayId,
+                                 IComposerCallback::Connection::CONNECTED);
+  }
+}
+
+uint32_t VrHwc::getMaxVirtualDisplayCount() { return 0; }
+
+Error VrHwc::createVirtualDisplay(uint32_t width, uint32_t height,
+                                  PixelFormat* format, Display* outDisplay) {
+  *format = PixelFormat::RGBA_8888;
+  *outDisplay = 0;
+  return Error::NONE;
+}
+
+Error VrHwc::destroyVirtualDisplay(Display display) { return Error::NONE; }
+
+Error VrHwc::createLayer(Display display, Layer* outLayer) {
+  if (display != kDefaultDisplayId) {
+    return Error::BAD_DISPLAY;
+  }
+
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  HwcLayer* layer = display_.CreateLayer();
+  *outLayer = layer->id;
+  return Error::NONE;
+}
+
+Error VrHwc::destroyLayer(Display display, Layer layer) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  return display_.DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
+}
+
+Error VrHwc::getActiveConfig(Display display, Config* outConfig) {
+  if (display != kDefaultDisplayId) 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) {
+  if (display != kDefaultDisplayId) {
+    return Error::BAD_DISPLAY;
+  }
+
+  if (config != kDefaultConfigId) {
+    return Error::BAD_CONFIG;
+  }
+
+  switch (attribute) {
+    case IComposerClient::Attribute::WIDTH:
+      *outValue = 1920;
+      break;
+    case IComposerClient::Attribute::HEIGHT:
+      *outValue = 1080;
+      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) {
+  if (display != kDefaultDisplayId) {
+    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) {
+  if (display != kDefaultDisplayId) {
+    *outType = IComposerClient::DisplayType::INVALID;
+    return Error::BAD_DISPLAY;
+  }
+
+  *outType = IComposerClient::DisplayType::PHYSICAL;
+  return Error::NONE;
+}
+
+Error VrHwc::getDozeSupport(Display display, bool* outSupport) {
+  *outSupport = false;
+  if (display == kDefaultDisplayId)
+    return Error::NONE;
+  else
+    return Error::BAD_DISPLAY;
+}
+
+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) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  if (config != kDefaultConfigId) return Error::BAD_CONFIG;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setColorMode(Display display, ColorMode mode) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setColorTransform(Display display, const float* matrix,
+                               int32_t hint) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  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);
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  if (target == nullptr) return Error::NONE;
+
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  if (!display_.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);
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  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) {
+  if (display != kDefaultDisplayId) {
+    return Error::BAD_DISPLAY;
+  }
+
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  display_.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;
+  if (display != kDefaultDisplayId) {
+    return Error::BAD_DISPLAY;
+  }
+
+  std::vector<ComposerView::ComposerLayer> frame;
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+    frame = display_.GetFrame();
+    display_.GetReleaseFences(outPresentFence, outLayers, outReleaseFences);
+  }
+
+  if (observer_)
+    observer_->OnNewFrame(frame);
+  else
+    ReleaseFrame();
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
+                                    int32_t y) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerBuffer(Display display, Layer layer,
+                            buffer_handle_t buffer, int32_t acquireFence) {
+  base::unique_fd fence(acquireFence);
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_.GetLayer(layer);
+  if (!hwc_layer) return Error::BAD_LAYER;
+
+  hwc_layer->info.buffer = GetBufferFromHandle(buffer);
+  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) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_.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) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerCompositionType(Display display, Layer layer,
+                                     int32_t type) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_.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) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerDisplayFrame(Display display, Layer layer,
+                                  const hwc_rect_t& frame) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_.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) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_.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) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerSourceCrop(Display display, Layer layer,
+                                const hwc_frect_t& crop) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_.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) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerVisibleRegion(Display display, Layer layer,
+                                   const std::vector<hwc_rect_t>& visible) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_.GetLayer(layer);
+  if (!hwc_layer) return Error::BAD_LAYER;
+
+  hwc_layer->z_order = z;
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type,
+                          uint32_t appId) {
+  if (display != kDefaultDisplayId) return Error::BAD_DISPLAY;
+
+  HwcLayer* hwc_layer = display_.GetLayer(layer);
+  if (!hwc_layer) return Error::BAD_LAYER;
+
+  hwc_layer->info.type = type;
+  hwc_layer->info.app_id = appId;
+
+  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;
+}
+
+void VrHwc::ReleaseFrame() {
+  std::lock_guard<std::mutex> guard(mutex_);
+  display_.ReleaseFrame();
+}
+
+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/vr_window_manager/composer/impl/vr_hwc.h b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
new file mode 100644
index 0000000..1de056a
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
@@ -0,0 +1,275 @@
+/*
+ * 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 VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_HWC_H_
+#define VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_HWC_H_
+
+#include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <ComposerBase.h>
+#include <ui/GraphicBufferMapper.h>
+#include <utils/StrongPointer.h>
+
+#include <mutex>
+
+#include "sync_timeline.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 {
+
+class Fence;
+class GraphicBuffer;
+
+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;
+
+    // TODO(dnicoara): Add all layer properties. For now just the basics to get
+    // it going.
+    sp<GraphicBuffer> buffer;
+    sp<Fence> fence;
+    Recti display_frame;
+    Rectf crop;
+    BlendMode blend_mode;
+    float alpha;
+    uint32_t type;
+    uint32_t app_id;
+  };
+
+  using Frame = std::vector<ComposerLayer>;
+
+  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 void OnNewFrame(const Frame& frame) = 0;
+  };
+
+  virtual ~ComposerView() {}
+
+  virtual void RegisterObserver(Observer* observer) = 0;
+  virtual void UnregisterObserver(Observer* observer) = 0;
+
+  // Called to release the oldest frame received by the observer.
+  virtual void ReleaseFrame() = 0;
+};
+
+struct HwcLayer {
+  using Composition =
+      hardware::graphics::composer::V2_1::IComposerClient::Composition;
+
+  HwcLayer(Layer new_id) : id(new_id) {}
+
+  Layer id;
+  Composition composition_type;
+  uint32_t z_order;
+  ComposerView::ComposerLayer info;
+};
+
+class HwcDisplay {
+ public:
+  HwcDisplay();
+  ~HwcDisplay();
+
+  bool Initialize();
+
+  HwcLayer* CreateLayer();
+  bool DestroyLayer(Layer id);
+  HwcLayer* GetLayer(Layer id);
+
+  bool SetClientTarget(const native_handle_t* handle, base::unique_fd fence);
+
+  void GetChangedCompositionTypes(
+      std::vector<Layer>* layer_ids,
+      std::vector<IComposerClient::Composition>* composition);
+
+  std::vector<ComposerView::ComposerLayer> GetFrame();
+
+  void GetReleaseFences(int* present_fence, std::vector<Layer>* layer_ids,
+                        std::vector<int>* fences);
+
+  void ReleaseFrame();
+
+ private:
+  // The client target buffer and the associated fence.
+  // TODO(dnicoara): Replace this with a list of ComposerView::ComposerLayer.
+  sp<GraphicBuffer> buffer_;
+  sp<Fence> fence_;
+
+  // List of currently active layers.
+  std::vector<HwcLayer> layers_;
+
+  // Layer ID generator.
+  uint64_t layer_ids_ = 1;
+
+  // Creates software sync fences used to signal releasing frames.
+  SyncTimeline hwc_timeline_;
+
+  // Keeps track of the current fence time. Used in conjunction with
+  // |hwc_timeline_| to properly signal frame release times. Allows the observer
+  // to receive multiple presentation frames without calling ReleaseFrame() in
+  // between each presentation. When the observer is ready to release a frame
+  // only the oldest presentation frame is affected by the release.
+  int fence_time_ = 0;
+
+  HwcDisplay(const HwcDisplay&) = delete;
+  void operator=(const HwcDisplay&) = delete;
+};
+
+class VrHwc : public IComposer, public ComposerBase, public ComposerView {
+ public:
+  VrHwc();
+  ~VrHwc() override;
+
+  bool Initialize();
+
+  bool hasCapability(Capability capability) const;
+
+  Error setLayerInfo(Display display, Layer layer, uint32_t type,
+                     uint32_t appId);
+
+  // 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;
+  void ReleaseFrame() override;
+
+ private:
+  wp<VrComposerClient> client_;
+  sp<IComposerCallback> callbacks_;
+
+  // Guard access to internal state from binder threads.
+  std::mutex mutex_;
+
+  HwcDisplay display_;
+
+  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  // VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_HWC_H_
diff --git a/services/vr/vr_window_manager/composer_view/Android.bp_disable b/services/vr/vr_window_manager/composer_view/Android.bp_disable
new file mode 100644
index 0000000..1658154
--- /dev/null
+++ b/services/vr/vr_window_manager/composer_view/Android.bp_disable
@@ -0,0 +1,29 @@
+cc_binary {
+  name: "vr_composer_view",
+
+  srcs: ["vr_composer_view.cpp"],
+
+  static_libs: [
+    "libhwcomposer-client",
+  ],
+
+  shared_libs: [
+    "android.dvr.composer@1.0",
+    "android.hardware.graphics.composer@2.1",
+    "libbase",
+    "libbinder",
+    "libhardware",
+    "libhwbinder",
+    "liblog",
+    "libutils",
+    "libvrhwc",
+  ],
+
+  cflags: [
+    "-DLOG_TAG=\"vr_composer_view\"",
+  ],
+
+  init_rc: [
+    "vr_composer_view.rc",
+  ],
+}
diff --git a/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp b/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp
new file mode 100644
index 0000000..54dff3d
--- /dev/null
+++ b/services/vr/vr_window_manager/composer_view/vr_composer_view.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 <hwbinder/IPCThreadState.h>
+#include <impl/vr_composer_view.h>
+#include <impl/vr_hwc.h>
+
+using namespace android;
+using namespace android::dvr;
+
+int main(int, char**) {
+  android::ProcessState::self()->startThreadPool();
+
+  const char instance[] = "vr_hwcomposer";
+  sp<IComposer> service = 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");
+
+  sp<IVrComposerView> composer_view = HIDL_FETCH_IVrComposerView(
+      "DaydreamDisplay");
+  LOG_ALWAYS_FATAL_IF(!composer_view.get(),
+                      "Failed to get vr_composer_view service");
+  LOG_ALWAYS_FATAL_IF(composer_view->isRemote(),
+                      "vr_composer_view service is remote");
+
+  composer_view->registerAsService("DaydreamDisplay");
+
+  GetVrComposerViewFromIVrComposerView(composer_view.get())->Initialize(
+      GetComposerViewFromIComposer(service.get()));
+
+  android::hardware::ProcessState::self()->startThreadPool();
+  android::hardware::IPCThreadState::self()->joinThreadPool();
+
+  return 0;
+}
diff --git a/services/vr/vr_window_manager/composer_view/vr_composer_view.rc b/services/vr/vr_window_manager/composer_view/vr_composer_view.rc
new file mode 100644
index 0000000..abb5265
--- /dev/null
+++ b/services/vr/vr_window_manager/composer_view/vr_composer_view.rc
@@ -0,0 +1,5 @@
+service vr_composer_view /system/bin/vr_composer_view
+  class core
+  user system
+  group system graphics
+  cpuset /system
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/elbow_model.cpp b/services/vr/vr_window_manager/elbow_model.cpp
new file mode 100644
index 0000000..54d1eb4
--- /dev/null
+++ b/services/vr/vr_window_manager/elbow_model.cpp
@@ -0,0 +1,134 @@
+#include "elbow_model.h"
+
+#include <cutils/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..b2edc20
--- /dev/null
+++ b/services/vr/vr_window_manager/hwc_callback.cpp
@@ -0,0 +1,98 @@
+#include "hwc_callback.h"
+
+#include <gralloc_priv.h>
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <private/dvr/native_buffer.h>
+#include <sync/sync.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+sp<GraphicBuffer> GetBufferFromHandle(const native_handle_t* handle) {
+  // TODO(dnicoara): Fix this once gralloc1 is available.
+  private_handle_t* private_handle = private_handle_t::dynamicCast(handle);
+  sp<GraphicBuffer> buffer = new GraphicBuffer(
+      private_handle->width, private_handle->height, private_handle->format, 1,
+      GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE |
+      GraphicBuffer::USAGE_HW_2D | GraphicBuffer::USAGE_HW_RENDER,
+      private_handle->width, native_handle_clone(handle), true);
+  if (GraphicBufferMapper::get().registerBuffer(buffer.get()) != OK) {
+    ALOGE("Failed to register buffer");
+    return nullptr;
+  }
+
+  return buffer;
+}
+
+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
+
+HwcCallback::HwcCallback(IVrComposerView* composer_view, Client* client)
+    : composer_view_(composer_view),
+      client_(client) {
+  composer_view_->registerCallback(this);
+}
+
+HwcCallback::~HwcCallback() {
+  composer_view_->registerCallback(nullptr);
+}
+
+Return<void> HwcCallback::onNewFrame(
+    const hidl_vec<IVrComposerCallback::Layer>& frame) {
+
+  std::vector<HwcLayer> hwc_frame(frame.size());
+  for (size_t i = 0; i < frame.size(); ++i) {
+    int fence = frame[i].fence.getNativeHandle()->numFds ?
+        dup(frame[i].fence.getNativeHandle()->data[0]) : -1;
+
+    hwc_frame[i] = HwcLayer{
+      .fence = new Fence(fence),
+      .buffer = GetBufferFromHandle(frame[i].buffer.getNativeHandle()),
+      .crop = frame[i].crop,
+      .display_frame = frame[i].display_frame,
+      .blending = static_cast<int32_t>(frame[i].blend_mode),
+      .appid = frame[i].app_id,
+      .type = static_cast<HwcLayer::LayerType>(frame[i].type),
+      .alpha = frame[i].alpha,
+    };
+  }
+
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (client_)
+    client_->OnFrame(std::make_unique<Frame>(std::move(hwc_frame)));
+
+  return Void();
+}
+
+HwcCallback::Frame::Frame(std::vector<HwcLayer>&& layers)
+    : 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..05a889b
--- /dev/null
+++ b/services/vr/vr_window_manager/hwc_callback.h
@@ -0,0 +1,110 @@
+#ifndef VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+#define VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include <android/dvr/composer/1.0/IVrComposerCallback.h>
+#include <android/dvr/composer/1.0/IVrComposerView.h>
+#include <impl/vr_hwc.h>
+
+namespace android {
+
+class Fence;
+class GraphicBuffer;
+
+namespace dvr {
+
+using Recti = ComposerView::ComposerLayer::Recti;
+using Rectf = ComposerView::ComposerLayer::Rectf;
+
+using composer::V1_0::IVrComposerCallback;
+using composer::V1_0::IVrComposerView;
+
+class HwcCallback : public IVrComposerCallback {
+ public:
+  struct HwcLayer {
+    enum LayerType : uint32_t {
+      // These are from frameworks/base/core/java/android/view/WindowManager.java
+      kUndefinedWindow = 0,
+      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 kUndefinedWindow:
+        return true;
+      default:
+        return false;
+      }
+    }
+
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+    Rectf crop;
+    Recti display_frame;
+    int32_t blending;
+    uint32_t appid;
+    LayerType type;
+    float alpha;
+  };
+
+  enum class FrameStatus {
+    kUnfinished,
+    kFinished,
+    kError
+  };
+
+  class Frame {
+  public:
+    Frame(std::vector<HwcLayer>&& layers);
+
+    FrameStatus Finish();
+    const std::vector<HwcLayer>& layers() const { return layers_; }
+
+  private:
+    std::vector<HwcLayer> layers_;
+    FrameStatus status_ = FrameStatus::kUnfinished;
+  };
+
+  class Client {
+   public:
+    virtual ~Client() {}
+    virtual void OnFrame(std::unique_ptr<Frame>) = 0;
+  };
+
+  explicit HwcCallback(IVrComposerView* composer_view, Client* client);
+  ~HwcCallback() override;
+
+ private:
+  // This is the only method called on the binder thread. Everything else is
+  // called on the render thread.
+  Return<void> onNewFrame(const hidl_vec<IVrComposerCallback::Layer>& frame)
+      override;
+
+  IVrComposerView* composer_view_;
+  Client *client_;
+  std::mutex mutex_;
+
+
+  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/java/com/google/vr/windowmanager/BootCompletedReceiver.java b/services/vr/vr_window_manager/java/com/google/vr/windowmanager/BootCompletedReceiver.java
new file mode 100644
index 0000000..01d1bdb
--- /dev/null
+++ b/services/vr/vr_window_manager/java/com/google/vr/windowmanager/BootCompletedReceiver.java
@@ -0,0 +1,17 @@
+package com.google.vr.windowmanager;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class BootCompletedReceiver extends BroadcastReceiver {
+  private static final String TAG = BootCompletedReceiver.class.getSimpleName();
+
+  @Override
+  public void onReceive(Context context, Intent intent) {
+    Log.i(TAG, "Starting VRWindowManager");
+    Intent vrWindowManagerIntent = new Intent(context, VrWindowManagerService.class);
+    context.startService(vrWindowManagerIntent);
+  }
+}
diff --git a/services/vr/vr_window_manager/java/com/google/vr/windowmanager/VrWindowManagerService.java b/services/vr/vr_window_manager/java/com/google/vr/windowmanager/VrWindowManagerService.java
new file mode 100644
index 0000000..1d815ca
--- /dev/null
+++ b/services/vr/vr_window_manager/java/com/google/vr/windowmanager/VrWindowManagerService.java
@@ -0,0 +1,87 @@
+package com.google.vr.windowmanager;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+public class VrWindowManagerService extends Service {
+  private static final String TAG = VrWindowManagerService.class.getSimpleName();
+  private long nativeVrWindowManager;
+
+  // This is a temporary debugging tool for development only.
+  // It allows us to show VrWindowManager in debug mode via command line.
+  private final BroadcastReceiver debugReceiver = new BroadcastReceiver() {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+      String action = intent.getAction();
+      if (action.equals("com.google.vr.windowmanager.intent.SHOW")) {
+        nativeEnableDebug(nativeVrWindowManager);
+      } else if (action.equals("com.google.vr.windowmanager.intent.HIDE")) {
+        nativeDisableDebug(nativeVrWindowManager);
+      } else if (action.equals("com.google.vr.windowmanager.intent.ENTER_VR")) {
+        nativeEnterVrMode(nativeVrWindowManager);
+      } else if (action.equals("com.google.vr.windowmanager.intent.EXIT_VR")) {
+        nativeExitVrMode(nativeVrWindowManager);
+      }
+    }
+  };
+
+  static {
+    System.loadLibrary("vr_window_manager_jni");
+  }
+
+  @Override
+  public void onCreate() {
+    super.onCreate();
+    destroyRenderer();
+    nativeVrWindowManager = nativeCreate(getClass().getClassLoader(), getApplicationContext());
+    if (nativeVrWindowManager == 0) {
+      Log.e(TAG, "Failed to create native renderer");
+    }
+
+    // For development, testing and debugging.
+    IntentFilter filter = new IntentFilter();
+    filter.addAction("com.google.vr.windowmanager.intent.SHOW");
+    filter.addAction("com.google.vr.windowmanager.intent.HIDE");
+    filter.addAction("com.google.vr.windowmanager.intent.ENTER_VR");
+    filter.addAction("com.google.vr.windowmanager.intent.EXIT_VR");
+    registerReceiver(debugReceiver, filter);
+  }
+
+  @Override
+  public int onStartCommand(Intent intent, int flags, int startId) {
+    return START_STICKY;
+  }
+
+  @Override
+  public IBinder onBind(Intent intent) {
+    Log.i(TAG, "Ignoring bind request");
+    return null;
+  }
+
+  @Override
+  public void onDestroy() {
+    super.onDestroy();
+    unregisterReceiver(debugReceiver);
+    destroyRenderer();
+  }
+
+  private void destroyRenderer() {
+    if (nativeVrWindowManager != 0) {
+      nativeDestroy(nativeVrWindowManager);
+      nativeVrWindowManager = 0;
+    }
+  }
+
+  private native long nativeCreate(ClassLoader appClassLoader, Context context);
+  private native void nativeDestroy(long nativeVrWindowManager);
+  private native void nativeEnableDebug(long nativeVrWindowManager);
+  private native void nativeDisableDebug(long nativeVrWindowManager);
+  private native void nativeEnterVrMode(long nativeVrWindowManager);
+  private native void nativeExitVrMode(long nativeVrWindowManager);
+}
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/render_thread.cpp b/services/vr/vr_window_manager/render_thread.cpp
new file mode 100644
index 0000000..00e3161
--- /dev/null
+++ b/services/vr/vr_window_manager/render_thread.cpp
@@ -0,0 +1,92 @@
+#include <cutils/log.h>
+#include <future>
+#include <jni.h>
+
+#include "render_thread.h"
+#include "shell_view.h"
+
+namespace android {
+namespace dvr {
+
+RenderThread::RenderThread(JNIEnv* env, jobject class_loader,
+                           jobject android_context)
+    : jvm_(nullptr),
+      class_loader_global_ref_(0),
+      android_context_global_ref_(0),
+      quit_(false) {
+  env->GetJavaVM(&jvm_);
+
+  // Create global references so we can access these objects on the render
+  // thread
+  class_loader_global_ref_ = env->NewGlobalRef(class_loader);
+  android_context_global_ref_ = env->NewGlobalRef(android_context);
+
+  std::promise<int> render_thread_init_result_promise;
+  thread_ = std::thread([this, &render_thread_init_result_promise] {
+    JNIEnv* render_thread_jni_env = nullptr;
+    jvm_->AttachCurrentThread(&render_thread_jni_env, nullptr);
+    RunRenderLoop(&render_thread_init_result_promise);
+    jvm_->DetachCurrentThread();
+  });
+
+  // Wait to see if the render thread started successfully. If not bail.
+  int render_thread_init_result =
+      render_thread_init_result_promise.get_future().get();
+  LOG_ALWAYS_FATAL_IF(render_thread_init_result != 0,
+                      "Failed initializing render thread. result=%d",
+                      render_thread_init_result);
+}
+
+RenderThread::~RenderThread() { Quit(); }
+
+void RenderThread::Quit() {
+  if (thread_.joinable()) {
+    quit_ = true;
+    thread_.join();
+  }
+
+  JNIEnv* env = GetJniEnv();
+  if (class_loader_global_ref_ != 0) {
+    env->DeleteGlobalRef(class_loader_global_ref_);
+    class_loader_global_ref_ = 0;
+  }
+  if (android_context_global_ref_ != 0) {
+    env->DeleteGlobalRef(android_context_global_ref_);
+    android_context_global_ref_ = 0;
+  }
+}
+
+void RenderThread::EnableDebug(bool debug) { shell_view_.EnableDebug(debug); }
+
+void RenderThread::VrMode(bool mode) { shell_view_.VrMode(mode); }
+
+JNIEnv* RenderThread::GetJniEnv() {
+  JNIEnv* env;
+  jvm_->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+  return env;
+}
+
+void RenderThread::RunRenderLoop(
+    std::promise<int>* init_result_promise) {
+  // TODO(steventhomas): Create local refs to work around b/33251144. Remove
+  // once that bug is fixed.
+  JNIEnv* env = GetJniEnv();
+  jobject class_loader = env->NewLocalRef(class_loader_global_ref_);
+  jobject android_context = env->NewLocalRef(android_context_global_ref_);
+
+  int init_result = shell_view_.Initialize(env, android_context, class_loader);
+  init_result += shell_view_.AllocateResources();
+  init_result_promise->set_value(init_result);
+  if (init_result == 0) {
+    while (!quit_)
+      shell_view_.DrawFrame();
+  } else {
+    ALOGE("Failed to initialize ShellView");
+  }
+
+  env->DeleteLocalRef(class_loader);
+  env->DeleteLocalRef(android_context);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/render_thread.h b/services/vr/vr_window_manager/render_thread.h
new file mode 100644
index 0000000..e193643
--- /dev/null
+++ b/services/vr/vr_window_manager/render_thread.h
@@ -0,0 +1,47 @@
+#ifndef VR_WINDOW_MANAGER_RENDER_THREAD_H_
+#define VR_WINDOW_MANAGER_RENDER_THREAD_H_
+
+#include <atomic>
+#include <future>
+#include <jni.h>
+#include <thread>
+
+#include "shell_view.h"
+
+namespace android {
+namespace dvr {
+
+class RenderThread {
+ public:
+  RenderThread(JNIEnv* env, jobject class_loader, jobject android_context);
+  ~RenderThread();
+  void Quit();
+  void EnableDebug(bool debug);
+  void VrMode(bool mode);
+
+  RenderThread(const RenderThread&) = delete;
+  void operator=(const RenderThread&) = delete;
+
+ private:
+  // Called by both the main thread and render thread. Will return the correct
+  // JNIEnv for the current thread.
+  JNIEnv* GetJniEnv();
+
+  void RunRenderLoop(std::promise<int>* init_result_promise);
+
+  // Accessed only by the main thread.
+  std::thread thread_;
+
+  // The vars below are accessed by both the main thread and the render thread.
+  JavaVM* jvm_;
+  jobject class_loader_global_ref_;
+  jobject android_context_global_ref_;
+  std::atomic_bool quit_;
+
+  ShellView shell_view_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_RENDER_THREAD_H_
diff --git a/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon.png b/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon.png
new file mode 100644
index 0000000..06f896d
--- /dev/null
+++ b/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon.png
Binary files differ
diff --git a/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon_background.png b/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon_background.png
new file mode 100644
index 0000000..d336da3
--- /dev/null
+++ b/services/vr/vr_window_manager/res/drawable-nodpi/vr_icon_background.png
Binary files differ
diff --git a/services/vr/vr_window_manager/res/values/styles.xml b/services/vr/vr_window_manager/res/values/styles.xml
new file mode 100644
index 0000000..8a1a74b
--- /dev/null
+++ b/services/vr/vr_window_manager/res/values/styles.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+<add-resource type="style" name="AppStyle"></add-resource>
+<style name="AppStyle"
+    parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
+  <item name="android:windowDisablePreview">true</item>
+</style>
+</resources>
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..3b18f74
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -0,0 +1,706 @@
+#include "shell_view.h"
+
+#include <android/input.h>
+#include <binder/IServiceManager.h>
+#include <cutils/log.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <hardware/hwcomposer2.h>
+
+#include "controller_mesh.h"
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr unsigned int kVRAppLayerCount = 2;
+
+constexpr unsigned int kMaximumPendingFrames = 8;
+
+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); }
+});
+
+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,
+};
+
+// 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;
+
+  return mat4(Eigen::Scaling<float>(xscale, yscale, 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;
+}
+
+// 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;
+}
+
+vec3 FromGvrVec3f(const gvr_vec3f& vec3f) {
+  return vec3(vec3f.x, vec3f.y, vec3f.z);
+}
+
+quat FromGvrQuatf(const gvr_quatf& quaternion) {
+  return quat(quaternion.qw, quaternion.qx, quaternion.qy, quaternion.qz);
+}
+
+// Determine if ths frame should be shown or hidden.
+bool CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
+                                        uint32_t vr_app) {
+  auto& layers = frame.layers();
+
+  // We assume the first two layers are the VR app.
+  if (layers.size() < kVRAppLayerCount)
+    return false;
+
+  if (vr_app != layers[0].appid || layers[0].appid == 0)
+    return false;
+
+  // If a non-VR-app, non-skipped layer appears, show.
+  size_t index = kVRAppLayerCount;
+  // 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 == 0) {
+      index = i + 1;
+      break;
+    }
+  }
+
+  // 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 true;
+  }
+  return false;
+}
+
+}  // namespace
+
+ShellView::ShellView() {
+  ime_translate_ = mat4(Eigen::Translation3f(0.0f, -0.5f, 0.25f));
+  ime_top_left_ = vec2(0, 0);
+  ime_size_ = vec2(0, 0);
+}
+
+ShellView::~ShellView() {}
+
+int ShellView::Initialize(JNIEnv* env, jobject app_context,
+                          jobject class_loader) {
+  int ret = Application::Initialize(env, app_context, class_loader);
+  if (ret)
+    return ret;
+
+  translate_ = Eigen::Translation3f(0, 0, -2.5f);
+
+  if (!InitializeTouch())
+    ALOGE("Failed to initialize virtual touchpad");
+
+  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;
+
+  surface_flinger_view_.reset(new SurfaceFlingerView);
+  if (!surface_flinger_view_->Initialize(this))
+    return 1;
+
+  reticle_.reset(new Reticle());
+  if (!reticle_->Initialize())
+    return 1;
+
+  controller_mesh_.reset(new Mesh<vec3, vec3, vec2>());
+  controller_mesh_->SetVertices(kNumControllerMeshVertices,
+                                kControllerMeshVertices);
+
+  initialized_ = true;
+
+  return 0;
+}
+
+void ShellView::DeallocateResources() {
+  surface_flinger_view_.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::OnDrawFrame() {
+  textures_.clear();
+  has_ime_ = false;
+
+  {
+    std::unique_lock<std::mutex> l(pending_frame_mutex_);
+    if (!pending_frames_.empty()) {
+      // Check if we should advance the frame.
+      auto& frame = pending_frames_.front();
+      if (!frame.visibility ||
+          frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
+        current_frame_ = std::move(frame);
+        pending_frames_.pop_front();
+      }
+    }
+  }
+
+  if (!debug_mode_ && current_frame_.visibility != is_visible_) {
+    SetVisibility(current_frame_.visibility);
+  }
+
+  if (!current_frame_.visibility)
+    return;
+
+  ime_texture_ = TextureLayer();
+
+  surface_flinger_view_->GetTextures(*current_frame_.frame.get(), &textures_,
+                                     &ime_texture_, debug_mode_);
+  has_ime_ = ime_texture_.texture != nullptr;
+}
+
+void ShellView::DrawEye(EyeType /* eye */, const mat4& perspective,
+                        const mat4& eye_matrix, const mat4& head_matrix) {
+  if (should_recenter_) {
+    // Position the quad horizontally aligned in the direction the user
+    // is facing, effectively taking out head roll.
+    initial_head_matrix_ = GetHorizontallyAlignedMatrixFromPose(last_pose_);
+    should_recenter_ = false;
+  }
+
+  size_ = vec2(surface_flinger_view_->width(), surface_flinger_view_->height());
+  scale_ = GetScalingMatrix(size_.x(), size_.y());
+
+  DrawOverlays(perspective, eye_matrix, head_matrix);
+
+  // TODO(alexst): Replicate controller rendering from VR Home.
+  // Current approach in the function below is a quick visualization.
+  DrawController(perspective, eye_matrix, head_matrix);
+
+  // TODO: Make sure reticle is shown only over visible overlays.
+  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_ && allow_input_) {
+      is_touching_ = true;
+    }
+  } else {
+    is_touching_ = false;
+  }
+  Touch();
+  return true;
+}
+
+void ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
+  if (!frame || frame->layers().empty())
+    return;
+
+  bool visibility = debug_mode_ || CalculateVisibilityFromLayerConfig(
+                                       *frame.get(), current_vr_app_);
+  current_vr_app_ = frame->layers().front().appid;
+
+  // If we are not showing the frame there's no need to keep anything around.
+  if (!visibility) {
+    // Hidden, no change so drop it completely
+    if (!current_frame_.visibility)
+      return;
+
+    frame.reset(nullptr);
+  }
+
+  std::unique_lock<std::mutex> l(pending_frame_mutex_);
+
+  pending_frames_.emplace_back(std::move(frame), visibility);
+
+  if (pending_frames_.size() > kMaximumPendingFrames)
+    pending_frames_.pop_front();
+
+  // If we are showing ourselves the main thread is not processing anything,
+  // so give it a kick.
+  if (visibility && !current_frame_.visibility)
+    QueueTask(MainThreadTask::Show);
+}
+
+bool ShellView::IsHit(const vec3& view_location, const vec3& view_direction,
+                      vec3* hit_location, vec2* hit_location_in_window_coord,
+                      bool test_ime) {
+  mat4 m = initial_head_matrix_ * translate_;
+  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 ShellView::DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+                             const mat4& head_matrix) {
+  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 = initial_head_matrix_ * translate_ * 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 ShellView::DrawIme() {
+  program_->Use();
+  glBindTexture(GL_TEXTURE_2D, ime_texture_.texture->id());
+
+  mat4 layer_transform = GetLayerTransform(ime_texture_, size_.x(), size_.y());
+
+  mat4 transform = initial_head_matrix_ * translate_ * ime_translate_ * scale_ *
+              layer_transform;
+
+  DrawWithTransform(transform, *program_);
+}
+
+void ShellView::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 =
+      initial_head_matrix_ * translate_ * scale_ * layer_transform;
+  DrawWithTransform(transform, *overlay_program_);
+  glDisable(GL_BLEND);
+}
+
+void ShellView::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 ShellView::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;
+}
+
+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 (controller_ && controller_api_status_ == gvr::kControllerApiOk) {
+    view_quaternion = FromGvrQuatf(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 (controller_state_->GetButtonDown(gvr::kControllerButtonClick))
+      OnClick(true);
+
+    if (controller_state_->GetButtonUp(gvr::kControllerButtonClick))
+      OnClick(false);
+
+    if (controller_state_->GetButtonDown(gvr::kControllerButtonApp))
+      OnTouchpadButton(true, AMOTION_EVENT_BUTTON_BACK);
+
+    if (controller_state_->GetButtonUp(gvr::kControllerButtonApp))
+      OnTouchpadButton(false, AMOTION_EVENT_BUTTON_BACK);
+  }
+
+  vec3 view_direction = vec3(view_quaternion * vec3(0, 0, -1));
+
+  vec3 hit_location;
+
+  bool is_hit;
+  if(has_ime_) {
+    // This will set allow_input_ and hit_location_in_window_coord_.
+    is_hit = IsImeHit(pointer_location, view_direction, &hit_location);
+  } else {
+    is_hit = IsHit(pointer_location, view_direction, &hit_location,
+                   &hit_location_in_window_coord_, false);
+    allow_input_ = is_hit;
+  }
+
+  if (is_hit) {
+    reticle_->ShowAt(
+        Eigen::Translation3f(hit_location) * view_quaternion.matrix(),
+        allow_input_ ? vec3(1, 0, 0) : vec3(0, 0, 0));
+    Touch();
+  }
+
+  reticle_->Draw(perspective, eye_matrix, head_matrix);
+}
+
+void ShellView::DrawController(const mat4& perspective, const mat4& eye_matrix,
+                               const mat4& head_matrix) {
+  if (!controller_)
+    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 = FromGvrQuatf(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();
+}
+
+bool ShellView::InitializeTouch() {
+  virtual_touchpad_ =
+      android::interface_cast<android::dvr::IVirtualTouchpadService>(
+          android::defaultServiceManager()->getService(
+              android::String16("virtual_touchpad")));
+  if (!virtual_touchpad_.get()) {
+    ALOGE("Failed to connect to virtual touchpad");
+    return false;
+  }
+  return true;
+}
+
+void ShellView::Touch() {
+  if (!virtual_touchpad_.get()) {
+    ALOGE("missing virtual touchpad");
+    // Try to reconnect; useful in development.
+    if (!InitializeTouch()) {
+      return;
+    }
+  }
+
+  const android::binder::Status status = virtual_touchpad_->touch(
+      hit_location_in_window_coord_.x() / size_.x(),
+      hit_location_in_window_coord_.y() / size_.y(),
+      is_touching_ ? 1.0f : 0.0f);
+  if (!status.isOk()) {
+    ALOGE("touch failed: %s", status.toString8().string());
+  }
+}
+
+bool ShellView::OnTouchpadButton(bool down, int button) {
+  int buttons = touchpad_buttons_;
+  if (down) {
+    if (allow_input_) {
+      buttons |= button;
+    }
+  } else {
+    buttons &= ~button;
+  }
+  if (buttons == touchpad_buttons_) {
+    return true;
+  }
+  touchpad_buttons_ = buttons;
+  if (!virtual_touchpad_.get()) {
+    ALOGE("missing virtual touchpad");
+    return false;
+  }
+
+  const android::binder::Status status =
+      virtual_touchpad_->buttonState(touchpad_buttons_);
+  if (!status.isOk()) {
+    ALOGE("touchpad button failed: %d %s", touchpad_buttons_,
+          status.toString8().string());
+  }
+  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..589902e
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -0,0 +1,121 @@
+#ifndef VR_WINDOW_MANAGER_SHELL_VIEW_H_
+#define VR_WINDOW_MANAGER_SHELL_VIEW_H_
+
+#include <private/dvr/graphics/mesh.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <android/dvr/IVirtualTouchpadService.h>
+
+#include <deque>
+
+#include "application.h"
+#include "reticle.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+class ShellView : public Application, public HwcCallback::Client {
+ public:
+  ShellView();
+  virtual ~ShellView();
+
+  int Initialize(JNIEnv* env, jobject app_context,
+                 jobject class_loader) override;
+
+  int AllocateResources() override;
+  void DeallocateResources() override;
+
+  void EnableDebug(bool debug);
+  void VrMode(bool mode);
+
+ protected:
+  void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
+               const mat4& head_matrix) override;
+  void OnVisibilityChanged(bool visible) override;
+
+  void DrawOverlays(const mat4& perspective, const mat4& eye_matrix,
+                    const mat4& head_matrix);
+  void DrawReticle(const mat4& perspective, const mat4& eye_matrix,
+                   const mat4& head_matrix);
+  void DrawIme();
+  void DrawDimOverlay(const mat4& mvp, const TextureLayer& layer,
+                      const vec2& top_left, const vec2& bottom_right);
+  void DrawController(const mat4& perspective, const mat4& eye_matrix,
+                      const mat4& head_matrix);
+
+  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);
+  bool InitializeTouch();
+  void Touch();
+  bool OnTouchpadButton(bool down, int button);
+
+  void OnDrawFrame() override;
+  void DrawWithTransform(const mat4& transform, const ShaderProgram& program);
+
+  bool OnClick(bool down);
+
+  // HwcCallback::Client:
+  void OnFrame(std::unique_ptr<HwcCallback::Frame> frame) override;
+
+  std::unique_ptr<ShaderProgram> program_;
+  std::unique_ptr<ShaderProgram> overlay_program_;
+  std::unique_ptr<ShaderProgram> controller_program_;
+
+  uint32_t current_vr_app_;
+
+  // Used to center the scene when the shell becomes visible.
+  bool should_recenter_ = true;
+  mat4 initial_head_matrix_;
+  mat4 scale_;
+  mat4 translate_;
+  mat4 ime_translate_;
+  vec2 size_;
+
+  std::unique_ptr<SurfaceFlingerView> surface_flinger_view_;
+  std::unique_ptr<Reticle> reticle_;
+  sp<IVirtualTouchpadService> virtual_touchpad_;
+  std::vector<TextureLayer> textures_;
+  TextureLayer ime_texture_;
+
+  bool is_touching_ = false;
+  bool allow_input_ = false;
+  int touchpad_buttons_ = 0;
+  vec2 hit_location_in_window_coord_;
+  vec2 ime_top_left_;
+  vec2 ime_size_;
+  bool has_ime_ = false;
+
+  std::unique_ptr<Mesh<vec3, vec3, vec2>> controller_mesh_;
+
+  struct PendingFrame {
+    PendingFrame() = default;
+    PendingFrame(std::unique_ptr<HwcCallback::Frame>&& frame, bool 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;
+    bool visibility = false;
+  };
+  std::deque<PendingFrame> pending_frames_;
+  std::mutex pending_frame_mutex_;
+  PendingFrame current_frame_;
+
+  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/surface_flinger_view.cpp b/services/vr/vr_window_manager/surface_flinger_view.cpp
new file mode 100644
index 0000000..d38fcc0
--- /dev/null
+++ b/services/vr/vr_window_manager/surface_flinger_view.cpp
@@ -0,0 +1,79 @@
+#include "surface_flinger_view.h"
+
+#include <binder/IServiceManager.h>
+#include <impl/vr_composer_view.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) {
+  const char instance[] = "DaydreamDisplay";
+  composer_service_ = IVrComposerView::getService(instance);
+  if (composer_service_ == nullptr) {
+    ALOGE("Failed to initialize composer service");
+    return false;
+  }
+
+  if (!composer_service_->isRemote()) {
+    ALOGE("Composer service is not remote");
+    return false;
+  }
+
+  // TODO(dnicoara): Query this from the composer service.
+  width_ = 1920;
+  height_ = 1080;
+
+  composer_observer_.reset(new HwcCallback(composer_service_.get(), client));
+  return true;
+}
+
+bool SurfaceFlingerView::GetTextures(const HwcCallback::Frame& frame,
+                                     std::vector<TextureLayer>* texture_layers,
+                                     TextureLayer* ime_layer,
+                                     bool debug) 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) {
+    start = 1;
+    if (layers[0].appid && layers[0].appid == layers[1].appid)
+      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..e079cdb
--- /dev/null
+++ b/services/vr/vr_window_manager/surface_flinger_view.h
@@ -0,0 +1,52 @@
+#ifndef APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
+#define APPLICATIONS_EXPERIMENTS_SURFACE_FLINGER_DEMO_SURFACE_FLINGER_VIEW_H_
+
+#include <utils/StrongPointer.h>
+
+#include <memory>
+
+#include "hwc_callback.h"
+
+namespace android {
+namespace dvr {
+
+class IDisplay;
+class Texture;
+
+struct TextureLayer {
+  std::unique_ptr<Texture> texture;
+  Rectf crop;
+  Recti display_frame;
+  int32_t blending;
+  float alpha;
+};
+
+class SurfaceFlingerView {
+ public:
+  SurfaceFlingerView();
+  ~SurfaceFlingerView();
+
+  int width() const { return width_; }
+  int height() const { return height_; }
+
+  bool Initialize(HwcCallback::Client *client);
+
+  bool GetTextures(const HwcCallback::Frame& layers,
+                   std::vector<TextureLayer>* texture_layers,
+                   TextureLayer* ime_layer, bool debug) const;
+
+ private:
+  sp<IVrComposerView> composer_service_;
+  std::unique_ptr<HwcCallback> composer_observer_;
+
+  int width_ = 0;
+  int height_ = 0;
+
+  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..dbd91b7
--- /dev/null
+++ b/services/vr/vr_window_manager/texture.cpp
@@ -0,0 +1,41 @@
+#include "texture.h"
+
+#include <cutils/log.h>
+#include <GLES/glext.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..736a14f
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager.cpp
@@ -0,0 +1,23 @@
+#include <binder/ProcessState.h>
+
+#include "shell_view.h"
+
+int main(int /* argc */, char** /* argv */) {
+  android::ProcessState::self()->startThreadPool();
+
+  android::dvr::ShellView app;
+  if (app.Initialize(nullptr, nullptr, nullptr)) {
+    ALOGE("Failed to initialize");
+    return 1;
+  }
+
+  if (app.AllocateResources()) {
+    ALOGE("Failed to allocate resources");
+    return 1;
+  }
+
+  while (true)
+    app.DrawFrame();
+
+  return 0;
+}
diff --git a/services/vr/vr_window_manager/vr_window_manager_jni.cpp b/services/vr/vr_window_manager/vr_window_manager_jni.cpp
new file mode 100644
index 0000000..f52658a
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager_jni.cpp
@@ -0,0 +1,58 @@
+#include <cutils/log.h>
+#include <jni.h>
+
+#include <memory>
+
+#include "render_thread.h"
+
+#define JNI_METHOD(return_type, method_name) \
+  JNIEXPORT return_type JNICALL              \
+      Java_com_google_vr_windowmanager_VrWindowManagerService_##method_name
+
+namespace {
+
+inline jlong jptr(android::dvr::RenderThread* native_vr_window_manager) {
+  return reinterpret_cast<intptr_t>(native_vr_window_manager);
+}
+
+inline android::dvr::RenderThread* native(jlong ptr) {
+  return reinterpret_cast<android::dvr::RenderThread*>(ptr);
+}
+
+}  // namespace
+
+extern "C" {
+
+JNI_METHOD(jlong, nativeCreate)(JNIEnv* env, jclass /*clazz*/,
+                                jobject class_loader,
+                                jobject android_context) {
+  return jptr(new android::dvr::RenderThread(
+      env, class_loader, android_context));
+}
+
+JNI_METHOD(void, nativeDestroy)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+  delete native(native_render_thread);
+}
+
+JNI_METHOD(void, nativeEnableDebug)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+  native(native_render_thread)->EnableDebug(true);
+}
+
+JNI_METHOD(void, nativeDisableDebug)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+  native(native_render_thread)->EnableDebug(false);
+}
+
+JNI_METHOD(void, nativeEnterVrMode)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+  native(native_render_thread)->VrMode(true);
+}
+
+JNI_METHOD(void, nativeExitVrMode)
+(JNIEnv* /*env*/, jclass /*clazz*/, jlong native_render_thread) {
+  native(native_render_thread)->VrMode(false);
+}
+
+}  // extern "C"
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..143b916
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_wm.rc
@@ -0,0 +1,9 @@
+service vr_wm /system/bin/vr_wm
+  class core
+  user system
+  group system graphics input
+  cpuset /system
+  disabled
+
+on property:persist.daydream.vr_wm=1
+  enable vr_wm
diff --git a/vulkan/api/platform.api b/vulkan/api/platform.api
index 7aa19e7..fb8e3ce 100644
--- a/vulkan/api/platform.api
+++ b/vulkan/api/platform.api
@@ -48,3 +48,5 @@
 // VK_USE_PLATFORM_WIN32_KHR
 @internal type void* HINSTANCE
 @internal type void* HWND
+@internal type void* HANDLE
+@internal class SECURITY_ATTRIBUTES {}
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 870f8eb..5a67d36 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -28,7 +28,7 @@
 // API version (major.minor.patch)
 define VERSION_MAJOR 1
 define VERSION_MINOR 0
-define VERSION_PATCH 13
+define VERSION_PATCH 38
 
 // API limits
 define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
@@ -75,10 +75,16 @@
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_SPEC_VERSION     5
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME             "VK_KHR_win32_surface"
 
-@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     5
+@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
+@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_NAME         "VK_KHR_incremental_present"
+
+@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     6
 @extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME             "VK_ANDROID_native_buffer"
 
-@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       2
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION     1
+@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_NAME             "VK_GOOGLE_display_timing"
+
+@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       4
 @extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME               "VK_EXT_debug_report"
 
 @extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION           1
@@ -93,9 +99,61 @@
 @extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION   1
 @extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_NAME           "VK_AMD_rasterization_order"
 
+@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1
+@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax"
+
+@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1
+@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
+
 @extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_SPEC_VERSION       3
 @extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_NAME               "VK_EXT_debug_marker"
 
+@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_SPEC_VERSION 1
+@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader"
+
+@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1
+@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation"
+
+@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
+@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
+@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
+
+@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1
+@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height"
+
+@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1
+@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
+
+@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
+@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
+
+@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1
+@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
+
+@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
+
+@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1
+@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory"
+
+@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
+
+@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
+
+@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
+@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
+
+@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
+
+@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"
+
+
 
 /////////////
 //  Types  //
@@ -144,6 +202,9 @@
 
 @extension("VK_EXT_debug_report") @nonDispatchHandle type u64 VkDebugReportCallbackEXT
 
+@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX
+@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX
+
 
 /////////////
 //  Enums  //
@@ -162,6 +223,9 @@
 
     //@extension("VK_KHR_swapchain")
     VK_IMAGE_LAYOUT_PRESENT_SRC_KHR                         = 1000001002,
+
+    //@extension("VK_KHR_shared_presentable_image")
+    VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR                      = 1000111000,
 }
 
 enum VkAttachmentLoadOp {
@@ -584,6 +648,30 @@
     VK_FORMAT_ASTC_12x10_SRGB_BLOCK                         = 182,
     VK_FORMAT_ASTC_12x12_UNORM_BLOCK                        = 183,
     VK_FORMAT_ASTC_12x12_SRGB_BLOCK                         = 184,
+
+    //@extension("VK_IMG_format_pvrtc")
+    VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
+
+    //@extension("VK_IMG_format_pvrtc")
+    VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
+
+    //@extension("VK_IMG_format_pvrtc")
+    VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
+
+    //@extension("VK_IMG_format_pvrtc")
+    VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
+
+    //@extension("VK_IMG_format_pvrtc")
+    VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
+
+    //@extension("VK_IMG_format_pvrtc")
+    VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
+
+    //@extension("VK_IMG_format_pvrtc")
+    VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
+
+    //@extension("VK_IMG_format_pvrtc")
+    VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
 }
 
 /// Structure type enumerant
@@ -667,8 +755,15 @@
     //@extension("VK_KHR_win32_surface")
     VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR             = 1000009000,
 
+    //@extension("VK_KHR_incremental_present")
+    VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR                       = 1000084000,
+
     //@extension("VK_ANDROID_native_buffer")
     VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID                     = 1000010000,
+    VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID       = 1000010001,
+
+    //@extension("VK_GOOGLE_display_timing")
+    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE                 = 1000092000,
 
     //@extension("VK_EXT_debug_report")
     VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT     = 1000011000,
@@ -684,6 +779,81 @@
 
     //@extension("VK_EXT_debug_marker")
     VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT              = 1000022002,
+
+    //@extension("VK_NV_dedicated_allocation")
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
+
+    //@extension("VK_NV_dedicated_allocation")
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
+
+    //@extension("VK_NV_dedicated_allocation")
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
+
+    //@extension("VK_NV_external_memory")
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000,
+
+    //@extension("VK_NV_external_memory")
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001,
+
+    //@extension("VK_NV_external_memory_win32")
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000,
+
+    //@extension("VK_NV_external_memory_win32")
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001,
+
+    //@extension("VK_NV_win32_keyed_mutex")
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
+
+    //@extension("VK_KHR_get_physical_device_properties2")
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+
+    //@extension("VK_EXT_validation_flags")
+    VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
+
+    //@extension("VK_KHR_incremental_present")
+    VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000,
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000,
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001,
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002,
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
 }
 
 enum VkSubpassContents {
@@ -721,6 +891,7 @@
     VK_ERROR_INCOMPATIBLE_DRIVER                            = 0xFFFFFFF7, // -9
     VK_ERROR_TOO_MANY_OBJECTS                               = 0xFFFFFFF6, // -10
     VK_ERROR_FORMAT_NOT_SUPPORTED                           = 0xFFFFFFF5, // -11
+    VK_ERROR_FRAGMENTED_POOL                                = 0xFFFFFFF4, // -12
 
     //@extension("VK_KHR_surface")
     VK_ERROR_SURFACE_LOST_KHR                               = 0xC4653600, // -1000000000
@@ -759,11 +930,27 @@
     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,
+    //@extension("VK_KHR_shared_presentable_image")
+    VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR   = 1000111001,
 }
 
 @extension("VK_KHR_surface")
 enum VkColorSpaceKHR {
     VK_COLORSPACE_SRGB_NONLINEAR_KHR                        = 0x00000000,
+    VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT 	 	    = 1000104001,
+    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT                 = 1000104002,
+    VK_COLOR_SPACE_SCRGB_LINEAR_EXT                         = 1000104003,
+    VK_COLOR_SPACE_SCRGB_NONLINEAR_EXT                      = 1000104004,
+    VK_COLOR_SPACE_DCI_P3_LINEAR_EXT                        = 1000104005,
+    VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT                     = 1000104006,
+    VK_COLOR_SPACE_BT709_LINEAR_EXT                         = 1000104007,
+    VK_COLOR_SPACE_BT709_NONLINEAR_EXT                      = 1000104008,
+    VK_COLOR_SPACE_BT2020_LINEAR_EXT                        = 1000104009,
+    VK_COLOR_SPACE_BT2020_NONLINEAR_EXT                     = 1000104010,
+    VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT                      = 1000104011,
+    VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT                   = 1000104012,
 }
 
 @extension("VK_EXT_debug_report")
@@ -797,6 +984,10 @@
     VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT             = 26,
     VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT           = 27,
     VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT            = 28,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT             = 29,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT        = 30,
+    VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT        = 31,
+    VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
 }
 
 @extension("VK_EXT_debug_report")
@@ -811,6 +1002,31 @@
     VK_RASTERIZATION_ORDER_RELAXED_AMD                      = 1,
 }
 
+@extension("VK_EXT_validation_flags")
+enum VkValidationCheckEXT {
+    VK_VALIDATION_CHECK_ALL_EXT                             = 0,
+}
+
+@extension("VK_NVX_device_generated_commands")
+enum VkIndirectCommandsTokenTypeNVX {
+    VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0,
+    VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1,
+    VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2,
+    VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3,
+    VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6,
+    VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7,
+}
+
+@extension("VK_NVX_device_generated_commands")
+enum VkObjectEntryTypeNVX {
+    VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0,
+    VK_OBJECT_ENTRY_PIPELINE_NVX = 1,
+    VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2,
+    VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3,
+    VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4,
+}
 
 /////////////////
 //  Bitfields  //
@@ -861,6 +1077,12 @@
     VK_ACCESS_HOST_WRITE_BIT                                = 0x00004000,
     VK_ACCESS_MEMORY_READ_BIT                               = 0x00008000,
     VK_ACCESS_MEMORY_WRITE_BIT                              = 0x00010000,
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX                  = 0x00020000,
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX                 = 0x00040000,
 }
 
 /// Buffer usage flags
@@ -1085,6 +1307,9 @@
 
     VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT                      = 0x00008000,  /// All stages of the graphics pipeline
     VK_PIPELINE_STAGE_ALL_COMMANDS_BIT                      = 0x00010000,  /// All graphics, compute, copy, and transition commands
+
+    //@extension("VK_NVX_device_generated_commands")
+    VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX               = 0x00020000,
 }
 
 /// Render pass attachment description flags
@@ -1354,6 +1579,50 @@
     VK_DEBUG_REPORT_DEBUG_BIT_EXT                           = 0x00000010,
 }
 
+@extension("VK_ANDROID_native_buffer")
+type VkFlags VkSwapchainImageUsageFlagsANDROID
+@extension("VK_ANDROID_native_buffer")
+bitfield VkSwapchainImageUsageFlagBitsANDROID {
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAGS_SHARED_BIT_ANDROID = 0x00000001,
+}
+
+@extension("VK_NV_external_memory_capabilities")
+type VkFlags VkExternalMemoryHandleTypeFlagsNV
+@extension("VK_NV_external_memory_capabilities")
+bitfield VkExternalMemoryHandleTypeFlagBitsNV {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008,
+}
+
+@extension("VK_NV_external_memory_capabilities")
+type VkFlags VkExternalMemoryFeatureFlagsNV
+@extension("VK_NV_external_memory_capabilities")
+bitfield VkExternalMemoryFeatureFlagBitsNV {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004,
+}
+
+@extension("VK_NVX_device_generated_commands")
+type VkFlags VkIndirectCommandsLayoutUsageFlagsNVX
+@extension("VK_NVX_device_generated_commands")
+bitfield VkIndirectCommandsLayoutUsageFlagBitsNVX {
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008,
+}
+
+@extension("VK_NVX_device_generated_commands")
+type VkFlags VkObjectEntryUsageFlagsNVX
+@extension("VK_NVX_device_generated_commands")
+bitfield VkObjectEntryUsageFlagBitsNVX {
+    VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001,
+    VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002,
+}
+
 
 //////////////////
 //  Structures  //
@@ -2649,6 +2918,42 @@
     int                                         usage
 }
 
+@extension("VK_ANDROID_native_buffer")
+class VkSwapchainImageCreateInfoANDROID {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainImageUsageFlagsANDROID           flags
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkRefreshCycleDurationGOOGLE {
+    u64                                         minRefreshDuration
+    u64                                         maxRefreshDuration
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPastPresentationTimingGOOGLE {
+    u32                                         presentID
+    u64                                         desiredPresentTime
+    u64                                         actualPresentTime
+    u64                                         earliestPresentTime
+    u64                                         presentMargin
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPresentTimeGOOGLE {
+    u32                                         presentID
+    u64                                         desiredPresentTime
+}
+
+@extension("VK_GOOGLE_display_timing")
+class VkPresentTimesInfoGOOGLE {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         swapchainCount
+    const VkPresentTimeGOOGLE*                  pTimes
+}
+
 @extension("VK_EXT_debug_report")
 class VkDebugReportCallbackCreateInfoEXT {
     VkStructureType                             sType
@@ -2693,6 +2998,306 @@
     f32[4]                                      color
 }
 
+@extension("VK_NV_dedicated_allocation")
+class VkDedicatedAllocationImageCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBool32                                    dedicatedAllocation
+}
+
+@extension("VK_NV_dedicated_allocation")
+class VkDedicatedAllocationBufferCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBool32                                    dedicatedAllocation
+}
+
+@extension("VK_NV_dedicated_allocation")
+class VkDedicatedAllocationMemoryAllocateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkImage                                     image
+    VkBuffer                                    buffer
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceFeatures2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkPhysicalDeviceFeatures                    features
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkPhysicalDeviceProperties                  properties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkFormatProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkFormatProperties                          formatProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkImageFormatProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkImageFormatProperties                     imageFormatProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceImageFormatInfo2KHR {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkFormat                                    format
+    VkImageType                                 type
+    VkImageTiling                               tiling
+    VkImageUsageFlags                           usage
+    VkImageCreateFlags                          flags
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkQueueFamilyProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkQueueFamilyProperties                     queueFamilyProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceMemoryProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkPhysicalDeviceMemoryProperties            memoryProperties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkSparseImageFormatProperties2KHR {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkSparseImageFormatProperties               properties
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+class VkPhysicalDeviceSparseImageFormatInfo2KHR {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkFormat                                    format
+    VkImageType                                 type
+    VkSampleCountFlagBits                       samples
+    VkImageUsageFlags                           usage
+    VkImageTiling                               tiling
+}
+
+@extension("VK_KHR_incremental_present")
+class VkRectLayerKHR {
+    VkOffset2D                                  offset
+    VkExtent2D                                  extent
+    u32                                         layer
+}
+
+@extension("VK_KHR_incremental_present")
+class VkPresentRegionKHR {
+    u32                                         rectangleCount
+    const VkRectLayerKHR*                       pRectangles
+}
+
+@extension("VK_KHR_incremental_present")
+class VkPresentRegionsKHR {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         swapchainCount
+    const VkPresentRegionKHR*                   pRegions
+}
+
+@extension("VK_NV_external_memory_capabilities")
+class VkExternalImageFormatPropertiesNV {
+    VkImageFormatProperties                     imageFormatProperties
+    VkExternalMemoryFeatureFlagsNV              externalMemoryFeatures
+    VkExternalMemoryHandleTypeFlagsNV           exportFromImportedHandleTypes
+    VkExternalMemoryHandleTypeFlagsNV           compatibleHandleTypes
+}
+
+@extension("VK_NV_external_memory")
+class VkExternalMemoryImageCreateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsNV           handleTypes
+}
+
+@extension("VK_NV_external_memory")
+class VkExportMemoryAllocateInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsNV           handleTypes
+}
+
+@extension("VK_NV_external_memory_win32")
+class VkImportMemoryWin32HandleInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkExternalMemoryHandleTypeFlagsNV           handleType
+    platform.HANDLE                             handle
+}
+
+@extension("VK_NV_external_memory_win32")
+class VkExportMemoryWin32HandleInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    const platform.SECURITY_ATTRIBUTES*         pAttributes
+    u32                                         dwAccess
+}
+
+@extension("VK_NV_win32_keyed_mutex")
+class VkWin32KeyedMutexAcquireReleaseInfoNV {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         acquireCount
+    const VkDeviceMemory*                       pAcquireSyncs
+    const u64*                                  pAcquireKeys
+    const u32*                                  pAcquireTimeoutMilliseconds
+    u32                                         releaseCount
+    const VkDeviceMemory*                       pReleaseSyncs
+    const u64*                                  pReleaseKeys
+}
+
+@extension("VK_EXT_validation_flags")
+class VkValidationFlagsEXT {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         disabledValidationCheckCount
+    VkValidationCheckEXT*                       pDisabledValidationChecks
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkDeviceGeneratedCommandsFeaturesNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkBool32                                    computeBindingPointSupport
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkDeviceGeneratedCommandsLimitsNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         maxIndirectCommandsLayoutTokenCount
+    u32                                         maxObjectEntryCounts
+    u32                                         minSequenceCountBufferOffsetAlignment
+    u32                                         minSequenceIndexBufferOffsetAlignment
+    u32                                         minCommandsTokenBufferOffsetAlignment
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkIndirectCommandsTokenNVX {
+    VkIndirectCommandsTokenTypeNVX              tokenType
+    VkBuffer                                    buffer
+    VkDeviceSize                                offset
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkIndirectCommandsLayoutTokenNVX {
+    VkIndirectCommandsTokenTypeNVX              tokenType
+    u32                                         bindingUnit
+    u32                                         dynamicCount
+    u32                                         divisor
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkIndirectCommandsLayoutCreateInfoNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkPipelineBindPoint                         pipelineBindPoint
+    VkIndirectCommandsLayoutUsageFlagsNVX       flags
+    u32                                         tokenCount
+    const VkIndirectCommandsLayoutTokenNVX*     pTokens
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkCmdProcessCommandsInfoNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkObjectTableNVX                            objectTable
+    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout
+    u32                                         indirectCommandsTokenCount
+    const VkIndirectCommandsTokenNVX*           pIndirectCommandsTokens
+    u32                                         maxSequencesCount
+    VkCommandBuffer                             targetCommandBuffer
+    VkBuffer                                    sequencesCountBuffer
+    VkDeviceSize                                sequencesCountOffset
+    VkBuffer                                    sequencesIndexBuffer
+    VkDeviceSize                                sequencesIndexOffset
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkCmdReserveSpaceForCommandsInfoNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkObjectTableNVX                            objectTable
+    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout
+    u32                                         maxSequencesCount
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableCreateInfoNVX {
+    VkStructureType                             sType
+    const void*                                 pNext
+    u32                                         objectCount
+    const VkObjectEntryTypeNVX*                 pObjectEntryTypes
+    const u32*                                  pObjectEntryCounts
+    const VkObjectEntryUsageFlagsNVX*           pObjectEntryUsageFlags
+    u32                                         maxUniformBuffersPerDescriptor
+    u32                                         maxStorageBuffersPerDescriptor
+    u32                                         maxStorageImagesPerDescriptor
+    u32                                         maxSampledImagesPerDescriptor
+    u32                                         maxPipelineLayouts
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTablePipelineEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkPipeline                                  pipeline
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableDescriptorSetEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkPipelineLayout                            pipelineLayout
+    VkDescriptorSet                             descriptorSet
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableVertexBufferEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkBuffer                                    buffer
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTableIndexBufferEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkBuffer                                    buffer
+}
+
+@extension("VK_NVX_device_generated_commands")
+class VkObjectTablePushConstantEntryNVX {
+    VkObjectEntryTypeNVX                        type
+    VkObjectEntryUsageFlagsNVX                  flags
+    VkPipelineLayout                            pipelineLayout
+    VkShaderStageFlags                          stageFlags
+}
+
+
 
 ////////////////
 //  Commands  //
@@ -4570,7 +5175,7 @@
         VkBuffer                                    dstBuffer,
         VkDeviceSize                                dstOffset,
         VkDeviceSize                                dataSize,
-        const u32*                                  pData) {
+        const void*                                 pData) {
     commandBufferObject := GetCommandBuffer(commandBuffer)
     dstBufferObject := GetBuffer(dstBuffer)
     assert(commandBufferObject.device == dstBufferObject.device)
@@ -5237,6 +5842,16 @@
 }
 
 @extension("VK_ANDROID_native_buffer")
+cmd VkResult vkGetSwapchainGrallocUsage2ANDROID(
+        VkDevice                                device,
+        VkFormat                                format,
+        VkImageUsageFlags                       imageUsage,
+        VkSwapchainImageUsageFlagsANDROID       swapchainImageUsage,
+        int*                                    grallocUsage) {
+    return ?
+}
+
+@extension("VK_ANDROID_native_buffer")
 cmd VkResult vkAcquireImageANDROID(
         VkDevice                                device,
         VkImage                                 image,
@@ -5256,6 +5871,29 @@
     return ?
 }
 
+@extension("VK_GOOGLE_display_timing")
+cmd VkResult vkGetRefreshCycleDurationGOOGLE(
+        VkDevice                                 device,
+        VkSwapchainKHR                           swapchain,
+        VkRefreshCycleDurationGOOGLE*            pDisplayTimingProperties) {
+    deviceObject := GetDevice(device)
+    swapchainObject := GetSwapchain(swapchain)
+
+    displayTimingProperties := ?
+    pDisplayTimingProperties[0] = displayTimingProperties
+
+    return ?
+}
+
+@extension("VK_GOOGLE_display_timing")
+cmd VkResult vkGetPastPresentationTimingGOOGLE(
+        VkDevice                                 device,
+        VkSwapchainKHR                           swapchain,
+        u32*                                     pPresentationTimingCount,
+        VkPastPresentationTimingGOOGLE*          pPresentationTimings) {
+    return ?
+}
+
 @extension("VK_EXT_debug_report")
 @external type void* PFN_vkDebugReportCallbackEXT
 @extension("VK_EXT_debug_report")
@@ -5330,6 +5968,176 @@
         VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo) {
 }
 
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceFeatures2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        VkPhysicalDeviceFeatures2KHR*               pFeatures) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        VkPhysicalDeviceProperties2KHR*             pProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceFormatProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        VkFormat                                    format,
+        VkFormatProperties2KHR*                     pFormatProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd VkResult vkGetPhysicalDeviceImageFormatProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        const VkPhysicalDeviceImageFormatInfo2KHR*  pImageFormatInfo,
+        VkImageFormatProperties2KHR*                pImageFormatProperties) {
+    return ?
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceQueueFamilyProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        u32*                                        pQueueFamilyPropertyCount,
+        VkQueueFamilyProperties2KHR*                pQueueFamilyProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceMemoryProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        VkPhysicalDeviceMemoryProperties2KHR*       pMemoryProperties) {
+}
+
+@extension("VK_KHR_get_physical_device_properties2")
+cmd void vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
+        VkPhysicalDevice                            physicalDevice,
+        const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo,
+        u32*                                        pPropertyCount,
+        VkSparseImageFormatProperties2KHR*          pProperties) {
+}
+
+@extension("VK_AMD_draw_indirect_count")
+cmd void vkCmdDrawIndirectCountAMD(
+        VkCommandBuffer                             commandBuffer,
+        VkBuffer                                    buffer,
+        VkDeviceSize                                offset,
+        VkBuffer                                    countBuffer,
+        VkDeviceSize                                countBufferOffset,
+        u32                                         maxDrawCount,
+        u32                                         stride) {
+}
+
+@extension("VK_AMD_draw_indirect_count")
+cmd void vkCmdDrawIndexedIndirectCountAMD(
+        VkCommandBuffer                             commandBuffer,
+        VkBuffer                                    buffer,
+        VkDeviceSize                                offset,
+        VkBuffer                                    countBuffer,
+        VkDeviceSize                                countBufferOffset,
+        u32                                         maxDrawCount,
+        u32                                         stride) {
+}
+
+@extension("VK_NV_external_memory_capabilities")
+cmd VkResult vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
+        VkPhysicalDevice                            physicalDevice,
+        VkFormat                                    format,
+        VkImageType                                 type,
+        VkImageTiling                               tiling,
+        VkImageUsageFlags                           usage,
+        VkImageCreateFlags                          flags,
+        VkExternalMemoryHandleTypeFlagsNV           externalHandleType,
+        VkExternalImageFormatPropertiesNV*          pExternalImageFormatProperties) {
+    return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult vkGetMemoryWin32HandleNV(
+        VkDevice                                    device,
+        VkDeviceMemory                              memory,
+        VkExternalMemoryHandleTypeFlagsNV           handleType,
+        platform.HANDLE*                            pHandle) {
+    return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void  vkCmdProcessCommandsNVX(
+        VkCommandBuffer                             commandBuffer,
+        const VkCmdProcessCommandsInfoNVX*          pProcessCommandsInfo) {
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void  vkCmdReserveSpaceForCommandsNVX(
+        VkCommandBuffer                             commandBuffer,
+        const VkCmdReserveSpaceForCommandsInfoNVX*  pReserveSpaceInfo) {
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult  vkCreateIndirectCommandsLayoutNVX(
+        VkDevice                                    device,
+        const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkIndirectCommandsLayoutNVX*                pIndirectCommandsLayout) {
+    return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void  vkDestroyIndirectCommandsLayoutNVX(
+        VkDevice                                    device,
+        VkIndirectCommandsLayoutNVX                 indirectCommandsLayout,
+        const VkAllocationCallbacks*                pAllocator) {
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult  vkCreateObjectTableNVX(
+        VkDevice                                    device,
+        const VkObjectTableCreateInfoNVX*           pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkObjectTableNVX*                           pObjectTable) {
+    return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void  vkDestroyObjectTableNVX(
+        VkDevice                                    device,
+        VkObjectTableNVX                            objectTable,
+        const VkAllocationCallbacks*                pAllocator) {
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult  vkRegisterObjectsNVX(
+        VkDevice                                    device,
+        VkObjectTableNVX                            objectTable,
+        u32                                         objectCount,
+        const VkObjectTableEntryNVX* const*         ppObjectTableEntries,
+        const u32*                                  pObjectIndices) {
+    return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd VkResult  vkUnregisterObjectsNVX(
+        VkDevice                                    device,
+        VkObjectTableNVX                            objectTable,
+        u32                                         objectCount,
+        const VkObjectEntryTypeNVX*                 pObjectEntryTypes,
+        const u32*                                  pObjectIndices) {
+    return ?
+}
+
+@extension("VK_NV_external_memory_win32")
+cmd void  vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
+        VkPhysicalDevice                            physicalDevice,
+        VkDeviceGeneratedCommandsFeaturesNVX*       pFeatures,
+        VkDeviceGeneratedCommandsLimitsNVX*         pLimits) {
+}
+
+@extension("VK_KHR_shared_presentable_image")
+cmd VkResult vkGetSwapchainStatusKHR(
+        VkDevice                                    device,
+        VkSwapchainKHR                              swapchain) {
+    return ?
+}
+
 
 ////////////////
 // Validation //
diff --git a/vulkan/doc/implementors_guide/implementors_guide.adoc b/vulkan/doc/implementors_guide/implementors_guide.adoc
index 7ace777..009472a 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.adoc
+++ b/vulkan/doc/implementors_guide/implementors_guide.adoc
@@ -59,6 +59,28 @@
 ----
 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.
 
+Implementations may further 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 additional 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;
+----
+
+If the driver provides the +vkGetSwapchainGrallocUsage2ANDROID+ function, the platform will use it in preference to +vkGetSwapchainGrallocUsageANDROID+ when translating a requested format, image usage flags, and swapchain image usage flags into gralloc usage flags. +vkGetSwapchainGrallocUsage2ANDROID+ behaves in the same way as +vkGetSwapchainGrallocUsageANDROID+, and is declared as
+[source,c]
+----
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+    VkDevice            device,
+    VkFormat            format,
+    VkImageUsageFlags   imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    int*                grallocUsage
+);
+----
+
 +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.
 
 [source,c]
@@ -78,6 +100,7 @@
 ----
 
 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 +116,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 +173,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 +194,9 @@
 . *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
diff --git a/vulkan/doc/implementors_guide/implementors_guide.html b/vulkan/doc/implementors_guide/implementors_guide.html
index 0bfeb81..4e74a78 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.html
+++ b/vulkan/doc/implementors_guide/implementors_guide.html
@@ -730,7 +730,7 @@
 /*]]>*/
 </script>
 </head>
-<body class="book">
+<body class="article">
 <div id="header">
 <h1>Vulkan on Android Implementor&#8217;s Guide</h1>
 <span id="revnumber">version 5</span>
@@ -795,7 +795,7 @@
 <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. 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 -->
@@ -806,9 +806,33 @@
     <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>Implementations may further 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 additional 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>If the driver provides the <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> function, the platform will use it in preference to <span class="monospaced">vkGetSwapchainGrallocUsageANDROID</span> when translating a requested format, image usage flags, and swapchain image usage flags into gralloc usage flags. <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> behaves in the same way as <span class="monospaced">vkGetSwapchainGrallocUsageANDROID</span>, and is declared 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>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>
+    <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><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 -->
@@ -826,8 +850,11 @@
 <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 +865,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 +910,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 +922,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 +1017,33 @@
 </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>
 </ol></div>
 </div>
 </div>
@@ -986,7 +1052,7 @@
 <div id="footer">
 <div id="footer-text">
 Version 5<br>
-Last updated 2016-06-17 13:54:25 PDT
+Last updated 2017-01-12 14:25:30 NZDT
 </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..db23e79 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -27,11 +27,18 @@
 #define VK_ANDROID_native_buffer 1
 
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     5
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     6
 #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)
+
+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
@@ -46,7 +53,15 @@
     int                         usage;
 } VkNativeBufferANDROID;
 
+typedef struct {
+    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+    const void*                            pNext;
+
+    VkSwapchainImageUsageFlagsANDROID      usage;
+} VkSwapchainImageCreateInfoANDROID;
+
 typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, int* grallocUsage);
 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);
 
@@ -57,6 +72,13 @@
     VkImageUsageFlags   imageUsage,
     int*                grallocUsage
 );
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+    VkDevice            device,
+    VkFormat            format,
+    VkImageUsageFlags   imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    int*                grallocUsage
+);
 VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
     VkDevice            device,
     VkImage             image,
diff --git a/vulkan/include/vulkan/vk_platform.h b/vulkan/include/vulkan/vk_platform.h
index 5d0fc76..c2232ec 100644
--- a/vulkan/include/vulkan/vk_platform.h
+++ b/vulkan/include/vulkan/vk_platform.h
@@ -51,13 +51,13 @@
     #define VKAPI_ATTR
     #define VKAPI_CALL __stdcall
     #define VKAPI_PTR  VKAPI_CALL
-#elif defined(__ANDROID__) && defined(__ARM_EABI__) && !defined(__ARM_ARCH_7A__)
-    // Android does not support Vulkan in native code using the "armeabi" ABI.
-    #error "Vulkan requires the 'armeabi-v7a' or 'armeabi-v7a-hard' ABI on 32-bit ARM CPUs"
-#elif defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
-    // On Android/ARMv7a, Vulkan functions use the armeabi-v7a-hard calling
-    // convention, even if the application's native code is compiled with the
-    // armeabi-v7a calling convention.
+#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
+    #error "Vulkan isn't supported for the 'armeabi' NDK ABI"
+#elif defined(__ANDROID__) && __ARM_ARCH >= 7 && __ARM_32BIT_STATE
+    // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
+    // calling convention, i.e. float parameters are passed in registers. This
+    // is true even if the rest of the application passes floats on the stack,
+    // as it does by default when compiling for the armeabi-v7a NDK ABI.
     #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
     #define VKAPI_CALL
     #define VKAPI_PTR  VKAPI_ATTR
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index 2f18076..8d24aa7 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -43,7 +43,7 @@
 #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
 #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
 // Version of this file
-#define VK_HEADER_VERSION 13
+#define VK_HEADER_VERSION 38
 
 
 #define VK_NULL_HANDLE 0
@@ -53,11 +53,13 @@
 #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
 
 
-#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE)
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
         #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;
 #else
         #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
 #endif
+#endif
         
 
 
@@ -135,6 +137,7 @@
     VK_ERROR_INCOMPATIBLE_DRIVER = -9,
     VK_ERROR_TOO_MANY_OBJECTS = -10,
     VK_ERROR_FORMAT_NOT_SUPPORTED = -11,
+    VK_ERROR_FRAGMENTED_POOL = -12,
     VK_ERROR_SURFACE_LOST_KHR = -1000000000,
     VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001,
     VK_SUBOPTIMAL_KHR = 1000001003,
@@ -142,9 +145,9 @@
     VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001,
     VK_ERROR_VALIDATION_FAILED_EXT = -1000011001,
     VK_ERROR_INVALID_SHADER_NV = -1000012000,
-    VK_RESULT_BEGIN_RANGE = VK_ERROR_FORMAT_NOT_SUPPORTED,
+    VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL,
     VK_RESULT_END_RANGE = VK_INCOMPLETE,
-    VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FORMAT_NOT_SUPPORTED + 1),
+    VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1),
     VK_RESULT_MAX_ENUM = 0x7FFFFFFF
 } VkResult;
 
@@ -214,6 +217,32 @@
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000,
     VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001,
     VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
+    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
+    VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000,
+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001,
+    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000,
+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001,
+    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001,
+    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004,
+    VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006,
+    VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+    VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
+    VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000,
+    VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001,
+    VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002,
+    VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003,
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
+    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
+    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000,
     VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
     VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
     VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -426,6 +455,14 @@
     VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182,
     VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183,
     VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184,
+    VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
+    VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
+    VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
+    VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
+    VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
+    VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
+    VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
+    VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
     VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED,
     VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
     VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1),
@@ -493,6 +530,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),
@@ -898,6 +936,7 @@
     VK_PIPELINE_STAGE_HOST_BIT = 0x00004000,
     VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000,
     VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000,
+    VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000,
     VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkPipelineStageFlagBits;
 typedef VkFlags VkPipelineStageFlags;
@@ -1072,6 +1111,8 @@
     VK_ACCESS_HOST_WRITE_BIT = 0x00004000,
     VK_ACCESS_MEMORY_READ_BIT = 0x00008000,
     VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000,
+    VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000,
+    VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000,
     VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkAccessFlagBits;
 typedef VkFlags VkAccessFlags;
@@ -2347,7 +2388,7 @@
 typedef void (VKAPI_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
 typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
 typedef void (VKAPI_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
 typedef void (VKAPI_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
 typedef void (VKAPI_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -3032,7 +3073,7 @@
     VkBuffer                                    dstBuffer,
     VkDeviceSize                                dstOffset,
     VkDeviceSize                                dataSize,
-    const uint32_t*                             pData);
+    const void*                                 pData);
 
 VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer(
     VkCommandBuffer                             commandBuffer,
@@ -3177,6 +3218,18 @@
 
 typedef enum VkColorSpaceKHR {
     VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0,
+    VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT = 1000104001,
+    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104002,
+    VK_COLOR_SPACE_SCRGB_LINEAR_EXT = 1000104003,
+    VK_COLOR_SPACE_SCRGB_NONLINEAR_EXT = 1000104004,
+    VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = 1000104005,
+    VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104006,
+    VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104007,
+    VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104008,
+    VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104009,
+    VK_COLOR_SPACE_BT2020_NONLINEAR_EXT = 1000104010,
+    VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011,
+    VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012,
     VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
     VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
     VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1),
@@ -3188,6 +3241,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),
@@ -3712,10 +3767,154 @@
 #define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge"
 
 
+#define VK_KHR_get_physical_device_properties2 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
+typedef struct VkPhysicalDeviceFeatures2KHR {
+    VkStructureType             sType;
+    void*                       pNext;
+    VkPhysicalDeviceFeatures    features;
+} VkPhysicalDeviceFeatures2KHR;
+
+typedef struct VkPhysicalDeviceProperties2KHR {
+    VkStructureType               sType;
+    void*                         pNext;
+    VkPhysicalDeviceProperties    properties;
+} VkPhysicalDeviceProperties2KHR;
+
+typedef struct VkFormatProperties2KHR {
+    VkStructureType       sType;
+    void*                 pNext;
+    VkFormatProperties    formatProperties;
+} VkFormatProperties2KHR;
+
+typedef struct VkImageFormatProperties2KHR {
+    VkStructureType            sType;
+    void*                      pNext;
+    VkImageFormatProperties    imageFormatProperties;
+} VkImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceImageFormatInfo2KHR {
+    VkStructureType       sType;
+    const void*           pNext;
+    VkFormat              format;
+    VkImageType           type;
+    VkImageTiling         tiling;
+    VkImageUsageFlags     usage;
+    VkImageCreateFlags    flags;
+} VkPhysicalDeviceImageFormatInfo2KHR;
+
+typedef struct VkQueueFamilyProperties2KHR {
+    VkStructureType            sType;
+    void*                      pNext;
+    VkQueueFamilyProperties    queueFamilyProperties;
+} VkQueueFamilyProperties2KHR;
+
+typedef struct VkPhysicalDeviceMemoryProperties2KHR {
+    VkStructureType                     sType;
+    void*                               pNext;
+    VkPhysicalDeviceMemoryProperties    memoryProperties;
+} VkPhysicalDeviceMemoryProperties2KHR;
+
+typedef struct VkSparseImageFormatProperties2KHR {
+    VkStructureType                  sType;
+    void*                            pNext;
+    VkSparseImageFormatProperties    properties;
+} VkSparseImageFormatProperties2KHR;
+
+typedef struct VkPhysicalDeviceSparseImageFormatInfo2KHR {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkFormat                 format;
+    VkImageType              type;
+    VkSampleCountFlagBits    samples;
+    VkImageUsageFlags        usage;
+    VkImageTiling            tiling;
+} VkPhysicalDeviceSparseImageFormatInfo2KHR;
+
+
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceFeatures2KHR*               pFeatures);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceProperties2KHR*             pProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkFormatProperties2KHR*                     pFormatProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceImageFormatInfo2KHR*  pImageFormatInfo,
+    VkImageFormatProperties2KHR*                pImageFormatProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    uint32_t*                                   pQueueFamilyPropertyCount,
+    VkQueueFamilyProperties2KHR*                pQueueFamilyProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    VkPhysicalDeviceMemoryProperties2KHR*       pMemoryProperties);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
+    VkPhysicalDevice                            physicalDevice,
+    const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo,
+    uint32_t*                                   pPropertyCount,
+    VkSparseImageFormatProperties2KHR*          pProperties);
+#endif
+
+#define VK_KHR_incremental_present 1
+#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
+#define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"
+
+typedef struct VkRectLayerKHR {
+    VkOffset2D offset;
+    VkExtent2D extent;
+    uint32_t layer;
+} VkRectLayerKHR;
+
+typedef struct VkPresentRegionKHR {
+    uint32_t rectangleCount;
+    const VkRectLayerKHR* pRectangles;
+} VkPresentRegionKHR;
+
+typedef struct VkPresentRegionsKHR {
+    VkStructureType sType;
+    const void* pNext;
+    uint32_t swapchainCount;
+    const VkPresentRegionKHR* pRegions;
+} VkPresentRegionsKHR;
+
+#define VK_KHR_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 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_EXT_debug_report 1
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
 
-#define VK_EXT_DEBUG_REPORT_SPEC_VERSION  2
+#define VK_EXT_DEBUG_REPORT_SPEC_VERSION  4
 #define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report"
 #define VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT
 
@@ -3750,9 +3949,13 @@
     VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26,
     VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27,
     VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30,
+    VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31,
+    VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
     VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
-    VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
-    VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
+    VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT,
+    VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
     VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
 } VkDebugReportObjectTypeEXT;
 
@@ -3855,6 +4058,16 @@
 
 
 
+#define VK_AMD_shader_trinary_minmax 1
+#define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1
+#define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax"
+
+
+#define VK_AMD_shader_explicit_vertex_parameter 1
+#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1
+#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
+
+
 #define VK_EXT_debug_marker 1
 #define VK_EXT_DEBUG_MARKER_SPEC_VERSION  3
 #define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker"
@@ -3912,6 +4125,489 @@
     VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo);
 #endif
 
+#define VK_AMD_gcn_shader 1
+#define VK_AMD_GCN_SHADER_SPEC_VERSION    1
+#define VK_AMD_GCN_SHADER_EXTENSION_NAME  "VK_AMD_gcn_shader"
+
+
+#define VK_NV_dedicated_allocation 1
+#define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1
+#define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation"
+
+typedef struct VkDedicatedAllocationImageCreateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           dedicatedAllocation;
+} VkDedicatedAllocationImageCreateInfoNV;
+
+typedef struct VkDedicatedAllocationBufferCreateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           dedicatedAllocation;
+} VkDedicatedAllocationBufferCreateInfoNV;
+
+typedef struct VkDedicatedAllocationMemoryAllocateInfoNV {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkImage            image;
+    VkBuffer           buffer;
+} VkDedicatedAllocationMemoryAllocateInfoNV;
+
+
+#define VK_GOOGLE_display_timing 1
+#define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+#define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+typedef struct VkRefreshCycleDurationGOOGLE {
+    uint64_t    refreshDuration;
+} VkRefreshCycleDurationGOOGLE;
+
+typedef struct VkPastPresentationTimingGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+    uint64_t    actualPresentTime;
+    uint64_t    earliestPresentTime;
+    uint64_t    presentMargin;
+} VkPastPresentationTimingGOOGLE;
+
+typedef struct VkPresentTimeGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+} VkPresentTimeGOOGLE;
+
+typedef struct VkPresentTimesInfoGOOGLE {
+    VkStructureType               sType;
+    const void*                   pNext;
+    uint32_t                      swapchainCount;
+    const VkPresentTimeGOOGLE*    pTimes;
+} VkPresentTimesInfoGOOGLE;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    uint32_t*                                   pPresentationTimingCount,
+    VkPastPresentationTimingGOOGLE*             pPresentationTimings);
+#endif
+
+
+#define VK_AMD_draw_indirect_count 1
+#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
+#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
+
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountAMD(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    VkBuffer                                    countBuffer,
+    VkDeviceSize                                countBufferOffset,
+    uint32_t                                    maxDrawCount,
+    uint32_t                                    stride);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD(
+    VkCommandBuffer                             commandBuffer,
+    VkBuffer                                    buffer,
+    VkDeviceSize                                offset,
+    VkBuffer                                    countBuffer,
+    VkDeviceSize                                countBufferOffset,
+    uint32_t                                    maxDrawCount,
+    uint32_t                                    stride);
+#endif
+
+#define VK_AMD_negative_viewport_height 1
+#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1
+#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height"
+
+
+#define VK_AMD_gpu_shader_half_float 1
+#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1
+#define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
+
+
+#define VK_AMD_shader_ballot 1
+#define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
+#define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
+
+
+#define VK_IMG_format_pvrtc 1
+#define VK_IMG_FORMAT_PVRTC_SPEC_VERSION  1
+#define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
+
+
+#define VK_NV_external_memory_capabilities 1
+#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
+
+
+typedef enum VkExternalMemoryHandleTypeFlagBitsNV {
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008,
+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF
+} VkExternalMemoryHandleTypeFlagBitsNV;
+typedef VkFlags VkExternalMemoryHandleTypeFlagsNV;
+
+typedef enum VkExternalMemoryFeatureFlagBitsNV {
+    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001,
+    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002,
+    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004,
+    VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF
+} VkExternalMemoryFeatureFlagBitsNV;
+typedef VkFlags VkExternalMemoryFeatureFlagsNV;
+
+typedef struct VkExternalImageFormatPropertiesNV {
+    VkImageFormatProperties              imageFormatProperties;
+    VkExternalMemoryFeatureFlagsNV       externalMemoryFeatures;
+    VkExternalMemoryHandleTypeFlagsNV    exportFromImportedHandleTypes;
+    VkExternalMemoryHandleTypeFlagsNV    compatibleHandleTypes;
+} VkExternalImageFormatPropertiesNV;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
+    VkPhysicalDevice                            physicalDevice,
+    VkFormat                                    format,
+    VkImageType                                 type,
+    VkImageTiling                               tiling,
+    VkImageUsageFlags                           usage,
+    VkImageCreateFlags                          flags,
+    VkExternalMemoryHandleTypeFlagsNV           externalHandleType,
+    VkExternalImageFormatPropertiesNV*          pExternalImageFormatProperties);
+#endif
+
+#define VK_NV_external_memory 1
+#define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory"
+
+typedef struct VkExternalMemoryImageCreateInfoNV {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkExternalMemoryHandleTypeFlagsNV    handleTypes;
+} VkExternalMemoryImageCreateInfoNV;
+
+typedef struct VkExportMemoryAllocateInfoNV {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkExternalMemoryHandleTypeFlagsNV    handleTypes;
+} VkExportMemoryAllocateInfoNV;
+
+
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_NV_external_memory_win32 1
+#define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
+#define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
+
+typedef struct VkImportMemoryWin32HandleInfoNV {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkExternalMemoryHandleTypeFlagsNV    handleType;
+    HANDLE                               handle;
+} VkImportMemoryWin32HandleInfoNV;
+
+typedef struct VkExportMemoryWin32HandleInfoNV {
+    VkStructureType               sType;
+    const void*                   pNext;
+    const SECURITY_ATTRIBUTES*    pAttributes;
+    DWORD                         dwAccess;
+} VkExportMemoryWin32HandleInfoNV;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV(
+    VkDevice                                    device,
+    VkDeviceMemory                              memory,
+    VkExternalMemoryHandleTypeFlagsNV           handleType,
+    HANDLE*                                     pHandle);
+#endif
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+#define VK_NV_win32_keyed_mutex 1
+#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1
+#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
+
+typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV {
+    VkStructureType          sType;
+    const void*              pNext;
+    uint32_t                 acquireCount;
+    const VkDeviceMemory*    pAcquireSyncs;
+    const uint64_t*          pAcquireKeys;
+    const uint32_t*          pAcquireTimeoutMilliseconds;
+    uint32_t                 releaseCount;
+    const VkDeviceMemory*    pReleaseSyncs;
+    const uint64_t*          pReleaseKeys;
+} VkWin32KeyedMutexAcquireReleaseInfoNV;
+
+
+#endif /* VK_USE_PLATFORM_WIN32_KHR */
+
+#define VK_EXT_validation_flags 1
+#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
+#define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
+
+
+typedef enum VkValidationCheckEXT {
+    VK_VALIDATION_CHECK_ALL_EXT = 0,
+    VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT,
+    VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT,
+    VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_ALL_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1),
+    VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkValidationCheckEXT;
+
+typedef struct VkValidationFlagsEXT {
+    VkStructureType          sType;
+    const void*              pNext;
+    uint32_t                 disabledValidationCheckCount;
+    VkValidationCheckEXT*    pDisabledValidationChecks;
+} VkValidationFlagsEXT;
+
+
+
+#define VK_NVX_device_generated_commands 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX)
+
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
+
+
+typedef enum VkIndirectCommandsTokenTypeNVX {
+    VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX = 0,
+    VK_INDIRECT_COMMANDS_TOKEN_DESCRIPTOR_SET_NVX = 1,
+    VK_INDIRECT_COMMANDS_TOKEN_INDEX_BUFFER_NVX = 2,
+    VK_INDIRECT_COMMANDS_TOKEN_VERTEX_BUFFER_NVX = 3,
+    VK_INDIRECT_COMMANDS_TOKEN_PUSH_CONSTANT_NVX = 4,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_INDEXED_NVX = 5,
+    VK_INDIRECT_COMMANDS_TOKEN_DRAW_NVX = 6,
+    VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX = 7,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX,
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_PIPELINE_NVX + 1),
+    VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkIndirectCommandsTokenTypeNVX;
+
+typedef enum VkObjectEntryTypeNVX {
+    VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX = 0,
+    VK_OBJECT_ENTRY_PIPELINE_NVX = 1,
+    VK_OBJECT_ENTRY_INDEX_BUFFER_NVX = 2,
+    VK_OBJECT_ENTRY_VERTEX_BUFFER_NVX = 3,
+    VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX = 4,
+    VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX,
+    VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX,
+    VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_DESCRIPTOR_SET_NVX + 1),
+    VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkObjectEntryTypeNVX;
+
+
+typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX {
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008,
+    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkIndirectCommandsLayoutUsageFlagBitsNVX;
+typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX;
+
+typedef enum VkObjectEntryUsageFlagBitsNVX {
+    VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001,
+    VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002,
+    VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF
+} VkObjectEntryUsageFlagBitsNVX;
+typedef VkFlags VkObjectEntryUsageFlagsNVX;
+
+typedef struct VkDeviceGeneratedCommandsFeaturesNVX {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           computeBindingPointSupport;
+} VkDeviceGeneratedCommandsFeaturesNVX;
+
+typedef struct VkDeviceGeneratedCommandsLimitsNVX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           maxIndirectCommandsLayoutTokenCount;
+    uint32_t           maxObjectEntryCounts;
+    uint32_t           minSequenceCountBufferOffsetAlignment;
+    uint32_t           minSequenceIndexBufferOffsetAlignment;
+    uint32_t           minCommandsTokenBufferOffsetAlignment;
+} VkDeviceGeneratedCommandsLimitsNVX;
+
+typedef struct VkIndirectCommandsTokenNVX {
+    VkIndirectCommandsTokenTypeNVX    tokenType;
+    VkBuffer                          buffer;
+    VkDeviceSize                      offset;
+} VkIndirectCommandsTokenNVX;
+
+typedef struct VkIndirectCommandsLayoutTokenNVX {
+    VkIndirectCommandsTokenTypeNVX    tokenType;
+    uint32_t                          bindingUnit;
+    uint32_t                          dynamicCount;
+    uint32_t                          divisor;
+} VkIndirectCommandsLayoutTokenNVX;
+
+typedef struct VkIndirectCommandsLayoutCreateInfoNVX {
+    VkStructureType                            sType;
+    const void*                                pNext;
+    VkPipelineBindPoint                        pipelineBindPoint;
+    VkIndirectCommandsLayoutUsageFlagsNVX      flags;
+    uint32_t                                   tokenCount;
+    const VkIndirectCommandsLayoutTokenNVX*    pTokens;
+} VkIndirectCommandsLayoutCreateInfoNVX;
+
+typedef struct VkCmdProcessCommandsInfoNVX {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    VkObjectTableNVX                     objectTable;
+    VkIndirectCommandsLayoutNVX          indirectCommandsLayout;
+    uint32_t                             indirectCommandsTokenCount;
+    const VkIndirectCommandsTokenNVX*    pIndirectCommandsTokens;
+    uint32_t                             maxSequencesCount;
+    VkCommandBuffer                      targetCommandBuffer;
+    VkBuffer                             sequencesCountBuffer;
+    VkDeviceSize                         sequencesCountOffset;
+    VkBuffer                             sequencesIndexBuffer;
+    VkDeviceSize                         sequencesIndexOffset;
+} VkCmdProcessCommandsInfoNVX;
+
+typedef struct VkCmdReserveSpaceForCommandsInfoNVX {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkObjectTableNVX               objectTable;
+    VkIndirectCommandsLayoutNVX    indirectCommandsLayout;
+    uint32_t                       maxSequencesCount;
+} VkCmdReserveSpaceForCommandsInfoNVX;
+
+typedef struct VkObjectTableCreateInfoNVX {
+    VkStructureType                      sType;
+    const void*                          pNext;
+    uint32_t                             objectCount;
+    const VkObjectEntryTypeNVX*          pObjectEntryTypes;
+    const uint32_t*                      pObjectEntryCounts;
+    const VkObjectEntryUsageFlagsNVX*    pObjectEntryUsageFlags;
+    uint32_t                             maxUniformBuffersPerDescriptor;
+    uint32_t                             maxStorageBuffersPerDescriptor;
+    uint32_t                             maxStorageImagesPerDescriptor;
+    uint32_t                             maxSampledImagesPerDescriptor;
+    uint32_t                             maxPipelineLayouts;
+} VkObjectTableCreateInfoNVX;
+
+typedef struct VkObjectTableEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+} VkObjectTableEntryNVX;
+
+typedef struct VkObjectTablePipelineEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipeline                    pipeline;
+} VkObjectTablePipelineEntryNVX;
+
+typedef struct VkObjectTableDescriptorSetEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipelineLayout              pipelineLayout;
+    VkDescriptorSet               descriptorSet;
+} VkObjectTableDescriptorSetEntryNVX;
+
+typedef struct VkObjectTableVertexBufferEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkBuffer                      buffer;
+} VkObjectTableVertexBufferEntryNVX;
+
+typedef struct VkObjectTableIndexBufferEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkBuffer                      buffer;
+} VkObjectTableIndexBufferEntryNVX;
+
+typedef struct VkObjectTablePushConstantEntryNVX {
+    VkObjectEntryTypeNVX          type;
+    VkObjectEntryUsageFlagsNVX    flags;
+    VkPipelineLayout              pipelineLayout;
+    VkShaderStageFlags            stageFlags;
+} VkObjectTablePushConstantEntryNVX;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout);
+typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable);
+typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const*    ppObjectTableEntries, const uint32_t* pObjectIndices);
+typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX(
+    VkCommandBuffer                             commandBuffer,
+    const VkCmdProcessCommandsInfoNVX*          pProcessCommandsInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX(
+    VkCommandBuffer                             commandBuffer,
+    const VkCmdReserveSpaceForCommandsInfoNVX*  pReserveSpaceInfo);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX(
+    VkDevice                                    device,
+    const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkIndirectCommandsLayoutNVX*                pIndirectCommandsLayout);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX(
+    VkDevice                                    device,
+    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX(
+    VkDevice                                    device,
+    const VkObjectTableCreateInfoNVX*           pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkObjectTableNVX*                           pObjectTable);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    uint32_t                                    objectCount,
+    const VkObjectTableEntryNVX* const*         ppObjectTableEntries,
+    const uint32_t*                             pObjectIndices);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX(
+    VkDevice                                    device,
+    VkObjectTableNVX                            objectTable,
+    uint32_t                                    objectCount,
+    const VkObjectEntryTypeNVX*                 pObjectEntryTypes,
+    const uint32_t*                             pObjectIndices);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
+    VkPhysicalDevice                            physicalDevice,
+    VkDeviceGeneratedCommandsFeaturesNVX*       pFeatures,
+    VkDeviceGeneratedCommandsLimitsNVX*         pLimits);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 70c7e75..746ab4e 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",
     ],
@@ -66,10 +67,10 @@
     export_static_lib_headers: ["vulkan_headers"],
     static_libs: [
         "vulkan_headers",
-        "libziparchive",
     ],
     shared_libs: [
         "libgui",
+        "libziparchive",
         "libhardware",
         "libsync",
         "libbase",
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 b72aa0a..b8b7e94 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;
@@ -396,7 +396,7 @@
 VKAPI_ATTR void CmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
 VKAPI_ATTR void CmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
 VKAPI_ATTR void CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
 VKAPI_ATTR void CmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
 VKAPI_ATTR void CmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
 VKAPI_ATTR void CmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -453,13 +453,22 @@
         "vkEnumerateInstanceLayerProperties",
         "vkEnumeratePhysicalDevices",
         "vkGetInstanceProcAddr",
+        "vkGetPhysicalDeviceExternalImageFormatPropertiesNV",
         "vkGetPhysicalDeviceFeatures",
+        "vkGetPhysicalDeviceFeatures2KHR",
         "vkGetPhysicalDeviceFormatProperties",
+        "vkGetPhysicalDeviceFormatProperties2KHR",
+        "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX",
         "vkGetPhysicalDeviceImageFormatProperties",
+        "vkGetPhysicalDeviceImageFormatProperties2KHR",
         "vkGetPhysicalDeviceMemoryProperties",
+        "vkGetPhysicalDeviceMemoryProperties2KHR",
         "vkGetPhysicalDeviceProperties",
+        "vkGetPhysicalDeviceProperties2KHR",
         "vkGetPhysicalDeviceQueueFamilyProperties",
+        "vkGetPhysicalDeviceQueueFamilyProperties2KHR",
         "vkGetPhysicalDeviceSparseImageFormatProperties",
+        "vkGetPhysicalDeviceSparseImageFormatProperties2KHR",
         "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
         "vkGetPhysicalDeviceSurfaceFormatsKHR",
         "vkGetPhysicalDeviceSurfacePresentModesKHR",
@@ -1077,7 +1086,7 @@
     GetData(commandBuffer).dispatch.CmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions);
 }
 
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData) {
     GetData(commandBuffer).dispatch.CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
 }
 
@@ -1797,7 +1806,7 @@
 }
 
 __attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+VKAPI_ATTR void vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData) {
     vulkan::api::CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
 }
 
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index 46333ec..501877c 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -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;                                      \
       }                                                         \
@@ -413,10 +413,11 @@
   {{AssertType $ "Function"}}
 
   {{$ext := GetAnnotation $ "extension"}}
+  {{$required := (Macro "IsRequiredFunction" $)}}
   {{if $ext}}
-    INIT_PROC_EXT({{Macro "BaseName" $ext}}, §
+    INIT_PROC_EXT({{Macro "BaseName" $ext}}, {{$required}}, §
   {{else}}
-    INIT_PROC(§
+    INIT_PROC({{$required}}, §
   {{end}}
 
   {{if (Macro "IsInstanceDispatched" $)}}
@@ -431,6 +432,25 @@
 
 {{/*
 ------------------------------------------------------------------------------
+  Emits true if a function /must/ be resolved. The only time this is not
+  the case is for extension-added functions added in a later revision of the
+  extension, and where we have to cope with drivers written against an older
+  revision.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsRequiredFunction"}}
+  {{AssertType $ "Function"}}
+
+  {{if eq $.Name "vkGetSwapchainGrallocUsage2ANDROID"}}
+    false
+  {{else}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
   Emits true if a function is exported and instance-dispatched.
 ------------------------------------------------------------------------------
 */}}
@@ -494,9 +514,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)
@@ -681,8 +701,11 @@
 VK_ANDROID_native_buffer
 VK_EXT_debug_report
 VK_KHR_android_surface
+VK_KHR_incremental_present
 VK_KHR_surface
 VK_KHR_swapchain
+VK_GOOGLE_display_timing
+VK_KHR_shared_presentable_image
 {{end}}
 
 
@@ -796,9 +819,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}}
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index f9d3de1..e4e242a 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -467,9 +467,16 @@
                 name = VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
                 ext_bit = ProcHook::ANDROID_native_buffer;
                 break;
+            case ProcHook::GOOGLE_display_timing:
+                hook_extensions_.set(ext_bit);
+                // return now as these extensions do not require HAL support
+                return;
             case ProcHook::EXTENSION_UNKNOWN:
                 // HAL's extensions
                 break;
+            case ProcHook::KHR_shared_presentable_image:
+                // Exposed by HAL, but API surface is all in the loader
+                break;
             default:
                 ALOGW("Ignored invalid device extension %s", name);
                 return;
@@ -487,6 +494,10 @@
             if (ext_bit == ProcHook::ANDROID_native_buffer)
                 hook_extensions_.set(ProcHook::KHR_swapchain);
 
+            // Exposed by HAL, but API surface is all in the loader
+            if (ext_bit == ProcHook::KHR_shared_presentable_image)
+                hook_extensions_.set(ext_bit);
+
             hal_extensions_.set(ext_bit);
         }
 
@@ -725,26 +736,51 @@
     uint32_t* pPropertyCount,
     VkExtensionProperties* pProperties) {
     const InstanceData& data = GetData(physicalDevice);
+    static const std::array<VkExtensionProperties, 2> loader_extensions = {{
+        // WSI extensions
+        {VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
+         VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION},
+        {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;
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 8cbd398..689a228 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -75,6 +75,33 @@
     }
 }
 
+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 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 +247,13 @@
         nullptr,
     },
     {
+        "vkGetPastPresentationTimingGOOGLE",
+        ProcHook::DEVICE,
+        ProcHook::GOOGLE_display_timing,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPastPresentationTimingGOOGLE),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE),
+    },
+    {
         "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
@@ -248,6 +282,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 +310,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,
@@ -295,28 +350,31 @@
     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_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_surface") == 0) return ProcHook::KHR_surface;
     if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
+    if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing;
+    if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image;
     // 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 +384,14 @@
     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, 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);
     // clang-format on
 
     return success;
@@ -346,15 +404,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, true, dev, GetSwapchainGrallocUsageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
     // clang-format on
 
     return success;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index a60b2fe..9f3b705 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -36,8 +36,11 @@
         ANDROID_native_buffer,
         EXT_debug_report,
         KHR_android_surface,
+        KHR_incremental_present,
         KHR_surface,
         KHR_swapchain,
+        GOOGLE_display_timing,
+        KHR_shared_presentable_image,
 
         EXTENSION_CORE,  // valid bit
         EXTENSION_COUNT,
@@ -74,6 +77,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..d1582d1 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -20,6 +20,7 @@
 #include <gui/BufferQueue.h>
 #include <sync/sync.h>
 #include <utils/StrongPointer.h>
+#include <utils/SortedVector.h>
 
 #include "driver.h"
 
@@ -105,6 +106,69 @@
     }
 }
 
+class TimingInfo {
+   public:
+    TimingInfo()
+        : vals_{0, 0, 0, 0, 0},
+          timestamp_desired_present_time_(0),
+          timestamp_actual_present_time_(0),
+          timestamp_render_complete_time_(0),
+          timestamp_composition_latch_time_(0) {}
+    TimingInfo(const VkPresentTimeGOOGLE* qp)
+        : vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0},
+          timestamp_desired_present_time_(0),
+          timestamp_actual_present_time_(0),
+          timestamp_render_complete_time_(0),
+          timestamp_composition_latch_time_(0) {}
+    bool ready() {
+        return (timestamp_desired_present_time_ &&
+                timestamp_actual_present_time_ &&
+                timestamp_render_complete_time_ &&
+                timestamp_composition_latch_time_);
+    }
+    void calculate(uint64_t rdur) {
+        vals_.actualPresentTime = timestamp_actual_present_time_;
+        uint64_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".
+        uint64_t early_time = vals_.actualPresentTime;
+        while ((margin > rdur) &&
+               ((early_time - rdur) > timestamp_composition_latch_time_)) {
+            early_time -= rdur;
+            margin -= rdur;
+        }
+        vals_.earliestPresentTime = early_time;
+        vals_.presentMargin = margin;
+    }
+    void get_values(VkPastPresentationTimingGOOGLE* values) { *values = vals_; }
+
+   public:
+    VkPastPresentationTimingGOOGLE vals_;
+
+    uint64_t timestamp_desired_present_time_;
+    uint64_t timestamp_actual_present_time_;
+    uint64_t timestamp_render_complete_time_;
+    uint64_t timestamp_composition_latch_time_;
+};
+
+static inline int compare_type(const TimingInfo& lhs, const TimingInfo& rhs) {
+    // TODO(ianelliott): Change this from presentID to the frame ID once
+    // brianderson lands the appropriate patch:
+    if (lhs.vals_.presentID < rhs.vals_.presentID)
+        return -1;
+    if (lhs.vals_.presentID > rhs.vals_.presentID)
+        return 1;
+    return 0;
+}
+
 // ----------------------------------------------------------------------------
 
 struct Surface {
@@ -120,12 +184,30 @@
     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_) {}
+        : surface(surface_),
+          num_images(num_images_),
+          frame_timestamps_enabled(false) {
+        timing.clear();
+        ANativeWindow* window = surface.window.get();
+        int64_t rdur;
+        native_window_get_refresh_cycle_duration(
+            window,
+            &rdur);
+        refresh_duration = static_cast<uint64_t>(rdur);
+    }
 
     Surface& surface;
     uint32_t num_images;
+    bool frame_timestamps_enabled;
+    uint64_t refresh_duration;
 
     struct Image {
         Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
@@ -138,6 +220,8 @@
         int dequeue_fence;
         bool dequeued;
     } images[android::BufferQueue::NUM_BUFFER_SLOTS];
+
+    android::SortedVector<TimingInfo> timing;
 };
 
 VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
@@ -205,6 +289,110 @@
             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) {
+    uint32_t num_ready = 0;
+    uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
+    uint32_t frames_ago = num_timings;
+    for (uint32_t i = 0; i < num_timings; i++) {
+        TimingInfo* ti = &swapchain.timing.editItemAt(i);
+        if (ti) {
+            if (ti->ready()) {
+                // This TimingInfo is ready to be reported to the user.  Add it
+                // to the num_ready.
+                num_ready++;
+            } else {
+                // 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;
+                for (uint32_t f = MIN_NUM_FRAMES_AGO; f < frames_ago; f++) {
+                    // Obtain timestamps:
+                    int ret = native_window_get_frame_timestamps(
+                        swapchain.surface.window.get(), f,
+                        &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,  //&display_retire_time,
+                        NULL,  //&dequeue_ready_time,
+                        NULL /*&reads_done_time*/);
+                    if (ret) {
+                        break;
+                    } else if (!ret) {
+                        // We obtained at least one valid timestamp.  See if it
+                        // is for the present represented by this TimingInfo:
+                        if (static_cast<uint64_t>(desired_present_time) ==
+                            ti->vals_.desiredPresentTime) {
+                            // 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_ =
+                                static_cast<uint64_t>(desired_present_time);
+                            ti->timestamp_actual_present_time_ =
+                                static_cast<uint64_t>(actual_present_time);
+                            ti->timestamp_render_complete_time_ =
+                                static_cast<uint64_t>(render_complete_time);
+                            ti->timestamp_composition_latch_time_ =
+                                static_cast<uint64_t>(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++;
+                            }
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return num_ready;
+}
+
+// TODO(ianelliott): DEAL WITH RETURN VALUE (e.g. VK_INCOMPLETE)!!!
+void copy_ready_timings(Swapchain& swapchain,
+                        uint32_t* count,
+                        VkPastPresentationTimingGOOGLE* timings) {
+    uint32_t num_copied = 0;
+    uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
+    if (*count < num_timings) {
+        num_timings = *count;
+    }
+    for (uint32_t i = 0; i < num_timings; i++) {
+        TimingInfo* ti = &swapchain.timing.editItemAt(i);
+        if (ti && ti->ready()) {
+            ti->get_values(&timings[num_copied]);
+            num_copied++;
+            // We only report the values for a given present once, so remove
+            // them from swapchain.timing:
+            //
+            // TODO(ianelliott): SEE WHAT HAPPENS TO THE LOOP WHEN THE
+            // FOLLOWING IS DONE:
+            swapchain.timing.removeAt(i);
+            i--;
+            num_timings--;
+            if (*count == num_copied) {
+                break;
+            }
+        }
+    }
+    *count = num_copied;
 }
 
 }  // anonymous namespace
@@ -327,7 +515,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 =
@@ -376,6 +563,9 @@
                                                  VkPresentModeKHR* modes) {
     const VkPresentModeKHR kModes[] = {
         VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
+        // TODO(chrisforbes): should only expose this if the driver can.
+        VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR,
+        VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR,
     };
     const uint32_t kNumModes = sizeof(kModes) / sizeof(kModes[0]);
 
@@ -423,7 +613,9 @@
              "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);
 
@@ -477,6 +669,20 @@
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
+    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_INITIALIZATION_FAILED;
+    }
+
+    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_INITIALIZATION_FAILED;
+    }
+
     // -- Configure the native window --
 
     const auto& dispatch = GetData(device).driver;
@@ -584,9 +790,36 @@
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
+    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_INITIALIZATION_FAILED;
+        }
+    }
+
+    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_INITIALIZATION_FAILED;
+        }
+    }
+
     int gralloc_usage = 0;
-    // TODO(jessehall): Remove conditional once all drivers have been updated
-    if (dispatch.GetSwapchainGrallocUsageANDROID) {
+    if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+        result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+            device, create_info->imageFormat, create_info->imageUsage,
+            swapchain_image_usage, &gralloc_usage);
+        if (result != VK_SUCCESS) {
+            ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+    } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
         result = dispatch.GetSwapchainGrallocUsageANDROID(
             device, create_info->imageFormat, create_info->imageUsage,
             &gralloc_usage);
@@ -629,12 +862,20 @@
     // -- 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,
@@ -728,6 +969,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)
@@ -862,17 +1106,55 @@
     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) ? &regions[sc] : nullptr;
+        const VkPresentTimeGOOGLE* time = (times) ? &times[sc] : nullptr;
         VkResult swapchain_result = VK_SUCCESS;
         VkResult result;
         int err;
@@ -890,6 +1172,73 @@
             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 this presentID and desiredPresentTime so it can
+                    // be later correlated to this present.
+                    TimingInfo timing_record(time);
+                    swapchain.timing.add(timing_record);
+                    uint32_t num_timings =
+                        static_cast<uint32_t>(swapchain.timing.size());
+                    if (num_timings > 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) {
@@ -920,9 +1269,67 @@
         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 = 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;
+}
+
 }  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h
index 2c60c49..91d7219 100644
--- a/vulkan/libvulkan/swapchain.h
+++ b/vulkan/libvulkan/swapchain.h
@@ -34,6 +34,9 @@
 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);
 // clang-format on
 
 }  // namespace driver
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 4ac994b..55f7f19 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,11 @@
     };
 }
 
+void GetPhysicalDeviceProperties2KHR(VkPhysicalDevice physical_device,
+                                  VkPhysicalDeviceProperties2KHR* properties) {
+    GetPhysicalDeviceProperties(physical_device, &properties->properties);
+}
+
 void GetPhysicalDeviceQueueFamilyProperties(
     VkPhysicalDevice,
     uint32_t* count,
@@ -529,6 +530,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 +551,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 +616,10 @@
     };
 }
 
+void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physical_device, VkPhysicalDeviceFeatures2KHR* features) {
+    GetPhysicalDeviceFeatures(physical_device, &features->features);
+}
+
 // -----------------------------------------------------------------------------
 // Device
 
@@ -887,6 +902,16 @@
     return VK_SUCCESS;
 }
 
+VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice,
+                                          VkFormat,
+                                          VkImageUsageFlags,
+                                          VkSwapchainImageUsageFlagsANDROID,
+                                          int* grallocUsage) {
+    // The null driver never reads or writes the gralloc buffer
+    *grallocUsage = 0;
+    return VK_SUCCESS;
+}
+
 VkResult AcquireImageANDROID(VkDevice,
                              VkImage,
                              int fence,
@@ -1073,11 +1098,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 +1166,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;
@@ -1335,7 +1379,7 @@
 void CmdCopyImageToBuffer(VkCommandBuffer cmdBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer destBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions) {
 }
 
-void CmdUpdateBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+void CmdUpdateBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize dataSize, const void* pData) {
 }
 
 void CmdFillBuffer(VkCommandBuffer cmdBuffer, VkBuffer destBuffer, VkDeviceSize destOffset, VkDeviceSize fillSize, uint32_t data) {
diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl
index 3a84971..ce15517 100644
--- a/vulkan/nulldrv/null_driver.tmpl
+++ b/vulkan/nulldrv/null_driver.tmpl
@@ -158,16 +158,7 @@
 }

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

 } // namespace null_driver
@@ -214,5 +205,6 @@
   {{$ext := index $.Arguments 0}}
   {{     if eq $ext "VK_ANDROID_native_buffer"}}true
   {{else if eq $ext "VK_EXT_debug_report"}}true
+  {{else if eq $ext "VK_KHR_get_physical_device_properties2"}}true
   {{end}}
 {{end}}
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 10da993..7cbbdb1 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -54,6 +54,7 @@
 
 const NameProc kInstanceProcs[] = {
     // clang-format off
+    {"vkAcquireImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAcquireImageANDROID>(AcquireImageANDROID))},
     {"vkAllocateCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateCommandBuffers>(AllocateCommandBuffers))},
     {"vkAllocateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateDescriptorSets>(AllocateDescriptorSets))},
     {"vkAllocateMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateMemory>(AllocateMemory))},
@@ -170,19 +171,29 @@
     {"vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSubresourceLayout>(GetImageSubresourceLayout))},
     {"vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr))},
     {"vkGetPhysicalDeviceFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFeatures>(GetPhysicalDeviceFeatures))},
+    {"vkGetPhysicalDeviceFeatures2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(GetPhysicalDeviceFeatures2KHR))},
     {"vkGetPhysicalDeviceFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFormatProperties>(GetPhysicalDeviceFormatProperties))},
+    {"vkGetPhysicalDeviceFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFormatProperties2KHR>(GetPhysicalDeviceFormatProperties2KHR))},
     {"vkGetPhysicalDeviceImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(GetPhysicalDeviceImageFormatProperties))},
+    {"vkGetPhysicalDeviceImageFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceImageFormatProperties2KHR>(GetPhysicalDeviceImageFormatProperties2KHR))},
     {"vkGetPhysicalDeviceMemoryProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(GetPhysicalDeviceMemoryProperties))},
+    {"vkGetPhysicalDeviceMemoryProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceMemoryProperties2KHR>(GetPhysicalDeviceMemoryProperties2KHR))},
     {"vkGetPhysicalDeviceProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceProperties>(GetPhysicalDeviceProperties))},
+    {"vkGetPhysicalDeviceProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(GetPhysicalDeviceProperties2KHR))},
     {"vkGetPhysicalDeviceQueueFamilyProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(GetPhysicalDeviceQueueFamilyProperties))},
+    {"vkGetPhysicalDeviceQueueFamilyProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR>(GetPhysicalDeviceQueueFamilyProperties2KHR))},
     {"vkGetPhysicalDeviceSparseImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(GetPhysicalDeviceSparseImageFormatProperties))},
+    {"vkGetPhysicalDeviceSparseImageFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR>(GetPhysicalDeviceSparseImageFormatProperties2KHR))},
     {"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
     {"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
     {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+    {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
+    {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
     {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
     {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
     {"vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMergePipelineCaches>(MergePipelineCaches))},
     {"vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueBindSparse>(QueueBindSparse))},
+    {"vkQueueSignalReleaseImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID))},
     {"vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSubmit>(QueueSubmit))},
     {"vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueWaitIdle>(QueueWaitIdle))},
     {"vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetCommandBuffer>(ResetCommandBuffer))},
@@ -206,21 +217,7 @@
 }
 
 PFN_vkVoidFunction GetInstanceProcAddr(const char* name) {
-    PFN_vkVoidFunction pfn;
-    if ((pfn = Lookup(name, kInstanceProcs)))
-        return pfn;
-    if (strcmp(name, "vkGetSwapchainGrallocUsageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(
-            static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(
-                GetSwapchainGrallocUsageANDROID));
-    if (strcmp(name, "vkAcquireImageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(
-            static_cast<PFN_vkAcquireImageANDROID>(AcquireImageANDROID));
-    if (strcmp(name, "vkQueueSignalReleaseImageANDROID") == 0)
-        return reinterpret_cast<PFN_vkVoidFunction>(
-            static_cast<PFN_vkQueueSignalReleaseImageANDROID>(
-                QueueSignalReleaseImageANDROID));
-    return nullptr;
+    return Lookup(name, kInstanceProcs);
 }
 
 }  // namespace null_driver
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 98952b8..cfc14dd 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -145,7 +145,7 @@
 VKAPI_ATTR void CmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter);
 VKAPI_ATTR void CmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions);
 VKAPI_ATTR void CmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions);
-VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData);
+VKAPI_ATTR void CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData);
 VKAPI_ATTR void CmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data);
 VKAPI_ATTR void CmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
 VKAPI_ATTR void CmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges);
@@ -165,9 +165,20 @@
 VKAPI_ATTR void CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents);
 VKAPI_ATTR void CmdEndRenderPass(VkCommandBuffer commandBuffer);
 VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, 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);
 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[] = {