Merge "libbinder: use sysconf(_SC_PAGESIZE) to get pagesize" am: b638bf8d3f am: f83403fdd2
am: 391becb8a7

Change-Id: I11546e02a736bd6c6ece606831462f7ef85c4ec4
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
index 674c163..7e89220 100644
--- a/aidl/gui/android/view/Surface.aidl
+++ b/aidl/gui/android/view/Surface.aidl
@@ -17,4 +17,4 @@
 
 package android.view;
 
-parcelable Surface cpp_header "gui/Surface.h";
+parcelable Surface cpp_header "gui/view/Surface.h";
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
index 69ed416..225de20 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -16,6 +16,9 @@
         "libz",
         "libbase",
     ],
+    static_libs: [
+        "libpdx_default_transport",
+    ],
 
     init_rc: ["atrace.rc"],
 }
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 965a78e..14eff03 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -18,6 +18,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <ftw.h>
 #include <getopt.h>
 #include <inttypes.h>
 #include <signal.h>
@@ -41,6 +42,7 @@
 #include <hidl/ServiceManagement.h>
 #include <cutils/properties.h>
 
+#include <pdx/default_transport/service_utility.h>
 #include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Tokenizer.h>
@@ -48,6 +50,7 @@
 #include <android-base/file.h>
 
 using namespace android;
+using pdx::default_transport::ServiceUtility;
 
 using std::string;
 #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
@@ -383,7 +386,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;
@@ -565,6 +568,46 @@
     }
 }
 
+// Sends the sysprop_change message to the service at fpath, so it re-reads its
+// system properties. Returns 0 on success or a negated errno code on failure.
+static int pokeOnePDXService(const char *fpath, const struct stat * /*sb*/,
+                             int typeflag, struct FTW * /*ftwbuf*/)
+{
+    const bool kIgnoreErrors = true;
+
+    if (typeflag == FTW_F) {
+        int error;
+        auto utility = ServiceUtility::Create(fpath, &error);
+        if (!utility) {
+            if (error != -ECONNREFUSED) {
+                ALOGE("pokeOnePDXService: Failed to open %s, %s.", fpath,
+                      strerror(-error));
+            }
+            return kIgnoreErrors ? 0 : error;
+        }
+
+        auto status = utility->ReloadSystemProperties();
+        if (!status) {
+            ALOGE("pokeOnePDXService: Failed to send sysprop change to %s, "
+                  "error %d, %s.", fpath, status.error(),
+                  status.GetErrorMessage().c_str());
+            return kIgnoreErrors ? 0 : -status.error();
+        }
+    }
+
+    return 0;
+}
+
+// Pokes all the PDX processes in the system to get them to re-read
+// their system properties. Returns true on success, false on failure.
+static bool pokePDXServices()
+{
+    const int kMaxDepth = 16;
+    const int result = nftw(ServiceUtility::GetRootEndpointPath().c_str(),
+                            pokeOnePDXService, kMaxDepth, FTW_PHYS);
+    return result == 0 ? true : false;
+}
+
 // Set the trace tags that userland tracing uses, and poke the running
 // processes to pick up the new value.
 static bool setTagsProperty(uint64_t tags)
@@ -808,6 +851,7 @@
     ok &= setAppCmdlineProperty(&packageList[0]);
     ok &= pokeBinderServices();
     pokeHalServices();
+    ok &= pokePDXServices();
 
     // Disable all the sysfs enables.  This is done as a separate loop from
     // the enables to allow the same enable to exist in multiple categories.
@@ -845,6 +889,7 @@
     setTagsProperty(0);
     clearAppProperties();
     pokeBinderServices();
+    pokePDXServices();
 
     // Set the options back to their defaults.
     setTraceOverwriteEnable(true);
@@ -1036,7 +1081,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..d1e94ed 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,20 +1,181 @@
 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)
+
+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 a0f9b09..1cf00f5 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #define LOG_TAG "dumpstate"
 
 #include <dirent.h>
@@ -36,36 +37,37 @@
 #include <unistd.h>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
+#include <cutils/native_handle.h>
 #include <cutils/properties.h>
-
+#include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
 #include "dumpstate.h"
-#include "ziparchive/zip_writer.h"
 
-#include <openssl/sha.h>
+using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
 
-using android::base::StringPrintf;
+// TODO: remove once moved to namespace
+using android::os::dumpstate::CommandOptions;
+using android::os::dumpstate::DumpFileToFd;
+using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::GetPidByName;
 
 /* read before root is shed */
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = NULL;
 
-// TODO: variables below should be part of dumpstate object
-static unsigned long id;
-static char build_type[PROPERTY_VALUE_MAX];
-static time_t now;
-static std::unique_ptr<ZipWriter> zip_writer;
+// TODO: variables and functions below should be part of dumpstate object
+
 static std::set<std::string> mount_points;
 void add_mountinfo();
-int control_socket_fd = -1;
-/* suffix of the bugreport files - it's typically the date (when invoked with -d),
- * although it could be changed by the user using a system property */
-static std::string suffix;
 
 #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
 #define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
@@ -90,41 +92,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 +156,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 +165,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 +189,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 +212,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 +230,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 +284,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 +300,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 +321,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 +341,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 +357,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 +389,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 +401,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 +446,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 +465,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 +624,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 +646,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 +686,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 +707,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"}, CommandOptions::AS_ROOT);
+
+    RunCommand("PRINTENV", {"printenv"});
+    RunCommand("NETSTAT", {"netstat", "-nW"});
+    struct stat s;
+    if (stat("/proc/modules", &s) != 0) {
+        MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
+    } else {
+        RunCommand("LSMOD", {"lsmod"});
+    }
 
     do_dmesg();
 
-    run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
+    RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
     for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
     for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
     for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
 
     /* Dump Bluetooth HCI logs */
-    add_dir("/data/misc/bluetooth/logs", true);
+    ds.AddDir("/data/misc/bluetooth/logs", true);
 
-    if (!screenshot_path.empty()) {
+    if (!ds.do_early_screenshot_) {
         MYLOGI("taking late screenshot\n");
-        take_screenshot(screenshot_path);
-        MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
+        ds.TakeScreenshot();
     }
 
-    do_logcat();
+    DoLogcat();
 
-    /* show the traces we collected in main(), if that was done */
-    if (dump_traces_path != NULL) {
-        dump_file("VM TRACES JUST NOW", dump_traces_path);
-    }
-
-    /* only show ANR traces if they're less than 15 minutes old */
-    struct stat st;
-    char anr_traces_path[PATH_MAX];
-    property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
-    if (!anr_traces_path[0]) {
-        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
-    } else {
-      int fd = TEMP_FAILURE_RETRY(open(anr_traces_path,
-                                       O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
-      if (fd < 0) {
-          printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
-      } else {
-          dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path, fd);
-      }
-    }
-
-    /* slow traces for slow operations */
-    if (anr_traces_path[0] != 0) {
-        int tail = strlen(anr_traces_path)-1;
-        while (tail > 0 && anr_traces_path[tail] != '/') {
-            tail--;
-        }
-        int i = 0;
-        while (1) {
-            sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
-            if (stat(anr_traces_path, &st)) {
-                // No traces file at this index, done with the files.
-                break;
-            }
-            dump_file("VM TRACES WHEN SLOW", anr_traces_path);
-            i++;
-        }
-    }
+    AddAnrTraceFiles();
 
     int dumped = 0;
     for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -969,8 +996,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 +1011,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");
 
-    run_command("SYSTEM PROPERTIES", 5, "getprop", NULL);
+    RunCommand("SYSTEM PROPERTIES", {"getprop"});
 
-    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", "-v", "all"});
 
     printf("========================================================\n");
     printf("== Running Application Services\n");
     printf("========================================================\n");
 
-    run_command("APP SERVICES", 30, "dumpsys", "-t", "30", "activity", "service", "all", NULL);
+    RunDumpsys("APP SERVICES", {"activity", "service", "all"});
 
     printf("========================================================\n");
     printf("== Running Application Providers\n");
     printf("========================================================\n");
 
-    run_command("APP PROVIDERS", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
+    RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
 
-    // dump_modem_logs adds the modem logs if available to the bugreport.
+    // DumpModemLogs adds the modem logs if available to the bugreport.
     // Do this at the end to allow for sufficient time for the modem logs to be
     // collected.
-    dump_modem_logs();
+    DumpModemLogs();
 
     printf("========================================================\n");
-    printf("== Final progress (pid %d): %d/%d (originally %d)\n",
-            getpid(), progress, weight_total, WEIGHT_TOTAL);
+    printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
+           ds.progress_->GetMax(), ds.progress_->GetInitialMax());
     printf("========================================================\n");
-    printf("== dumpstate: done\n");
+    printf("== dumpstate: done (id %d)\n", ds.id_);
     printf("========================================================\n");
 }
 
-static void usage() {
-  fprintf(stderr,
-          "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-t]"
-          "[-z] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
-          "  -h: display this help message\n"
-          "  -b: play sound file instead of vibrate, at beginning of job\n"
-          "  -e: play sound file instead of vibrate, at end of job\n"
-          "  -o: write to file (instead of stdout)\n"
-          "  -d: append date to filename (requires -o)\n"
-          "  -p: capture screenshot to filename.png (requires -o)\n"
-          "  -t: only captures telephony sections\n"
-          "  -z: generate zipped file (requires -o)\n"
-          "  -s: write output to control socket (for init)\n"
-          "  -S: write file location to control socket (for init; requires -o and -z)"
-          "  -q: disable vibrate\n"
-          "  -B: send broadcast when finished (requires -o)\n"
-          "  -P: send broadcast when started and update system properties on "
-          "progress (requires -o and -B)\n"
-          "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
-          "shouldn't be used with -P)\n"
-          "  -V: sets the bugreport format version (valid values: %s)\n",
-          VERSION_DEFAULT.c_str());
+void Dumpstate::DumpstateBoard() {
+    DurationReporter duration_reporter("dumpstate_board()");
+    printf("========================================================\n");
+    printf("== Board\n");
+    printf("========================================================\n");
+
+    ::android::sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService());
+    if (dumpstate_device == nullptr) {
+        MYLOGE("No IDumpstateDevice implementation\n");
+        return;
+    }
+
+    if (!IsZipping()) {
+        MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
+        return;
+    }
+
+    std::string path = kDumpstateBoardPath;
+    MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path.c_str());
+
+    int fd =
+        TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+    if (fd < 0) {
+        MYLOGE("Could not open file %s: %s\n", path.c_str(), strerror(errno));
+        return;
+    }
+
+    native_handle_t* handle = native_handle_create(1, 0);
+    if (handle == nullptr) {
+        MYLOGE("Could not create native_handle\n");
+        return;
+    }
+    handle->data[0] = fd;
+
+    // TODO: need a timeout mechanism so dumpstate does not hang on device implementation call.
+    android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle);
+    if (!status.isOk()) {
+        MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+        native_handle_close(handle);
+        native_handle_delete(handle);
+        return;
+    }
+
+    AddZipEntry("dumpstate-board.txt", path);
+    printf("*** See dumpstate-board.txt entry ***\n");
+
+    native_handle_close(handle);
+    native_handle_delete(handle);
+
+    if (remove(path.c_str()) != 0) {
+        MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno));
+    }
 }
 
-static void sigpipe_handler(int n) {
-    // don't complain to stderr or stdout
+static void ShowUsageAndExit(int exitCode = 1) {
+    fprintf(stderr,
+            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file] [-d] [-p] "
+            "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
+            "  -h: display this help message\n"
+            "  -b: play sound file instead of vibrate, at beginning of job\n"
+            "  -e: play sound file instead of vibrate, at end of job\n"
+            "  -o: write to file (instead of stdout)\n"
+            "  -d: append date to filename (requires -o)\n"
+            "  -p: capture screenshot to filename.png (requires -o)\n"
+            "  -z: generate zipped file (requires -o)\n"
+            "  -s: write output to control socket (for init)\n"
+            "  -S: write file location to control socket (for init; requires -o and -z)"
+            "  -q: disable vibrate\n"
+            "  -B: send broadcast when finished (requires -o)\n"
+            "  -P: send broadcast when started and update system properties on "
+            "progress (requires -o and -B)\n"
+            "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
+            "shouldn't be used with -P)\n"
+            "  -v: prints the dumpstate header and exit\n");
+    exit(exitCode);
+}
+
+static void ExitOnInvalidArgs() {
+    fprintf(stderr, "invalid combination of args\n");
+    ShowUsageAndExit();
+}
+
+static void sig_handler(int) {
     _exit(EXIT_FAILURE);
 }
 
-/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
-   temporary file.
- */
-static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path,
-        time_t now) {
-    if (!add_zip_entry(bugreport_name, bugreport_path)) {
+static void register_sig_handler() {
+    struct sigaction sa;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    sa.sa_handler = sig_handler;
+    sigaction(SIGPIPE, &sa, NULL); // broken pipe
+    sigaction(SIGSEGV, &sa, NULL); // segment fault
+    sigaction(SIGINT, &sa, NULL); // ctrl-c
+    sigaction(SIGTERM, &sa, NULL); // killed
+    sigaction(SIGQUIT, &sa, NULL); // quit
+}
+
+bool Dumpstate::FinishZipFile() {
+    std::string entry_name = base_name_ + "-" + name_ + ".txt";
+    MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
+           tmp_path_.c_str());
+    // Final timestamp
+    char date[80];
+    time_t the_real_now_please_stand_up = time(nullptr);
+    strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up));
+    MYLOGD("dumpstate id %d finished around %s (%ld s)\n", ds.id_, date,
+           the_real_now_please_stand_up - ds.now_);
+
+    if (!ds.AddZipEntry(entry_name, tmp_path_)) {
         MYLOGE("Failed to add text entry to .zip file\n");
         return false;
     }
-    if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
+    if (!AddTextZipEntry("main_entry.txt", entry_name)) {
         MYLOGE("Failed to add main_entry.txt to .zip file\n");
         return false;
     }
 
-    int32_t err = zip_writer->Finish();
-    if (err) {
-        MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
+    // Add log file (which contains stderr output) to zip...
+    fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
+    if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) {
+        MYLOGE("Failed to add dumpstate log to .zip file\n");
+        return false;
+    }
+    // ... and re-opens it for further logging.
+    redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+    fprintf(stderr, "\n");
+
+    int32_t err = zip_writer_->Finish();
+    if (err != 0) {
+        MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
-    if (is_user_build()) {
-        MYLOGD("Removing temporary file %s\n", bugreport_path.c_str())
-        if (remove(bugreport_path.c_str())) {
-            ALOGW("remove(%s): %s\n", bugreport_path.c_str(), strerror(errno));
-        }
-    } else {
-        MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str())
+    // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
+    ds.zip_file.reset(nullptr);
+
+    MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
+    if (remove(tmp_path_.c_str()) != 0) {
+        MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno));
     }
 
     return true;
@@ -1232,8 +1336,34 @@
     return std::string(hash_buffer);
 }
 
+static void SendBroadcast(const std::string& action, const std::vector<std::string>& args) {
+    // clang-format off
+    std::vector<std::string> am = {"/system/bin/cmd", "activity", "broadcast", "--user", "0",
+                    "--receiver-foreground", "--receiver-include-background", "-a", action};
+    // clang-format on
+
+    am.insert(am.end(), args.begin(), args.end());
+
+    RunCommand("", am,
+               CommandOptions::WithTimeout(20)
+                   .Log("Sending broadcast: '%s'\n")
+                   .Always()
+                   .DropRoot()
+                   .RedirectStderr()
+                   .Build());
+}
+
+static void Vibrate(int duration_ms) {
+    // clang-format off
+    RunCommand("", {"cmd", "vibrator", "vibrate", std::to_string(duration_ms), "dumpstate"},
+               CommandOptions::WithTimeout(10)
+                   .Log("Vibrate: '%s'\n")
+                   .Always()
+                   .Build());
+    // clang-format on
+}
+
 int main(int argc, char *argv[]) {
-    struct sigaction sigact;
     int do_add_date = 0;
     int do_zip_file = 0;
     int do_vibrate = 1;
@@ -1242,33 +1372,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 +1394,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 +1529,74 @@
 
     if (use_control_socket) {
         MYLOGD("Opening control socket\n");
-        control_socket_fd = open_socket("dumpstate");
-        do_update_progress = 1;
+        ds.control_socket_fd_ = open_socket("dumpstate");
+        ds.update_progress_ = 1;
     }
 
-    /* full path of the temporary file containing the bugreport */
-    std::string tmp_path;
-
-    /* full path of the file containing the dumpstate logs*/
-    std::string log_path;
-
-    /* full path of the systrace file, when enabled */
-    std::string systrace_path;
-
-    /* full path of the temporary file containing the screenshot (when requested) */
-    std::string screenshot_path;
-
-    /* base name (without suffix or extensions) of the bugreport files */
-    std::string base_name;
-
-    /* pointer to the actual path, be it zip or text */
-    std::string path;
-
-    /* pointer to the zipped file */
-    std::unique_ptr<FILE, int(*)(FILE*)> zip_file(NULL, fclose);
-
-    /* redirect output if needed */
-    bool is_redirecting = !use_socket && use_outfile;
-
     if (is_redirecting) {
-        bugreport_dir = dirname(use_outfile);
-        base_name = basename(use_outfile);
+        ds.bugreport_dir_ = dirname(use_outfile);
+        std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
+        std::string device_name = android::base::GetProperty("ro.product.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
+
                 std::vector<std::string> am_args = {
-                     "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
-                     "--es", "android.intent.extra.NAME", suffix,
-                     "--ei", "android.intent.extra.ID", std::to_string(id),
-                     "--ei", "android.intent.extra.PID", std::to_string(getpid()),
-                     "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
+                     "--receiver-permission", "android.permission.DUMP",
+                     "--es", "android.intent.extra.NAME", ds.name_,
+                     "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
+                     "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
+                     "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
                 };
                 // clang-format on
-                send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
+                SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
             }
             if (use_control_socket) {
-                dprintf(control_socket_fd, "BEGIN:%s\n", path.c_str());
+                dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str());
             }
         }
     }
@@ -1448,66 +1608,61 @@
         fclose(cmdline);
     }
 
-    /* open the vibrator before dropping root */
-    std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
     if (do_vibrate) {
-        vibrator.reset(fopen("/sys/class/timed_output/vibrator/enable", "we"));
-        if (vibrator) {
-            vibrate(vibrator.get(), 150);
-        }
+        Vibrate(150);
     }
 
-    if (do_fb && do_early_screenshot) {
-        if (screenshot_path.empty()) {
+    if (do_fb && ds.do_early_screenshot_) {
+        if (ds.screenshot_path_.empty()) {
             // should not have happened
             MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
         } else {
             MYLOGI("taking early screenshot\n");
-            take_screenshot(screenshot_path);
-            MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
-            if (chown(screenshot_path.c_str(), AID_SHELL, AID_SHELL)) {
-                MYLOGE("Unable to change ownership of screenshot file %s: %s\n",
-                        screenshot_path.c_str(), strerror(errno));
-            }
+            ds.TakeScreenshot();
         }
     }
 
     if (do_zip_file) {
-        if (chown(path.c_str(), AID_SHELL, AID_SHELL)) {
-            MYLOGE("Unable to change ownership of zip file %s: %s\n", path.c_str(), strerror(errno));
+        if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) {
+            MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(),
+                   strerror(errno));
         }
     }
 
     if (is_redirecting) {
-        redirect_to_file(stderr, const_cast<char*>(log_path.c_str()));
-        if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) {
+        redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+        if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
-                    log_path.c_str(), strerror(errno));
+                   ds.log_path_.c_str(), strerror(errno));
         }
         /* TODO: rather than generating a text file now and zipping it later,
            it would be more efficient to redirect stdout to the zip entry
            directly, but the libziparchive doesn't support that option yet. */
-        redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
-        if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) {
+        redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str()));
+        if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
-                    tmp_path.c_str(), strerror(errno));
+                   ds.tmp_path_.c_str(), strerror(errno));
         }
     }
+
+    // Don't buffer stdout
+    setvbuf(stdout, nullptr, _IONBF, 0);
+
     // NOTE: there should be no stdout output until now, otherwise it would break the header.
     // In particular, DurationReport objects should be created passing 'title, NULL', so their
     // duration is logged into MYLOG instead.
-    print_header(version);
+    ds.PrintHeader();
 
     if (telephony_only) {
-        dump_iptables();
-        if (!drop_root_user()) {
+        DumpIpTables();
+        if (!DropRootUser()) {
             return -1;
         }
         do_dmesg();
-        do_logcat();
-        do_kmsg();
-        dumpstate_board();
-        dump_modem_logs();
+        DoLogcat();
+        DoKmsg();
+        ds.DumpstateBoard();
+        DumpModemLogs();
     } else {
         // Dumps systrace right away, otherwise it will be filled with unnecessary events.
         // First try to dump anrd trace if the daemon is running. Otherwise, dump
@@ -1516,40 +1671,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,125 +1720,126 @@
     if (use_outfile) {
 
         /* check if user changed the suffix using system properties */
-        char key[PROPERTY_KEY_MAX];
-        char value[PROPERTY_VALUE_MAX];
-        snprintf(key, sizeof(key), "dumpstate.%d.name", getpid());
-        property_get(key, value, "");
+        std::string name = android::base::GetProperty(
+            android::base::StringPrintf("dumpstate.%d.name", ds.pid_), "");
         bool change_suffix= false;
-        if (value[0]) {
+        if (!name.empty()) {
             /* must whitelist which characters are allowed, otherwise it could cross directories */
             std::regex valid_regex("^[-_a-zA-Z0-9]+$");
-            if (std::regex_match(value, valid_regex)) {
+            if (std::regex_match(name.c_str(), valid_regex)) {
                 change_suffix = true;
             } else {
-                MYLOGE("invalid suffix provided by user: %s\n", value);
+                MYLOGE("invalid suffix provided by user: %s\n", name.c_str());
             }
         }
         if (change_suffix) {
-            MYLOGI("changing suffix from %s to %s\n", suffix.c_str(), value);
-            suffix = value;
-            if (!screenshot_path.empty()) {
-                std::string new_screenshot_path =
-                        bugreport_dir + "/" + base_name + "-" + suffix + ".png";
-                if (rename(screenshot_path.c_str(), new_screenshot_path.c_str())) {
-                    MYLOGE("rename(%s, %s): %s\n", screenshot_path.c_str(),
-                            new_screenshot_path.c_str(), strerror(errno));
+            MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
+            ds.name_ = name;
+            if (!ds.screenshot_path_.empty()) {
+                std::string new_screenshot_path = ds.GetPath(".png");
+                if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
+                    MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
+                           new_screenshot_path.c_str(), strerror(errno));
                 } else {
-                    screenshot_path = new_screenshot_path;
+                    ds.screenshot_path_ = new_screenshot_path;
                 }
             }
         }
 
         bool do_text_file = true;
         if (do_zip_file) {
-            std::string entry_name = base_name + "-" + suffix + ".txt";
-            MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str());
-            if (!finish_zip_file(entry_name, tmp_path, now)) {
+            if (!ds.FinishZipFile()) {
                 MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
                 do_text_file = true;
             } else {
                 do_text_file = false;
                 // Since zip file is already created, it needs to be renamed.
-                std::string new_path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
-                if (path != new_path) {
-                    MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str());
-                    if (rename(path.c_str(), new_path.c_str())) {
-                        MYLOGE("rename(%s, %s): %s\n", path.c_str(),
-                                new_path.c_str(), strerror(errno));
+                std::string new_path = ds.GetPath(".zip");
+                if (ds.path_ != new_path) {
+                    MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
+                    if (rename(ds.path_.c_str(), new_path.c_str())) {
+                        MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
+                               strerror(errno));
                     } else {
-                        path = new_path;
+                        ds.path_ = new_path;
                     }
                 }
             }
         }
         if (do_text_file) {
-            path = bugreport_dir + "/" + base_name + "-" + suffix + ".txt";
-            MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str());
-            if (rename(tmp_path.c_str(), path.c_str())) {
-                MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno));
-                path.clear();
+            ds.path_ = ds.GetPath(".txt");
+            MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(),
+                   ds.tmp_path_.c_str());
+            if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
+                MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(),
+                       strerror(errno));
+                ds.path_.clear();
             }
         }
         if (use_control_socket) {
             if (do_text_file) {
-                dprintf(control_socket_fd, "FAIL:could not create zip file, check %s "
-                        "for more details\n", log_path.c_str());
+                dprintf(ds.control_socket_fd_,
+                        "FAIL:could not create zip file, check %s "
+                        "for more details\n",
+                        ds.log_path_.c_str());
             } else {
-                dprintf(control_socket_fd, "OK:%s\n", path.c_str());
+                dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
             }
         }
     }
 
     /* vibrate a few but shortly times to let user know it's finished */
-    if (vibrator) {
-        for (int i = 0; i < 3; i++) {
-            vibrate(vibrator.get(), 75);
-            usleep((75 + 50) * 1000);
-        }
+    for (int i = 0; i < 3; i++) {
+        Vibrate(75);
+        usleep((75 + 50) * 1000);
     }
 
     /* tell activity manager we're done */
     if (do_broadcast) {
-        if (!path.empty()) {
-            MYLOGI("Final bugreport path: %s\n", path.c_str());
+        if (!ds.path_.empty()) {
+            MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
             // clang-format off
+
             std::vector<std::string> am_args = {
-                 "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
-                 "--ei", "android.intent.extra.ID", std::to_string(id),
-                 "--ei", "android.intent.extra.PID", std::to_string(getpid()),
-                 "--ei", "android.intent.extra.MAX", std::to_string(weight_total),
-                 "--es", "android.intent.extra.BUGREPORT", path,
-                 "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
+                 "--receiver-permission", "android.permission.DUMP",
+                 "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
+                 "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
+                 "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
+                 "--es", "android.intent.extra.BUGREPORT", ds.path_,
+                 "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
             };
             // clang-format on
             if (do_fb) {
                 am_args.push_back("--es");
                 am_args.push_back("android.intent.extra.SCREENSHOT");
-                am_args.push_back(screenshot_path);
+                am_args.push_back(ds.screenshot_path_);
             }
             if (is_remote_mode) {
                 am_args.push_back("--es");
                 am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
-                am_args.push_back(SHA256_file_hash(path));
-                send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
+                am_args.push_back(SHA256_file_hash(ds.path_));
+                SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED",
+                              am_args);
             } else {
-                send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
+                SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args);
             }
         } else {
             MYLOGE("Skipping finished broadcast because bugreport could not be generated\n");
         }
     }
 
-    MYLOGD("Final progress: %d/%d (originally %d)\n", progress, weight_total, WEIGHT_TOTAL);
-    MYLOGI("done\n");
+    MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(),
+           ds.progress_->GetInitialMax());
+    ds.progress_->Save();
+    MYLOGI("done (id %d)\n", ds.id_);
 
     if (is_redirecting) {
         fclose(stderr);
     }
 
-    if (use_control_socket && control_socket_fd != -1) {
-      MYLOGD("Closing control socket\n");
-      close(control_socket_fd);
+    if (use_control_socket && ds.control_socket_fd_ != -1) {
+        MYLOGD("Closing control socket\n");
+        close(ds.control_socket_fd_);
     }
 
     return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0b6aaab..7fcb980 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,34 +360,8 @@
  * to false when set to NULL. dump_from_fd will always be
  * called with title NULL.
  */
-int dump_files(const char *title, const char *dir,
-        bool (*skip)(const char *path),
-        int (*dump_from_fd)(const char *title, const char *path, int fd));
-
-// TODO: need to refactor all those run_command variations; there shold be just one, receiving an
-// optional CommandOptions objects with values such as run_always, drop_root, etc...
-
-/* forks a command and waits for it to finish -- terminate args with NULL */
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...);
-int run_command(const char *title, int timeout_seconds, const char *command, ...);
-
-enum RootMode { DROP_ROOT, DONT_DROP_ROOT };
-enum StdoutMode { NORMAL_STDOUT, REDIRECT_TO_STDERR };
-
-/* forks a command and waits for it to finish
-   first element of args is the command, and last must be NULL.
-   command is always ran, even when _DUMPSTATE_DRY_RUN_ is defined. */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
-        int timeout_seconds, const char *args[]);
-
-/* switch to non-root user and group */
-bool drop_root_user();
-
-/* sends a broadcast using Activity Manager */
-void send_broadcast(const std::string& action, const std::vector<std::string>& args);
-
-/* updates the overall progress of dumpstate by the given weight increment */
-void update_progress(int weight);
+int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path),
+               int (*dump_from_fd)(const char* title, const char* path, int fd));
 
 /** opens a socket and returns its file descriptor */
 int open_socket(const char *service);
@@ -151,9 +369,12 @@
 /* redirect output to a service control socket */
 void redirect_to_socket(FILE *redirect, const char *service);
 
-/* redirect output to a file */
+/* redirect output to a new file */
 void redirect_to_file(FILE *redirect, char *path);
 
+/* redirect output to an existing file */
+void redirect_to_existing_file(FILE *redirect, char *path);
+
 /* create leading directories, if necessary */
 void create_parent_dirs(const char *path);
 
@@ -184,15 +405,6 @@
 /* Play a sound via Stagefright */
 void play_sound(const char *path);
 
-/* Implemented by libdumpstate_board to dump board-specific info */
-void dumpstate_board();
-
-/* Takes a screenshot and save it to the given file */
-void take_screenshot(const std::string& path);
-
-/* Vibrates for a given durating (in milliseconds). */
-void vibrate(FILE* vibrator, int ms);
-
 /* Checks if a given path is a directory. */
 bool is_dir(const char* pathname);
 
@@ -205,34 +417,8 @@
 /** Gets command-line arguments. */
 void format_args(int argc, const char *argv[], std::string *args);
 
-/** Tells if the device is running a user build. */
-bool is_user_build();
-
-/*
- * Helper class used to report how long it takes for a section to finish.
- *
- * Typical usage:
- *
- *    DurationReporter duration_reporter(title);
- *
- */
-class DurationReporter {
-public:
-    explicit DurationReporter(const char *title);
-    DurationReporter(const char *title, FILE* out);
-
-    ~DurationReporter();
-
-    static uint64_t nanotime();
-
-private:
-    const char* title_;
-    FILE* out_;
-    uint64_t started_;
-};
-
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* _DUMPSTATE_H_ */
+#endif /* FRAMEWORK_NATIVE_CMD_DUMPSTATE_H_ */
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 336db9f..2e72574 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -17,40 +17,3 @@
     class main
     disabled
     oneshot
-
-# bugreportplus is an enhanced version of bugreport that provides a better
-# user interface (like displaying progress and allowing user to enter details).
-# It's typically triggered by the power button or developer settings.
-service bugreportplus /system/bin/dumpstate -d -B -P -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
-    class main
-    disabled
-    oneshot
-
-# bugreportremote is an altered version of bugreport that is supposed to be
-# called not by human user of the device, but by DevicePolicyManagerService only when the
-# Device Owner explicitly requests it, and shared with the Device Policy Controller (DPC) app only
-# if the user consents
-# it will disable vibrations, screenshot taking and will not track progress or
-# allow user to enter any details
-service bugreportremote /system/bin/dumpstate -d -q -B -R -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/remote/bugreport
-    class main
-    disabled
-    oneshot
-
-# bugreportwear is a wearable version of bugreport that displays progress and takes early
-# screenshot.
-service bugreportwear /system/bin/dumpstate -d -B -P -p -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
-    class main
-    disabled
-    oneshot
-
-# bugreportelefony is a lightweight version of bugreport that only includes a few, urgent
-# sections used to report telephony bugs.
-service bugreportelefony /system/bin/dumpstate -t -d -B -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
-    class main
-    disabled
-    oneshot
diff --git a/cmds/dumpstate/libdumpstate_default.cpp b/cmds/dumpstate/libdumpstate_default.cpp
deleted file mode 100644
index fd840df..0000000
--- a/cmds/dumpstate/libdumpstate_default.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "dumpstate.h"
-
-void dumpstate_board(void)
-{
-}
-
diff --git a/cmds/dumpstate/testdata/empty-file.txt b/cmds/dumpstate/testdata/empty-file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/dumpstate/testdata/empty-file.txt
diff --git a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
new file mode 100644
index 0000000..7b7a187
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
diff --git a/cmds/dumpstate/testdata/multiple-lines.txt b/cmds/dumpstate/testdata/multiple-lines.txt
new file mode 100644
index 0000000..bead103
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/single-line-with-newline.txt b/cmds/dumpstate/testdata/single-line-with-newline.txt
new file mode 100644
index 0000000..cb48c82
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line-with-newline.txt
@@ -0,0 +1 @@
+I AM LINE1
diff --git a/cmds/dumpstate/testdata/single-line.txt b/cmds/dumpstate/testdata/single-line.txt
new file mode 100644
index 0000000..2f64046
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line.txt
@@ -0,0 +1 @@
+I AM LINE1
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
new file mode 100644
index 0000000..dad9fe8
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
@@ -0,0 +1 @@
+SIX_SIX_SIX 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
new file mode 100644
index 0000000..4facef9
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
@@ -0,0 +1 @@
+-666 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
new file mode 100644
index 0000000..42508f1
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
@@ -0,0 +1 @@
+4815162342 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
new file mode 100644
index 0000000..a23ba2c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
@@ -0,0 +1 @@
+666 FORTY_TWO
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
new file mode 100644
index 0000000..dd529b4
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
@@ -0,0 +1 @@
+666 -42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
new file mode 100644
index 0000000..b148b46
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
@@ -0,0 +1 @@
+666 4815162342
diff --git a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
new file mode 100644
index 0000000..4a9466d
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
@@ -0,0 +1 @@
+N_RUNS AVERAGE
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
new file mode 100644
index 0000000..0aef60c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
@@ -0,0 +1 @@
+1 10
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-two-runs.txt b/cmds/dumpstate/testdata/stats-two-runs.txt
new file mode 100644
index 0000000..9af1233
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-two-runs.txt
@@ -0,0 +1 @@
+2 15
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
new file mode 100644
index 0000000..1c19268
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -0,0 +1,1157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+#include "DumpstateInternal.h"
+#include "DumpstateService.h"
+#include "android/os/BnDumpstate.h"
+#include "dumpstate.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+using ::testing::EndsWith;
+using ::testing::HasSubstr;
+using ::testing::IsNull;
+using ::testing::IsEmpty;
+using ::testing::NotNull;
+using ::testing::StrEq;
+using ::testing::StartsWith;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class DumpstateListenerMock : public IDumpstateListener {
+  public:
+    MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
+    MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
+
+  protected:
+    MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
+static int calls_;
+
+// Base class for all tests in this file
+class DumpstateBaseTest : public Test {
+  public:
+    virtual void SetUp() override {
+        calls_++;
+        SetDryRun(false);
+    }
+
+    void SetDryRun(bool dry_run) const {
+        PropertiesHelper::dry_run_ = dry_run;
+    }
+
+    void SetBuildType(const std::string& build_type) const {
+        PropertiesHelper::build_type_ = build_type;
+    }
+
+    bool IsStandalone() const {
+        return calls_ == 1;
+    }
+
+    void DropRoot() const {
+        DropRootUser();
+        uid_t uid = getuid();
+        ASSERT_EQ(2000, (int)uid);
+    }
+
+  protected:
+    const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str());
+    const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/";
+    const std::string kTestDataPath = kFixturesPath + "/testdata/";
+    const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture";
+    const std::string kEchoCommand = "/system/bin/echo";
+
+    /*
+     * Copies a text file fixture to a temporary file, returning it's path.
+     *
+     * Useful in cases where the test case changes the content of the tile.
+     */
+    std::string CopyTextFileFixture(const std::string& relative_name) {
+        std::string from = kTestDataPath + relative_name;
+        // Not using TemporaryFile because it's deleted at the end, and it's useful to keep it
+        // around for poking when the test fails.
+        std::string to = kTestDataPath + relative_name + ".tmp";
+        ALOGD("CopyTextFileFixture: from %s to %s\n", from.c_str(), to.c_str());
+        android::base::RemoveFileIfExists(to);
+        CopyTextFile(from, to);
+        return to.c_str();
+    }
+
+    // Need functions that returns void to use assertions -
+    // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#assertion-placement
+    void ReadFileToString(const std::string& path, std::string* content) {
+        ASSERT_TRUE(android::base::ReadFileToString(path, content))
+            << "could not read contents from " << path;
+    }
+    void WriteStringToFile(const std::string& content, const std::string& path) {
+        ASSERT_TRUE(android::base::WriteStringToFile(content, path))
+            << "could not write contents to " << path;
+    }
+
+  private:
+    void CopyTextFile(const std::string& from, const std::string& to) {
+        std::string content;
+        ReadFileToString(from, &content);
+        WriteStringToFile(content, to);
+    }
+};
+
+class DumpstateTest : public DumpstateBaseTest {
+  public:
+    void SetUp() {
+        DumpstateBaseTest::SetUp();
+        SetDryRun(false);
+        SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
+        ds.progress_.reset(new Progress());
+        ds.update_progress_ = false;
+        ds.update_progress_threshold_ = 0;
+    }
+
+    // Runs a command and capture `stdout` and `stderr`.
+    int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+                   const CommandOptions& options = CommandOptions::DEFAULT) {
+        CaptureStdout();
+        CaptureStderr();
+        int status = ds.RunCommand(title, full_command, options);
+        out = GetCapturedStdout();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    // Dumps a file and capture `stdout` and `stderr`.
+    int DumpFile(const std::string& title, const std::string& path) {
+        CaptureStdout();
+        CaptureStderr();
+        int status = ds.DumpFile(title, path);
+        out = GetCapturedStdout();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    void SetProgress(long progress, long initial_max, long threshold = 0) {
+        ds.update_progress_ = true;
+        ds.update_progress_threshold_ = threshold;
+        ds.last_updated_progress_ = 0;
+        ds.progress_.reset(new Progress(initial_max, progress, 1.2));
+    }
+
+    std::string GetProgressMessage(const std::string& listener_name, int progress, int max,
+                                   int old_max = 0, bool update_progress = true) {
+        EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
+        EXPECT_EQ(max, ds.progress_->GetMax()) << "invalid max";
+
+        bool max_increased = old_max > 0;
+
+        std::string message = "";
+        if (max_increased) {
+            message =
+                android::base::StringPrintf("Adjusting max progress from %d to %d\n", old_max, max);
+        }
+
+        if (update_progress) {
+            message += android::base::StringPrintf("Setting progress (%s): %d/%d\n",
+                                                   listener_name.c_str(), progress, max);
+        }
+
+        return message;
+    }
+
+    // `stdout` and `stderr` from the last command ran.
+    std::string out, err;
+
+    Dumpstate& ds = Dumpstate::GetInstance();
+};
+
+TEST_F(DumpstateTest, RunCommandNoArgs) {
+    EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateTest, RunCommandNoTitle) {
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithTitle) {
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out,
+                StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
+    EXPECT_EQ(
+        0, RunCommand("", {kSimpleCommand},
+                      CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandRedirectStderr) {
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
+                            CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithOneArg) {
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandDryRun) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand +
+                                ") ------\n\t(skipped on dry run)\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunAlways) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandNotFound) {
+    EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+    EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+    EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateTest, RunCommandFails) {
+    EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+    EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
+                           " --exit 42' failed: exit code 42\n"));
+    EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
+                           " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandCrashes) {
+    EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
+    // We don't know the exit code, so check just the prefix.
+    EXPECT_THAT(
+        out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+    EXPECT_THAT(
+        err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateTest, RunCommandTimesout) {
+    EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
+                             CommandOptions::WithTimeout(1).Build()));
+    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
+                                " --sleep 2' timed out after 1"));
+    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
+                                " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateTest, RunCommandIsKilled) {
+    CaptureStdout();
+    CaptureStderr();
+
+    std::thread t([=]() {
+        EXPECT_EQ(SIGTERM, ds.RunCommand("", {kSimpleCommand, "--pid", "--sleep", "20"},
+                                         CommandOptions::WithTimeout(100).Always().Build()));
+    });
+
+    // Capture pid and pre-sleep output.
+    sleep(1);  // Wait a little bit to make sure pid and 1st line were printed.
+    std::string err = GetCapturedStderr();
+    EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+    std::string out = GetCapturedStdout();
+    std::vector<std::string> lines = android::base::Split(out, "\n");
+    ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+    int pid = atoi(lines[0].c_str());
+    EXPECT_THAT(lines[1], StrEq("stdout line1"));
+    EXPECT_THAT(lines[2], IsEmpty());  // \n
+
+    // Then kill the process.
+    CaptureStdout();
+    CaptureStderr();
+    ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+    t.join();
+
+    // Finally, check output after murder.
+    out = GetCapturedStdout();
+    err = GetCapturedStderr();
+
+    EXPECT_THAT(out, StrEq("*** command '" + kSimpleCommand +
+                           " --pid --sleep 20' failed: killed by signal 15\n"));
+    EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
+                           " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandProgress) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    ds.listener_ = listener;
+    ds.listener_name_ = "FoxMulder";
+    SetProgress(0, 30);
+
+    EXPECT_CALL(*listener, onProgressUpdated(20));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build()));
+    std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    EXPECT_CALL(*listener, onProgressUpdated(30));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 30, 30);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Run a command that will increase maximum timeout.
+    EXPECT_CALL(*listener, onProgressUpdated(31));
+    EXPECT_CALL(*listener, onMaxProgressUpdated(37));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30);  // 20% increase
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Make sure command ran while in dry_run is counted.
+    SetDryRun(true);
+    EXPECT_CALL(*listener, onProgressUpdated(35));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 35, 37);
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, StrEq(progress_message));
+
+    ds.listener_.clear();
+}
+
+TEST_F(DumpstateTest, RunCommandProgressIgnoreThreshold) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    ds.listener_ = listener;
+    ds.listener_name_ = "FoxMulder";
+    SetProgress(0, 8, 5);  // 8 max, 5 threshold
+
+    // First update should always be sent.
+    EXPECT_CALL(*listener, onProgressUpdated(1));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Fourth update should be ignored because it's between the threshold (5 -1 = 4 < 5).
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+
+    // Third update should be sent because it reaches threshold (6 - 1 = 5).
+    EXPECT_CALL(*listener, onProgressUpdated(6));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 6, 8);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Fourth update should be ignored because it's between the threshold (9 - 6 = 3 < 5).
+    // But max update should be sent.
+    EXPECT_CALL(*listener, onMaxProgressUpdated(10));  // 9 * 120% = 10.8 = 10
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 9, 10, 8, false);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    ds.listener_.clear();
+}
+
+TEST_F(DumpstateTest, RunCommandDropRoot) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateTest.RunCommandDropRoot() on test suite\n")
+        return;
+    }
+    // First check root case - only available when running with 'adb root'.
+    uid_t uid = getuid();
+    if (uid == 0) {
+        EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
+        EXPECT_THAT(out, StrEq("0\nstdout\n"));
+        EXPECT_THAT(err, StrEq("stderr\n"));
+        return;
+    }
+    // Then run dropping root.
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).DropRoot().Build()));
+    EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+    EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateTest.RunCommandAsRootUserBuild() on test suite\n")
+        return;
+    }
+    if (!PropertiesHelper::IsUserBuild()) {
+        // Emulates user build if necessarily.
+        SetBuildType("user");
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    // We don't know the exact path of su, so we just check for the 'root ...' commands
+    EXPECT_THAT(out, StartsWith("Skipping"));
+    EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateTest.RunCommandAsRootNonUserBuild() on test suite\n")
+        return;
+    }
+    if (PropertiesHelper::IsUserBuild()) {
+        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+        return;
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    EXPECT_THAT(out, StrEq("0\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
+    EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(out,
+                StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundWithTitle) {
+    EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(err, IsEmpty());
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No "
+                                "such file or directory\n"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'Y U NO EXIST?' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLine) {
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLineWithNewLine) {
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLines) {
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLinesWithNewLine) {
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRunNoTitle) {
+    SetDryRun(true);
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRun) {
+    SetDryRun(true);
+    EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(
+        out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
+    EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileUpdateProgress) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    ds.listener_ = listener;
+    ds.listener_name_ = "FoxMulder";
+    SetProgress(0, 30);
+
+    EXPECT_CALL(*listener, onProgressUpdated(5));
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+
+    std::string progress_message =
+        GetProgressMessage(ds.listener_name_, 5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
+    EXPECT_THAT(err, StrEq(progress_message));
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
+
+    ds.listener_.clear();
+}
+
+class DumpstateServiceTest : public DumpstateBaseTest {
+  public:
+    DumpstateService dss;
+};
+
+TEST_F(DumpstateServiceTest, SetListenerNoName) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("", listener, &token).isOk());
+    ASSERT_THAT(token, IsNull());
+}
+
+TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk());
+    ASSERT_THAT(token, IsNull());
+}
+
+TEST_F(DumpstateServiceTest, SetListenerTwice) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk());
+    ASSERT_THAT(token, NotNull());
+    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+
+    token.clear();
+    EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk());
+    ASSERT_THAT(token, IsNull());
+    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+}
+
+class ProgressTest : public DumpstateBaseTest {
+  public:
+    Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {
+        return Progress(max, growth_factor, path);
+    }
+
+    void AssertStats(const std::string& path, int32_t expected_runs, int32_t expected_average) {
+        std::string expected_content =
+            android::base::StringPrintf("%d %d\n", expected_runs, expected_average);
+        std::string actual_content;
+        ReadFileToString(path, &actual_content);
+        ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path;
+    }
+};
+
+TEST_F(ProgressTest, SimpleTest) {
+    Progress progress;
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+
+    bool max_increased = progress.Inc(1);
+    EXPECT_EQ(1, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Ignore negative increase.
+    max_increased = progress.Inc(-1);
+    EXPECT_EQ(1, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsInsideNewRange) {
+    Progress progress = GetInstance(10, 1.2);  // 20% growth factor
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(10, progress.GetInitialMax());
+    EXPECT_EQ(10, progress.GetMax());
+
+    // No increase
+    bool max_increased = progress.Inc(10);
+    EXPECT_EQ(10, progress.Get());
+    EXPECT_EQ(10, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Increase, with new value < max*20%
+    max_increased = progress.Inc(1);
+    EXPECT_EQ(11, progress.Get());
+    EXPECT_EQ(13, progress.GetMax());  // 11 average * 20% growth = 13.2 = 13
+    EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsOutsideNewRange) {
+    Progress progress = GetInstance(10, 1.2);  // 20% growth factor
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(10, progress.GetInitialMax());
+    EXPECT_EQ(10, progress.GetMax());
+
+    // No increase
+    bool max_increased = progress.Inc(10);
+    EXPECT_EQ(10, progress.Get());
+    EXPECT_EQ(10, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Increase, with new value > max*20%
+    max_increased = progress.Inc(5);
+    EXPECT_EQ(15, progress.Get());
+    EXPECT_EQ(18, progress.GetMax());  // 15 average * 20% growth = 18
+    EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, InvalidPath) {
+    Progress progress("/devil/null");
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, EmptyFile) {
+    Progress progress(CopyTextFileFixture("empty-file.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLineBothNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-both-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNegative) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-negative.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNegative) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-negative.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryTooBig) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-too-big.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryTooBig) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-too-big.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+// Tests stats are properly saved when the file does not exists.
+TEST_F(ProgressTest, FirstTime) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it's failing when running as suite
+        MYLOGE("Skipping ProgressTest.FirstTime() on test suite\n")
+        return;
+    }
+
+    std::string path = kTestDataPath + "FirstTime.txt";
+    android::base::RemoveFileIfExists(path);
+
+    Progress run1(path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    run1.Save();
+    AssertStats(path, 1, 20);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 1 run.
+// Data on file is 1 run and 109 average.
+TEST_F(ProgressTest, SecondTime) {
+    std::string path = CopyTextFileFixture("stats-one-run-no-newline.txt");
+
+    Progress run1 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(10, run1.GetInitialMax());
+    EXPECT_EQ(10, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(24, run1.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 2 runs and (10 + 20)/ 2 = 15
+    run1.Save();
+    AssertStats(path, 2, 15);
+
+    Progress run2 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run2.Get());
+    EXPECT_EQ(15, run2.GetInitialMax());
+    EXPECT_EQ(15, run2.GetMax());
+
+    max_increased = run2.Inc(25);
+    EXPECT_EQ(25, run2.Get());
+    EXPECT_EQ(30, run2.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 3 runs and (15 * 2 + 25)/ 3 = 18.33 = 18
+    run2.Save();
+    AssertStats(path, 3, 18);
+
+    Progress run3 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run3.Get());
+    EXPECT_EQ(18, run3.GetInitialMax());
+    EXPECT_EQ(18, run3.GetMax());
+
+    // Make sure average decreases as well
+    max_increased = run3.Inc(5);
+    EXPECT_EQ(5, run3.Get());
+    EXPECT_EQ(18, run3.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Average now is 4 runs and (18 * 3 + 5)/ 4 = 14.75 = 14
+    run3.Save();
+    AssertStats(path, 4, 14);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 2 runs.
+// Data on file is 2 runs and 15 average.
+TEST_F(ProgressTest, ThirdTime) {
+    std::string path = CopyTextFileFixture("stats-two-runs.txt");
+    AssertStats(path, 2, 15);  // Sanity check
+
+    Progress run1 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(15, run1.GetInitialMax());
+    EXPECT_EQ(15, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(24, run1.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 3 runs and (15 * 2 + 20)/ 3 = 16.66 = 16
+    run1.Save();
+    AssertStats(path, 3, 16);
+}
+
+class DumpstateUtilTest : public DumpstateBaseTest {
+  public:
+    void SetUp() {
+        DumpstateBaseTest::SetUp();
+        SetDryRun(false);
+    }
+
+    void CaptureFdOut() {
+        ReadFileToString(path_, &out);
+    }
+
+    void CreateFd(const std::string& name) {
+        path_ = kTestDataPath + name;
+        MYLOGD("Creating fd for file %s\n", path_.c_str());
+
+        fd = TEMP_FAILURE_RETRY(open(path_.c_str(),
+                                     O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+        ASSERT_GE(fd, 0) << "could not create FD for path " << path_;
+    }
+
+    // Runs a command into the `fd` and capture `stderr`.
+    int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+                   const CommandOptions& options = CommandOptions::DEFAULT) {
+        CaptureStderr();
+        int status = RunCommandToFd(fd, title, full_command, options);
+        close(fd);
+
+        CaptureFdOut();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    // Dumps a file and into the `fd` and `stderr`.
+    int DumpFile(const std::string& title, const std::string& path) {
+        CaptureStderr();
+        int status = DumpFileToFd(fd, title, path);
+        close(fd);
+
+        CaptureFdOut();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    // Find out the pid of the process_name
+    int FindPidOfProcess(const std::string& process_name) {
+        CaptureStderr();
+        int status = GetPidByName(process_name);
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    int fd;
+
+    // 'fd` output and `stderr` from the last command ran.
+    std::string out, err;
+
+  private:
+    std::string path_;
+};
+
+TEST_F(DumpstateUtilTest, RunCommandNoArgs) {
+    CreateFd("RunCommandNoArgs.txt");
+    EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandNoTitle) {
+    CreateFd("RunCommandWithNoArgs.txt");
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithTitle) {
+    CreateFd("RunCommandWithNoArgs.txt");
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+    EXPECT_THAT(out, StrEq("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithOneArg) {
+    CreateFd("RunCommandWithOneArg.txt");
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithMultipleArgs) {
+    CreateFd("RunCommandWithMultipleArgs.txt");
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandWithLoggingMessage) {
+    CreateFd("RunCommandWithLoggingMessage.txt");
+    EXPECT_EQ(
+        0, RunCommand("", {kSimpleCommand},
+                      CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandRedirectStderr) {
+    CreateFd("RunCommandRedirectStderr.txt");
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
+                            CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRun) {
+    CreateFd("RunCommandDryRun.txt");
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
+    EXPECT_THAT(out, StrEq(android::base::StringPrintf(
+                         "------ I AM GROOT (%s) ------\n\t(skipped on dry run)\n",
+                         kSimpleCommand.c_str())));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRunNoTitle) {
+    CreateFd("RunCommandDryRun.txt");
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
+    EXPECT_THAT(
+        out, StrEq(android::base::StringPrintf("%s: skipped on dry run\n", kSimpleCommand.c_str())));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDryRunAlways) {
+    CreateFd("RunCommandDryRunAlways.txt");
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandNotFound) {
+    CreateFd("RunCommandNotFound.txt");
+    EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+    EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+    EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandFails) {
+    CreateFd("RunCommandFails.txt");
+    EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+    EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
+                           " --exit 42' failed: exit code 42\n"));
+    EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
+                           " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandCrashes) {
+    CreateFd("RunCommandCrashes.txt");
+    EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
+    // We don't know the exit code, so check just the prefix.
+    EXPECT_THAT(
+        out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+    EXPECT_THAT(
+        err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandTimesout) {
+    CreateFd("RunCommandTimesout.txt");
+    EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
+                             CommandOptions::WithTimeout(1).Build()));
+    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
+                                " --sleep 2' timed out after 1"));
+    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
+                                " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandIsKilled) {
+    CreateFd("RunCommandIsKilled.txt");
+    CaptureStderr();
+
+    std::thread t([=]() {
+        EXPECT_EQ(SIGTERM, RunCommandToFd(fd, "", {kSimpleCommand, "--pid", "--sleep", "20"},
+                                          CommandOptions::WithTimeout(100).Always().Build()));
+    });
+
+    // Capture pid and pre-sleep output.
+    sleep(1);  // Wait a little bit to make sure pid and 1st line were printed.
+    std::string err = GetCapturedStderr();
+    EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+    CaptureFdOut();
+    std::vector<std::string> lines = android::base::Split(out, "\n");
+    ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+    int pid = atoi(lines[0].c_str());
+    EXPECT_THAT(lines[1], StrEq("stdout line1"));
+    EXPECT_THAT(lines[2], IsEmpty());  // \n
+
+    // Then kill the process.
+    CaptureFdOut();
+    CaptureStderr();
+    ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+    t.join();
+
+    // Finally, check output after murder.
+    CaptureFdOut();
+    err = GetCapturedStderr();
+
+    // out starts with the pid, which is an unknown
+    EXPECT_THAT(out, EndsWith("stdout line1\n*** command '" + kSimpleCommand +
+                              " --pid --sleep 20' failed: killed by signal 15\n"));
+    EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
+                           " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootUserBuild() on test suite\n")
+        return;
+    }
+    CreateFd("RunCommandAsRootUserBuild.txt");
+    if (!PropertiesHelper::IsUserBuild()) {
+        // Emulates user build if necessarily.
+        SetBuildType("user");
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    // We don't know the exact path of su, so we just check for the 'root ...' commands
+    EXPECT_THAT(out, StartsWith("Skipping"));
+    EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootNonUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootNonUserBuild() on test suite\n")
+        return;
+    }
+    CreateFd("RunCommandAsRootNonUserBuild.txt");
+    if (PropertiesHelper::IsUserBuild()) {
+        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+        return;
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    EXPECT_THAT(out, StrEq("0\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandDropRoot) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateUtilTest.RunCommandDropRoot() on test suite\n")
+        return;
+    }
+    CreateFd("RunCommandDropRoot.txt");
+    // First check root case - only available when running with 'adb root'.
+    uid_t uid = getuid();
+    if (uid == 0) {
+        EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
+        EXPECT_THAT(out, StrEq("0\nstdout\n"));
+        EXPECT_THAT(err, StrEq("stderr\n"));
+        return;
+    }
+    // Then run dropping root.
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).DropRoot().Build()));
+    EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+    EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileNotFoundNoTitle) {
+    CreateFd("DumpFileNotFound.txt");
+    EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(out,
+                StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, DumpFileNotFoundWithTitle) {
+    CreateFd("DumpFileNotFound.txt");
+    EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(out, StrEq("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No such "
+                           "file or directory\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, DumpFileSingleLine) {
+    CreateFd("DumpFileSingleLine.txt");
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateUtilTest, DumpFileSingleLineWithNewLine) {
+    CreateFd("DumpFileSingleLineWithNewLine.txt");
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileMultipleLines) {
+    CreateFd("DumpFileMultipleLines.txt");
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileMultipleLinesWithNewLine) {
+    CreateFd("DumpFileMultipleLinesWithNewLine.txt");
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileOnDryRunNoTitle) {
+    CreateFd("DumpFileOnDryRun.txt");
+    SetDryRun(true);
+    std::string path = kTestDataPath + "single-line.txt";
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq(path + ": skipped on dry run\n"));
+}
+
+TEST_F(DumpstateUtilTest, DumpFileOnDryRun) {
+    CreateFd("DumpFileOnDryRun.txt");
+    SetDryRun(true);
+    std::string path = kTestDataPath + "single-line.txt";
+    EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(
+        out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
+    EXPECT_THAT(out, EndsWith("skipped on dry run\n"));
+}
+
+TEST_F(DumpstateUtilTest, FindingPidWithExistingProcess) {
+    // init process always has pid 1.
+    EXPECT_EQ(1, FindPidOfProcess("init"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateUtilTest, FindingPidWithNotExistingProcess) {
+    // find the process with abnormal name.
+    EXPECT_EQ(-1, FindPidOfProcess("abcdef12345-543"));
+    EXPECT_THAT(err, StrEq("can't find the pid\n"));
+}
+
+}  // namespace dumpstate
+}  // namespace os
+}  // namespace android
diff --git a/cmds/dumpstate/tests/dumpstate_test_fixture.cpp b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
new file mode 100644
index 0000000..5be4719
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+void PrintDefaultOutput() {
+    fprintf(stdout, "stdout\n");
+    fflush(stdout);
+    fprintf(stderr, "stderr\n");
+    fflush(stderr);
+}
+
+/*
+ * Binary used to on RunCommand tests.
+ *
+ * Usage:
+ *
+ * - Unless stated otherwise this command:
+ *
+ *   1.Prints `stdout\n` on `stdout` and flushes it.
+ *   2.Prints `stderr\n` on `stderr` and flushes it.
+ *   3.Exit with status 0.
+ *
+ * - If 1st argument is '--pid', it first prints its pid on `stdout`.
+ *
+ * - If 1st argument is '--uid', it first prints its uid on `stdout`.
+ *
+ * - If 1st argument is '--crash', it uses ALOGF to crash and returns 666.
+ *
+ * - With argument '--exit' 'CODE', returns CODE;
+ *
+ * - With argument '--sleep 'TIME':
+ *
+ *   1.Prints `stdout line1\n` on `stdout` and `sleeping TIME s\n` on `stderr`
+ *   2.Sleeps for TIME s
+ *   3.Prints `stdout line2\n` on `stdout` and `woke up\n` on `stderr`
+ */
+int main(int argc, char* const argv[]) {
+    if (argc == 2) {
+        if (strcmp(argv[1], "--crash") == 0) {
+            PrintDefaultOutput();
+            LOG_FATAL("D'OH\n");
+            return 666;
+        }
+    }
+    if (argc == 3) {
+        if (strcmp(argv[1], "--exit") == 0) {
+            PrintDefaultOutput();
+            return atoi(argv[2]);
+        }
+    }
+
+    if (argc > 1) {
+        int index = 1;
+
+        // First check arguments that can shift the index.
+        if (strcmp(argv[1], "--pid") == 0) {
+            index++;
+            fprintf(stdout, "%d\n", getpid());
+            fflush(stdout);
+        } else if (strcmp(argv[1], "--uid") == 0) {
+            index++;
+            fprintf(stdout, "%d\n", getuid());
+            fflush(stdout);
+        }
+
+        // Then the "common" arguments, if any.
+        if (argc > index + 1) {
+            if (strcmp(argv[index], "--sleep") == 0) {
+                int napTime = atoi(argv[index + 1]);
+                fprintf(stdout, "stdout line1\n");
+                fflush(stdout);
+                fprintf(stderr, "sleeping for %ds\n", napTime);
+                fflush(stderr);
+                sleep(napTime);
+                fprintf(stdout, "stdout line2\n");
+                fflush(stdout);
+                fprintf(stderr, "woke up\n");
+                fflush(stderr);
+                return 0;
+            }
+        }
+    }
+
+    PrintDefaultOutput();
+    return 0;
+}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 6ec636e..4655d33 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,30 @@
         close(fd);
         return -1;
     }
-    return _dump_file_from_fd(title, path, fd);
+    return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun());
 }
 
-bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
-    sigset_t child_mask, old_mask;
-    sigemptyset(&child_mask);
-    sigaddset(&child_mask, SIGCHLD);
-
-    if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
-        printf("*** sigprocmask failed: %s\n", strerror(errno));
-        return false;
-    }
-
-    struct timespec ts;
-    ts.tv_sec = timeout_seconds;
-    ts.tv_nsec = 0;
-    int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
-    int saved_errno = errno;
-    // Set the signals back the way they were.
-    if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
-        printf("*** sigprocmask failed: %s\n", strerror(errno));
-        if (ret == 0) {
-            return false;
-        }
-    }
-    if (ret == -1) {
-        errno = saved_errno;
-        if (errno == EAGAIN) {
-            errno = ETIMEDOUT;
-        } else {
-            printf("*** sigtimedwait failed: %s\n", strerror(errno));
-        }
-        return false;
-    }
-
-    pid_t child_pid = waitpid(pid, status, WNOHANG);
-    if (child_pid != pid) {
-        if (child_pid != -1) {
-            printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
-        } else {
-            printf("*** waitpid failed: %s\n", strerror(errno));
-        }
-        return false;
-    }
-    return true;
-}
-
-// TODO: refactor all those commands that convert args
-void format_args(const char* command, const char *args[], std::string *string);
-
-int run_command(const char *title, int timeout_seconds, const char *command, ...) {
+int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
+                          const CommandOptions& options) {
     DurationReporter duration_reporter(title);
-    fflush(stdout);
 
-    const char *args[1024] = {command};
-    size_t arg;
-    va_list ap;
-    va_start(ap, command);
-    if (title) printf("------ %s (%s", title, command);
-    bool null_terminated = false;
-    for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
-        args[arg] = va_arg(ap, const char *);
-        if (args[arg] == nullptr) {
-            null_terminated = true;
-            break;
-        }
-        // TODO: null_terminated check is not really working; line below would crash dumpstate if
-        // nullptr is missing
-        if (title) printf(" %s", args[arg]);
-    }
-    if (title) printf(") ------\n");
-    fflush(stdout);
-    if (!null_terminated) {
-        // Fail now, otherwise execvp() call on run_command_always() might hang.
-        std::string cmd;
-        format_args(command, args, &cmd);
-        MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
-        return -1;
-    }
+    int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
 
-    ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
+    /* TODO: for now we're simplifying the progress calculation by using the
+     * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
+     * where its weight should be much higher proportionally to its timeout.
+     * Ideally, it should use a options.EstimatedDuration() instead...*/
+    UpdateProgress(options.Timeout());
 
-    int status = run_command_always(title, DONT_DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
-    va_end(ap);
     return status;
 }
 
-int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...) {
-    DurationReporter duration_reporter(title);
-    fflush(stdout);
-
-    const char *args[1024] = {command};
-    size_t arg;
-    va_list ap;
-    va_start(ap, command);
-    if (title) printf("------ %s (%s", title, command);
-    bool null_terminated = false;
-    for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
-        args[arg] = va_arg(ap, const char *);
-        if (args[arg] == nullptr) {
-            null_terminated = true;
-            break;
-        }
-        // TODO: null_terminated check is not really working; line below would crash dumpstate if
-        // nullptr is missing
-        if (title) printf(" %s", args[arg]);
-    }
-    if (title) printf(") ------\n");
-    fflush(stdout);
-    if (!null_terminated) {
-        // Fail now, otherwise execvp() call on run_command_always() might hang.
-        std::string cmd;
-        format_args(command, args, &cmd);
-        MYLOGE("skipping command %s because its args were not NULL-terminated", cmd.c_str());
-        return -1;
-    }
-
-    ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
-
-    int status = run_command_always(title, DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
-    va_end(ap);
-    return status;
-}
-
-/* forks a command and waits for it to finish */
-int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
-        int timeout_seconds, const char *args[]) {
-    bool silent = (stdout_mode == REDIRECT_TO_STDERR);
-    // TODO: need to check if args is null-terminated, otherwise execvp will crash dumpstate
-
-    /* TODO: for now we're simplifying the progress calculation by using the timeout as the weight.
-     * It's a good approximation for most cases, except when calling dumpsys, where its weight
-     * should be much higher proportionally to its timeout. */
-    int weight = timeout_seconds;
-
-    const char *command = args[0];
-    uint64_t start = DurationReporter::nanotime();
-    pid_t pid = fork();
-
-    /* handle error case */
-    if (pid < 0) {
-        if (!silent) printf("*** fork: %s\n", strerror(errno));
-        MYLOGE("*** fork: %s\n", strerror(errno));
-        return pid;
-    }
-
-    /* handle child case */
-    if (pid == 0) {
-        if (root_mode == DROP_ROOT && !drop_root_user()) {
-        if (!silent) printf("*** fail todrop root before running %s: %s\n", command,
-                strerror(errno));
-            MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
-            return -1;
-        }
-
-        if (silent) {
-            // Redirect stderr to stdout
-            dup2(STDERR_FILENO, STDOUT_FILENO);
-        }
-
-        /* make sure the child dies when dumpstate dies */
-        prctl(PR_SET_PDEATHSIG, SIGKILL);
-
-        /* just ignore SIGPIPE, will go down with parent's */
-        struct sigaction sigact;
-        memset(&sigact, 0, sizeof(sigact));
-        sigact.sa_handler = SIG_IGN;
-        sigaction(SIGPIPE, &sigact, NULL);
-
-        execvp(command, (char**) args);
-        // execvp's result will be handled after waitpid_with_timeout() below, but if it failed,
-        // it's safer to exit dumpstate.
-        MYLOGD("execvp on command '%s' failed (error: %s)", command, strerror(errno));
-        fflush(stdout);
-        // Must call _exit (instead of exit), otherwise it will corrupt the zip file.
-        _exit(EXIT_FAILURE);
-    }
-
-    /* handle parent case */
-    int status;
-    bool ret = waitpid_with_timeout(pid, timeout_seconds, &status);
-    uint64_t elapsed = DurationReporter::nanotime() - start;
-    std::string cmd; // used to log command and its args
-    if (!ret) {
-        if (errno == ETIMEDOUT) {
-            format_args(command, args, &cmd);
-            if (!silent) printf("*** command '%s' timed out after %.3fs (killing pid %d)\n",
-            cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
-            MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
-                   (float) elapsed / NANOS_PER_SEC, pid);
-        } else {
-            format_args(command, args, &cmd);
-            if (!silent) printf("*** command '%s': Error after %.4fs (killing pid %d)\n",
-            cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
-            MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
-                   (float) elapsed / NANOS_PER_SEC, pid);
-        }
-        kill(pid, SIGTERM);
-        if (!waitpid_with_timeout(pid, 5, NULL)) {
-            kill(pid, SIGKILL);
-            if (!waitpid_with_timeout(pid, 5, NULL)) {
-                if (!silent) printf("could not kill command '%s' (pid %d) even with SIGKILL.\n",
-                        command, pid);
-                MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
-            }
-        }
-        return -1;
-    } else if (status) {
-        format_args(command, args, &cmd);
-        if (!silent) printf("*** command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
-        MYLOGE("command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
-        return -2;
-    }
-
-    if (WIFSIGNALED(status)) {
-        if (!silent) printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
-        MYLOGE("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
-    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
-        if (!silent) printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
-        MYLOGE("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
-    }
-
-    if (weight > 0) {
-        update_progress(weight);
-    }
-    return status;
-}
-
-bool drop_root_user() {
-    if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
-        MYLOGD("drop_root_user(): already running as Shell");
-        return true;
-    }
-    /* ensure we will keep capabilities when we drop root */
-    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
-        MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
-        return false;
-    }
-
-    gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
-            AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_BLUETOOTH };
-    if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
-        MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
-        return false;
-    }
-    if (setgid(AID_SHELL) != 0) {
-        MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
-        return false;
-    }
-    if (setuid(AID_SHELL) != 0) {
-        MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
-        return false;
-    }
-
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    capheader.pid = 0;
-
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[0].inheritable = 0;
-    capdata[1].inheritable = 0;
-
-    if (capset(&capheader, &capdata[0]) < 0) {
-        MYLOGE("capset failed: %s\n", strerror(errno));
-        return false;
-    }
-
-    return true;
-}
-
-void send_broadcast(const std::string& action, const std::vector<std::string>& args) {
-    if (args.size() > 1000) {
-        MYLOGE("send_broadcast: too many arguments (%d)\n", (int) args.size());
-        return;
-    }
-    const char *am_args[1024] = { "/system/bin/am", "broadcast", "--user", "0", "-a",
-                                  action.c_str() };
-    size_t am_index = 5; // Starts at the index of last initial value above.
-    for (const std::string& arg : args) {
-        am_args[++am_index] = arg.c_str();
-    }
-    // Always terminate with NULL.
-    am_args[am_index + 1] = NULL;
-    std::string args_string;
-    format_args(am_index + 1, am_args, &args_string);
-    MYLOGD("send_broadcast command: %s\n", args_string.c_str());
-    run_command_always(NULL, DROP_ROOT, REDIRECT_TO_STDERR, 20, am_args);
+void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
+                           const CommandOptions& options, long dumpsysTimeout) {
+    long timeout = dumpsysTimeout > 0 ? dumpsysTimeout : options.Timeout();
+    std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-t", std::to_string(timeout)};
+    dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
+    RunCommand(title, dumpsys, options);
 }
 
 int open_socket(const char *service) {
@@ -940,11 +768,11 @@
     }
 }
 
-/* redirect output to a file */
-void redirect_to_file(FILE *redirect, char *path) {
+void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) {
     create_parent_dirs(path);
 
-    int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+    int fd = TEMP_FAILURE_RETRY(open(path,
+                                     O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW,
                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
     if (fd < 0) {
         MYLOGE("%s: %s\n", path, strerror(errno));
@@ -955,6 +783,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)) {
@@ -966,37 +802,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 */
@@ -1017,9 +849,9 @@
         goto error_close_fd;
     }
 
-    wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
+    wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE);
     if (wfd < 0) {
-        MYLOGE("inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
+        MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno));
         goto error_close_ifd;
     }
 
@@ -1052,7 +884,7 @@
             }
 
             ++dalvik_found;
-            uint64_t start = DurationReporter::nanotime();
+            uint64_t start = Nanotime();
             if (kill(pid, SIGQUIT)) {
                 MYLOGE("kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
                 continue;
@@ -1060,7 +892,7 @@
 
             /* wait for the writable-close notification from inotify */
             struct pollfd pfd = { ifd, POLLIN, 0 };
-            int ret = poll(&pfd, 1, 5000);  /* 5 sec timeout */
+            int ret = poll(&pfd, 1, TRACE_DUMP_TIMEOUT_MS);
             if (ret < 0) {
                 MYLOGE("poll: %s\n", strerror(errno));
             } else if (ret == 0) {
@@ -1073,8 +905,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 */
@@ -1082,7 +914,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) {
@@ -1093,8 +925,8 @@
                 } else {
                     timeout_failures = 0;
                 }
-                dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n",
-                        pid, (float)(DurationReporter::nanotime() - start) / NANOS_PER_SEC);
+                dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n", pid,
+                        (float)(Nanotime() - start) / NANOS_PER_SEC);
             }
         }
     }
@@ -1103,17 +935,17 @@
         MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
     }
 
-    static char dump_traces_path[PATH_MAX];
-    strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
-    strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
-    if (rename(traces_path, dump_traces_path)) {
-        MYLOGE("rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
+    static std::string dumptraces_path = android::base::StringPrintf(
+        "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str()));
+    if (rename(traces_path.c_str(), dumptraces_path.c_str())) {
+        MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(),
+               strerror(errno));
         goto error_close_ifd;
     }
-    result = dump_traces_path;
+    result = dumptraces_path.c_str();
 
     /* replace the saved [ANR] traces.txt file */
-    rename(anr_traces_path, traces_path);
+    rename(anrtraces_path.c_str(), traces_path.c_str());
 
 error_close_ifd:
     close(ifd);
@@ -1124,9 +956,9 @@
 
 void dump_route_tables() {
     DurationReporter duration_reporter("DUMP ROUTE TABLES");
-    ON_DRY_RUN_RETURN();
+    if (PropertiesHelper::IsDryRun()) return;
     const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
-    dump_file("RT_TABLES", RT_TABLES_PATH);
+    ds.DumpFile("RT_TABLES", RT_TABLES_PATH);
     FILE* fp = fopen(RT_TABLES_PATH, "re");
     if (!fp) {
         printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno));
@@ -1137,72 +969,67 @@
     // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name.
     // Add a fixed max limit so this doesn't go awry.
     for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) {
-        run_command("ROUTE TABLE IPv4", 10, "ip", "-4", "route", "show", "table", table, NULL);
-        run_command("ROUTE TABLE IPv6", 10, "ip", "-6", "route", "show", "table", table, NULL);
+        RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table});
+        RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table});
     }
     fclose(fp);
 }
 
-/* overall progress */
-int progress = 0;
-int do_update_progress = 0; // Set by dumpstate.cpp
-int weight_total = WEIGHT_TOTAL;
-
 // TODO: make this function thread safe if sections are generated in parallel.
-void update_progress(int delta) {
-    if (!do_update_progress) return;
+void Dumpstate::UpdateProgress(int32_t delta) {
+    if (progress_ == nullptr) {
+        MYLOGE("UpdateProgress: progress_ not set\n");
+        return;
+    }
 
-    progress += delta;
+    // Always update progess so stats can be tuned...
+    bool max_changed = progress_->Inc(delta);
 
-    char key[PROPERTY_KEY_MAX];
-    char value[PROPERTY_VALUE_MAX];
+    // ...but only notifiy listeners when necessary.
+    if (!update_progress_) return;
+
+    int progress = progress_->Get();
+    int max = progress_->GetMax();
 
     // adjusts max on the fly
-    if (progress > weight_total) {
-        int new_total = weight_total * 1.2;
-        MYLOGD("Adjusting total weight from %d to %d\n", weight_total, new_total);
-        weight_total = new_total;
-        snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
-        snprintf(value, sizeof(value), "%d", weight_total);
-        int status = property_set(key, value);
-        if (status) {
-            MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
-                    key, value, status);
+    if (max_changed && listener_ != nullptr) {
+        listener_->onMaxProgressUpdated(max);
+    }
+
+    int32_t last_update_delta = progress - last_updated_progress_;
+    if (last_updated_progress_ > 0 && last_update_delta < update_progress_threshold_) {
+        return;
+    }
+    last_updated_progress_ = progress;
+
+    if (control_socket_fd_ >= 0) {
+        dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max);
+        fsync(control_socket_fd_);
+    }
+
+    if (listener_ != nullptr) {
+        if (progress % 100 == 0) {
+            // We don't want to spam logcat, so only log multiples of 100.
+            MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
+        } else {
+            // stderr is ignored on normal invocations, but useful when calling
+            // /system/bin/dumpstate directly for debuggging.
+            fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
         }
+        listener_->onProgressUpdated(progress);
     }
+}
 
-    snprintf(key, sizeof(key), "dumpstate.%d.progress", getpid());
-    snprintf(value, sizeof(value), "%d", progress);
-
-    if (progress % 100 == 0) {
-        // We don't want to spam logcat, so only log multiples of 100.
-        MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total);
+void Dumpstate::TakeScreenshot(const std::string& path) {
+    const std::string& real_path = path.empty() ? screenshot_path_ : path;
+    int status =
+        RunCommand("", {"/system/bin/screencap", "-p", real_path},
+                   CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+    if (status == 0) {
+        MYLOGD("Screenshot saved on %s\n", real_path.c_str());
     } else {
-        // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
-        // directly for debuggging.
-        fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total);
+        MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
     }
-
-    if (control_socket_fd >= 0) {
-        dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total);
-        fsync(control_socket_fd);
-    }
-
-    int status = property_set(key, value);
-    if (status) {
-        MYLOGE("Could not update progress by setting system property %s to %s: %d\n",
-                key, value, status);
-    }
-}
-
-void take_screenshot(const std::string& path) {
-    const char *args[] = { "/system/bin/screencap", "-p", path.c_str(), NULL };
-    run_command_always(NULL, DONT_DROP_ROOT, REDIRECT_TO_STDERR, 10, args);
-}
-
-void vibrate(FILE* vibrator, int ms) {
-    fprintf(vibrator, "%d\n", ms);
-    fflush(vibrator);
 }
 
 bool is_dir(const char* pathname) {
@@ -1246,19 +1073,16 @@
     int ext_csd_rev = 0;
     std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex));
     if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) {
-        printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n",
-               ext_csd_path, sub.c_str());
+        printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path, sub.c_str());
         return;
     }
 
     static const char *ver_str[] = {
         "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
     };
-    printf("rev 1.%d (MMC %s)\n",
-           ext_csd_rev,
-           (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
-               ver_str[ext_csd_rev] :
-               "Unknown");
+    printf("rev 1.%d (MMC %s)\n", ext_csd_rev,
+           (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev]
+                                                                       : "Unknown");
     if (ext_csd_rev < 7) {
         printf("\n");
         return;
@@ -1272,8 +1096,7 @@
     int ext_pre_eol_info = 0;
     sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex));
     if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) {
-        printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n",
-               ext_csd_path, sub.c_str());
+        printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path, sub.c_str());
         return;
     }
 
@@ -1283,11 +1106,10 @@
         "Warning (consumed 80% of reserve)",
         "Urgent (consumed 90% of reserve)"
     };
-    printf("PRE_EOL_INFO %d (MMC %s)\n",
-           ext_pre_eol_info,
-           eol_str[(ext_pre_eol_info < (int)
-                       (sizeof(eol_str) / sizeof(eol_str[0]))) ?
-                           ext_pre_eol_info : 0]);
+    printf(
+        "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info,
+        eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info
+                                                                                 : 0]);
 
     for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A;
             lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B;
@@ -1316,48 +1138,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/CacheItem.cpp b/cmds/installd/CacheItem.cpp
index d1bdded..6caf149 100644
--- a/cmds/installd/CacheItem.cpp
+++ b/cmds/installd/CacheItem.cpp
@@ -16,8 +16,9 @@
 
 #include "CacheItem.h"
 
-#include <stdint.h>
 #include <inttypes.h>
+#include <stdint.h>
+#include <sys/xattr.h>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
@@ -29,12 +30,23 @@
 namespace android {
 namespace installd {
 
-CacheItem::CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p) : mParent(parent) {
+CacheItem::CacheItem(FTSENT* p) {
     level = p->fts_level;
     directory = S_ISDIR(p->fts_statp->st_mode);
     size = p->fts_statp->st_blocks * 512;
     modified = p->fts_statp->st_mtime;
-    mName = p->fts_path;
+
+    mParent = static_cast<CacheItem*>(p->fts_parent->fts_pointer);
+    if (mParent) {
+        atomic = mParent->atomic;
+        tombstone = mParent->tombstone;
+        mName = p->fts_name;
+        mName.insert(0, "/");
+    } else {
+        atomic = false;
+        tombstone = false;
+        mName = p->fts_path;
+    }
 }
 
 CacheItem::~CacheItem() {
@@ -46,7 +58,7 @@
 
 std::string CacheItem::buildPath() {
     std::string res = mName;
-    std::shared_ptr<CacheItem> parent = mParent;
+    CacheItem* parent = mParent;
     while (parent) {
         res.insert(0, parent->mName);
         parent = parent->mParent;
@@ -57,13 +69,47 @@
 int CacheItem::purge() {
     auto path = buildPath();
     if (directory) {
-        return delete_dir_contents_and_dir(path, true);
-    } else {
-        int res = unlink(path.c_str());
-        if (res != 0) {
-            PLOG(WARNING) << "Failed to unlink " << path;
+        FTS *fts;
+        FTSENT *p;
+        char *argv[] = { (char*) path.c_str(), nullptr };
+        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+            PLOG(WARNING) << "Failed to fts_open " << path;
+            return -1;
         }
-        return res;
+        while ((p = fts_read(fts)) != nullptr) {
+            switch (p->fts_info) {
+            case FTS_D:
+                if (p->fts_level == 0) {
+                    p->fts_number = tombstone;
+                } else {
+                    p->fts_number = p->fts_parent->fts_number
+                            | (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0);
+                }
+                break;
+            case FTS_F:
+                if (p->fts_parent->fts_number) {
+                    truncate(p->fts_path, 0);
+                } else {
+                    unlink(p->fts_path);
+                }
+                break;
+            case FTS_DEFAULT:
+            case FTS_SL:
+            case FTS_SLNONE:
+                unlink(p->fts_path);
+                break;
+            case FTS_DP:
+                rmdir(p->fts_path);
+                break;
+            }
+        }
+        return 0;
+    } else {
+        if (tombstone) {
+            return truncate(path.c_str(), 0);
+        } else {
+            return unlink(path.c_str());
+        }
     }
 }
 
diff --git a/cmds/installd/CacheItem.h b/cmds/installd/CacheItem.h
index bec8bc8..afbb15d 100644
--- a/cmds/installd/CacheItem.h
+++ b/cmds/installd/CacheItem.h
@@ -36,7 +36,7 @@
  */
 class CacheItem {
 public:
-    CacheItem(const std::shared_ptr<CacheItem>& parent, FTSENT* p);
+    CacheItem(FTSENT* p);
     ~CacheItem();
 
     std::string toString();
@@ -46,11 +46,13 @@
 
     short level;
     bool directory;
+    bool atomic;
+    bool tombstone;
     int64_t size;
     time_t modified;
 
 private:
-    std::shared_ptr<CacheItem> mParent;
+    CacheItem* mParent;
     std::string mName;
 
     DISALLOW_COPY_AND_ASSIGN(CacheItem);
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
index 23c4330..9377836 100644
--- a/cmds/installd/CacheTracker.cpp
+++ b/cmds/installd/CacheTracker.cpp
@@ -20,6 +20,7 @@
 
 #include <fts.h>
 #include <sys/quota.h>
+#include <sys/xattr.h>
 #include <utils/Trace.h>
 
 #include <android-base/logging.h>
@@ -86,30 +87,59 @@
         PLOG(WARNING) << "Failed to fts_open " << path;
         return;
     }
-    // TODO: add support for "user.atomic" and "user.tombstone" xattrs
-    while ((p = fts_read(fts)) != NULL) {
+    while ((p = fts_read(fts)) != nullptr) {
+        if (p->fts_level == 0) continue;
+
+        // Create tracking nodes for everything we encounter
         switch (p->fts_info) {
         case FTS_D:
-            // Track the newest mtime of anything inside so we consider
-            // deleting the directory last
-            p->fts_number = p->fts_statp->st_mtime;
-            break;
-        case FTS_DP:
-            p->fts_statp->st_mtime = p->fts_number;
-
-            // Ignore the actual top-level cache directories
-            if (p->fts_level == 0) break;
         case FTS_DEFAULT:
         case FTS_F:
         case FTS_SL:
-        case FTS_SLNONE:
-            // TODO: optimize path memory footprint
-            items.push_back(std::shared_ptr<CacheItem>(new CacheItem(nullptr, p)));
+        case FTS_SLNONE: {
+            auto item = std::shared_ptr<CacheItem>(new CacheItem(p));
+            p->fts_pointer = static_cast<void*>(item.get());
+            items.push_back(item);
+        }
+        }
 
-            // Track the newest modified item under this tree
-            p->fts_parent->fts_number =
-                    std::max(p->fts_parent->fts_number, p->fts_statp->st_mtime);
-            break;
+        switch (p->fts_info) {
+        case FTS_D: {
+            auto item = static_cast<CacheItem*>(p->fts_pointer);
+            item->atomic |= (getxattr(p->fts_path, kXattrCacheAtomic, nullptr, 0) >= 0);
+            item->tombstone |= (getxattr(p->fts_path, kXattrCacheTombstone, nullptr, 0) >= 0);
+
+            // When atomic, immediately collect all files under tree
+            if (item->atomic) {
+                while ((p = fts_read(fts)) != nullptr) {
+                    if (p->fts_info == FTS_DP && p->fts_level == item->level) break;
+                    switch (p->fts_info) {
+                    case FTS_D:
+                    case FTS_DEFAULT:
+                    case FTS_F:
+                    case FTS_SL:
+                    case FTS_SLNONE:
+                        item->size += p->fts_statp->st_blocks * 512;
+                        item->modified = std::max(item->modified, p->fts_statp->st_mtime);
+                    }
+                }
+            }
+        }
+        }
+
+        // Bubble up modified time to parent
+        switch (p->fts_info) {
+        case FTS_DP:
+        case FTS_DEFAULT:
+        case FTS_F:
+        case FTS_SL:
+        case FTS_SLNONE: {
+            auto item = static_cast<CacheItem*>(p->fts_pointer);
+            auto parent = static_cast<CacheItem*>(p->fts_parent->fts_pointer);
+            if (parent) {
+                parent->modified = std::max(parent->modified, item->modified);
+            }
+        }
         }
     }
     fts_close(fts);
@@ -137,7 +167,7 @@
         }
         return left->directory;
     };
-    std::sort(items.begin(), items.end(), cmp);
+    std::stable_sort(items.begin(), items.end(), cmp);
     ATRACE_END();
 }
 
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 29eb6e5..fbb30db 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -72,6 +72,8 @@
 static constexpr const char* kCpPath = "/system/bin/cp";
 static constexpr const char* kXattrDefault = "user.default";
 
+static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
+
 static constexpr const char* PKG_LIB_POSTFIX = "/lib";
 static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
 static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
@@ -84,9 +86,9 @@
 static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
 static constexpr int FLAG_USE_QUOTA = 1 << 12;
 static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
-static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 14;
+static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
+static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15;
 
-#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
 namespace {
 
 constexpr const char* kDump = "android.permission.DUMP";
@@ -832,6 +834,14 @@
         ATRACE_BEGIN("bounce");
         std::shared_ptr<CacheTracker> active;
         while (active || !queue.empty()) {
+            // Only look at apps under quota when explicitly requested
+            if (active && (active->getCacheRatio() < 10000)
+                    && !(flags & FLAG_FREE_CACHE_V2_DEFY_QUOTA)) {
+                LOG(DEBUG) << "Active ratio " << active->getCacheRatio()
+                        << " isn't over quota, and defy not requested";
+                break;
+            }
+
             // Find the best tracker to work with; this might involve swapping
             // if the active tracker is no longer the most over quota
             bool nextBetter = active && !queue.empty()
@@ -889,6 +899,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");
@@ -1818,6 +1832,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) {
@@ -1993,5 +2023,11 @@
     return mQuotaDevices[path];
 }
 
+binder::Status InstalldNativeService::isQuotaSupported(
+        const std::unique_ptr<std::string>& volumeUuid, bool* _aidl_return) {
+    *_aidl_return = !findQuotaDeviceForUuid(volumeUuid).empty();
+    return ok();
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 37e0090..feb2219 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,
@@ -110,6 +111,8 @@
         const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
 
     binder::Status invalidateMounts();
+    binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
+            bool* _aidl_return);
 
 private:
     std::recursive_mutex mLock;
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 4dbfa91..4195a01 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);
@@ -76,4 +77,5 @@
         int storage_flag);
 
     void invalidateMounts();
+    boolean isQuotaSupported(@nullable @utf8InCpp String uuid);
 }
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index d5d5236..240aa49 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,2 +1,103 @@
+
 service installd /system/bin/installd
     class main
+
+on early-boot
+    mkdir /config/sdcardfs/extensions/1055
+    mkdir /config/sdcardfs/extensions/1056
+    mkdir /config/sdcardfs/extensions/1057
+    mkdir /config/sdcardfs/extensions/1056/3gpp
+    mkdir /config/sdcardfs/extensions/1056/3gp
+    mkdir /config/sdcardfs/extensions/1056/3gpp2
+    mkdir /config/sdcardfs/extensions/1056/3g2
+    mkdir /config/sdcardfs/extensions/1056/avi
+    mkdir /config/sdcardfs/extensions/1056/dl
+    mkdir /config/sdcardfs/extensions/1056/dif
+    mkdir /config/sdcardfs/extensions/1056/dv
+    mkdir /config/sdcardfs/extensions/1056/fli
+    mkdir /config/sdcardfs/extensions/1056/m4v
+    mkdir /config/sdcardfs/extensions/1056/ts
+    mkdir /config/sdcardfs/extensions/1056/mpeg
+    mkdir /config/sdcardfs/extensions/1056/mpg
+    mkdir /config/sdcardfs/extensions/1056/mpe
+    mkdir /config/sdcardfs/extensions/1056/mp4
+    mkdir /config/sdcardfs/extensions/1056/vob
+    mkdir /config/sdcardfs/extensions/1056/qt
+    mkdir /config/sdcardfs/extensions/1056/mov
+    mkdir /config/sdcardfs/extensions/1056/mxu
+    mkdir /config/sdcardfs/extensions/1056/webm
+    mkdir /config/sdcardfs/extensions/1056/lsf
+    mkdir /config/sdcardfs/extensions/1056/lsx
+    mkdir /config/sdcardfs/extensions/1056/mkv
+    mkdir /config/sdcardfs/extensions/1056/mng
+    mkdir /config/sdcardfs/extensions/1056/asf
+    mkdir /config/sdcardfs/extensions/1056/asx
+    mkdir /config/sdcardfs/extensions/1056/wm
+    mkdir /config/sdcardfs/extensions/1056/wmv
+    mkdir /config/sdcardfs/extensions/1056/wmx
+    mkdir /config/sdcardfs/extensions/1056/wvx
+    mkdir /config/sdcardfs/extensions/1056/movie
+    mkdir /config/sdcardfs/extensions/1056/wrf
+    mkdir /config/sdcardfs/extensions/1057/bmp
+    mkdir /config/sdcardfs/extensions/1057/gif
+    mkdir /config/sdcardfs/extensions/1057/jpg
+    mkdir /config/sdcardfs/extensions/1057/jpeg
+    mkdir /config/sdcardfs/extensions/1057/jpe
+    mkdir /config/sdcardfs/extensions/1057/pcx
+    mkdir /config/sdcardfs/extensions/1057/png
+    mkdir /config/sdcardfs/extensions/1057/svg
+    mkdir /config/sdcardfs/extensions/1057/svgz
+    mkdir /config/sdcardfs/extensions/1057/tiff
+    mkdir /config/sdcardfs/extensions/1057/tif
+    mkdir /config/sdcardfs/extensions/1057/wbmp
+    mkdir /config/sdcardfs/extensions/1057/webp
+    mkdir /config/sdcardfs/extensions/1057/dng
+    mkdir /config/sdcardfs/extensions/1057/cr2
+    mkdir /config/sdcardfs/extensions/1057/ras
+    mkdir /config/sdcardfs/extensions/1057/art
+    mkdir /config/sdcardfs/extensions/1057/jng
+    mkdir /config/sdcardfs/extensions/1057/nef
+    mkdir /config/sdcardfs/extensions/1057/nrw
+    mkdir /config/sdcardfs/extensions/1057/orf
+    mkdir /config/sdcardfs/extensions/1057/rw2
+    mkdir /config/sdcardfs/extensions/1057/pef
+    mkdir /config/sdcardfs/extensions/1057/psd
+    mkdir /config/sdcardfs/extensions/1057/pnm
+    mkdir /config/sdcardfs/extensions/1057/pbm
+    mkdir /config/sdcardfs/extensions/1057/pgm
+    mkdir /config/sdcardfs/extensions/1057/ppm
+    mkdir /config/sdcardfs/extensions/1057/srw
+    mkdir /config/sdcardfs/extensions/1057/arw
+    mkdir /config/sdcardfs/extensions/1057/rgb
+    mkdir /config/sdcardfs/extensions/1057/xbm
+    mkdir /config/sdcardfs/extensions/1057/xpm
+    mkdir /config/sdcardfs/extensions/1057/xwd
+    mkdir /config/sdcardfs/extensions/1055/aac
+    mkdir /config/sdcardfs/extensions/1055/aac
+    mkdir /config/sdcardfs/extensions/1055/amr
+    mkdir /config/sdcardfs/extensions/1055/awb
+    mkdir /config/sdcardfs/extensions/1055/snd
+    mkdir /config/sdcardfs/extensions/1055/flac
+    mkdir /config/sdcardfs/extensions/1055/flac
+    mkdir /config/sdcardfs/extensions/1055/mp3
+    mkdir /config/sdcardfs/extensions/1055/mpga
+    mkdir /config/sdcardfs/extensions/1055/mpega
+    mkdir /config/sdcardfs/extensions/1055/mp2
+    mkdir /config/sdcardfs/extensions/1055/m4a
+    mkdir /config/sdcardfs/extensions/1055/aif
+    mkdir /config/sdcardfs/extensions/1055/aiff
+    mkdir /config/sdcardfs/extensions/1055/aifc
+    mkdir /config/sdcardfs/extensions/1055/gsm
+    mkdir /config/sdcardfs/extensions/1055/mka
+    mkdir /config/sdcardfs/extensions/1055/m3u
+    mkdir /config/sdcardfs/extensions/1055/wma
+    mkdir /config/sdcardfs/extensions/1055/wax
+    mkdir /config/sdcardfs/extensions/1055/ra
+    mkdir /config/sdcardfs/extensions/1055/rm
+    mkdir /config/sdcardfs/extensions/1055/ram
+    mkdir /config/sdcardfs/extensions/1055/ra
+    mkdir /config/sdcardfs/extensions/1055/pls
+    mkdir /config/sdcardfs/extensions/1055/sd2
+    mkdir /config/sdcardfs/extensions/1055/wav
+    mkdir /config/sdcardfs/extensions/1055/ogg
+    mkdir /config/sdcardfs/extensions/1055/oga
diff --git a/cmds/installd/matchgen.py b/cmds/installd/matchgen.py
index b37352b..131487d 100644
--- a/cmds/installd/matchgen.py
+++ b/cmds/installd/matchgen.py
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import collections
+import collections, sys
 
 TYPES = {
     "AID_MEDIA_AUDIO": ["aac","aac","amr","awb","snd","flac","flac","mp3","mpga","mpega","mp2","m4a","aif","aiff","aifc","gsm","mka","m3u","wma","wax","ra","rm","ram","ra","pls","sd2","wav","ogg","oga"],
@@ -22,6 +22,19 @@
     "AID_MEDIA_IMAGE": ["bmp","gif","jpg","jpeg","jpe","pcx","png","svg","svgz","tiff","tif","wbmp","webp","dng","cr2","ras","art","jng","nef","nrw","orf","rw2","pef","psd","pnm","pbm","pgm","ppm","srw","arw","rgb","xbm","xpm","xwd"]
 }
 
+if "--rc" in sys.argv:
+    print "on early-boot"
+    print "    mkdir /config/sdcardfs/extensions/1055"
+    print "    mkdir /config/sdcardfs/extensions/1056"
+    print "    mkdir /config/sdcardfs/extensions/1057"
+    for gid, exts in TYPES.iteritems():
+        if gid is "AID_MEDIA_AUDIO": gid = "1055"
+        if gid is "AID_MEDIA_VIDEO": gid = "1056"
+        if gid is "AID_MEDIA_IMAGE": gid = "1057"
+        for ext in exts:
+            print "    mkdir /config/sdcardfs/extensions/%s/%s" % (gid, ext)
+    exit()
+
 print """/*
  * Copyright (C) 2017 The Android Open Source Project
  *
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index a32df22..b5b080d 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -14,3 +14,22 @@
         "libdiskusage",
     ],
 }
+
+cc_test {
+    name: "installd_cache_test",
+    clang: true,
+    srcs: ["installd_cache_test.cpp"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "liblogwrap",
+        "libselinux",
+        "libutils",
+    ],
+    static_libs: [
+        "libinstalld",
+        "libdiskusage",
+    ],
+}
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
new file mode 100644
index 0000000..50a47d2
--- /dev/null
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/statvfs.h>
+#include <sys/xattr.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+constexpr const char* kTestUuid = "TEST";
+
+constexpr int64_t kKbInBytes = 1024;
+constexpr int64_t kMbInBytes = 1024 * kKbInBytes;
+constexpr int64_t kGbInBytes = 1024 * kMbInBytes;
+constexpr int64_t kTbInBytes = 1024 * kGbInBytes;
+
+static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13;
+static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
+
+int get_property(const char *key, char *value, const char *default_value) {
+    return property_get(key, value, default_value);
+}
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+        const char *oat_dir ATTRIBUTE_UNUSED,
+        const char *apk_path ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+        const char *apk_path ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
+        const char *src ATTRIBUTE_UNUSED,
+        const char *instruction_set ATTRIBUTE_UNUSED) {
+    return false;
+}
+
+static void mkdir(const char* path) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    ::mkdir(fullPath, 0755);
+}
+
+static void touch(const char* path, int len, int time) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    int fd = ::open(fullPath, O_RDWR | O_CREAT, 0644);
+    ::fallocate(fd, 0, 0, len);
+    ::close(fd);
+    struct utimbuf times;
+    times.actime = times.modtime = std::time(0) + time;
+    ::utime(fullPath, &times);
+}
+
+static int exists(const char* path) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    return ::access(fullPath, F_OK);
+}
+
+static int64_t size(const char* path) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    struct stat buf;
+    if (!stat(fullPath, &buf)) {
+        return buf.st_size;
+    } else {
+        return -1;
+    }
+}
+
+static int64_t free() {
+    struct statvfs buf;
+    if (!statvfs("/data/local/tmp", &buf)) {
+        return buf.f_bavail * buf.f_bsize;
+    } else {
+        return -1;
+    }
+}
+
+static void setxattr(const char* path, const char* key) {
+    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
+    ::setxattr(fullPath, key, "", 0, 0);
+}
+
+class CacheTest : public testing::Test {
+protected:
+    InstalldNativeService* service;
+    std::unique_ptr<std::string> testUuid;
+
+    virtual void SetUp() {
+        setenv("ANDROID_LOG_TAGS", "*:v", 1);
+        android::base::InitLogging(nullptr);
+
+        service = new InstalldNativeService();
+        testUuid = std::make_unique<std::string>();
+        *testUuid = std::string(kTestUuid);
+        system("mkdir -p /data/local/tmp/user/0");
+    }
+
+    virtual void TearDown() {
+        delete service;
+        system("rm -rf /data/local/tmp/user");
+    }
+};
+
+TEST_F(CacheTest, FreeCache_All) {
+    mkdir("com.example");
+    touch("com.example/normal", 1 * kMbInBytes, 60);
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/one", 1 * kMbInBytes, 60);
+    touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+    service->freeCache(testUuid, kTbInBytes,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(0, exists("com.example/normal"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
+}
+
+TEST_F(CacheTest, FreeCache_Age) {
+    mkdir("com.example");
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/one", kMbInBytes, 60);
+    touch("com.example/cache/foo/two", kMbInBytes, 120);
+
+    service->freeCache(testUuid, free() + kKbInBytes,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+    service->freeCache(testUuid, free() + kKbInBytes,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
+}
+
+TEST_F(CacheTest, FreeCache_Tombstone) {
+    mkdir("com.example");
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
+    touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 60);
+    mkdir("com.example/cache/bar");
+    touch("com.example/cache/bar/bar1", 2 * kMbInBytes, 120);
+    touch("com.example/cache/bar/bar2", 2 * kMbInBytes, 120);
+
+    setxattr("com.example/cache/bar", "user.cache_tombstone");
+
+    EXPECT_EQ(0, exists("com.example/cache/foo/foo1"));
+    EXPECT_EQ(0, exists("com.example/cache/foo/foo2"));
+    EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
+    EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
+    EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
+    EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
+
+    service->freeCache(testUuid, kTbInBytes,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
+    EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
+    EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
+    EXPECT_EQ(0, size("com.example/cache/bar/bar1"));
+    EXPECT_EQ(0, size("com.example/cache/bar/bar2"));
+}
+
+TEST_F(CacheTest, FreeCache_Atomic) {
+    mkdir("com.example");
+    mkdir("com.example/cache");
+    mkdir("com.example/cache/foo");
+    touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
+    touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 120);
+
+    setxattr("com.example/cache/foo", "user.cache_atomic");
+
+    service->freeCache(testUuid, free() + kKbInBytes,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
+    EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
+}
+
+TEST_F(CacheTest, FreeCache_AtomicTombstone) {
+    LOG(INFO) << "FreeCache_AtomicTombstone";
+
+    mkdir("com.example");
+    mkdir("com.example/cache");
+
+    // this dir must look really old for some reason?
+    mkdir("com.example/cache/atomic");
+    touch("com.example/cache/atomic/file1", kMbInBytes, 120);
+    touch("com.example/cache/atomic/file2", kMbInBytes, 120);
+    mkdir("com.example/cache/atomic/dir");
+    touch("com.example/cache/atomic/dir/file1", kMbInBytes, 120);
+    touch("com.example/cache/atomic/dir/file2", kMbInBytes, 120);
+    mkdir("com.example/cache/atomic/tomb");
+    touch("com.example/cache/atomic/tomb/file1", kMbInBytes, 120);
+    touch("com.example/cache/atomic/tomb/file2", kMbInBytes, 120);
+    mkdir("com.example/cache/atomic/tomb/dir");
+    touch("com.example/cache/atomic/tomb/dir/file1", kMbInBytes, 120);
+    touch("com.example/cache/atomic/tomb/dir/file2", kMbInBytes, 120);
+
+    mkdir("com.example/cache/tomb");
+    touch("com.example/cache/tomb/file1", kMbInBytes, 240);
+    touch("com.example/cache/tomb/file2", kMbInBytes, 240);
+    mkdir("com.example/cache/tomb/dir");
+    touch("com.example/cache/tomb/dir/file1", kMbInBytes, 240);
+    touch("com.example/cache/tomb/dir/file2", kMbInBytes, 240);
+    mkdir("com.example/cache/tomb/atomic");
+    touch("com.example/cache/tomb/atomic/file1", kMbInBytes, 60);
+    touch("com.example/cache/tomb/atomic/file2", kMbInBytes, 60);
+    mkdir("com.example/cache/tomb/atomic/dir");
+    touch("com.example/cache/tomb/atomic/dir/file1", kMbInBytes, 60);
+    touch("com.example/cache/tomb/atomic/dir/file2", kMbInBytes, 60);
+
+    setxattr("com.example/cache/atomic", "user.cache_atomic");
+    setxattr("com.example/cache/atomic/tomb", "user.cache_tombstone");
+    setxattr("com.example/cache/tomb", "user.cache_tombstone");
+    setxattr("com.example/cache/tomb/atomic", "user.cache_atomic");
+
+    service->freeCache(testUuid, free() + kKbInBytes,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/dir/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/dir/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/tomb/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/tomb/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/tomb/dir/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/atomic/tomb/dir/file2"));
+
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file2"));
+
+    service->freeCache(testUuid, free() + kKbInBytes,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, size("com.example/cache/atomic/file1"));
+    EXPECT_EQ(-1, size("com.example/cache/atomic/file2"));
+    EXPECT_EQ(-1, size("com.example/cache/atomic/dir/file1"));
+    EXPECT_EQ(-1, size("com.example/cache/atomic/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/atomic/tomb/file1"));
+    EXPECT_EQ(0, size("com.example/cache/atomic/tomb/file2"));
+    EXPECT_EQ(0, size("com.example/cache/atomic/tomb/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/atomic/tomb/dir/file2"));
+
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
+    EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file2"));
+
+    service->freeCache(testUuid, kTbInBytes,
+            FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+
+    EXPECT_EQ(-1, size("com.example/cache/atomic/file1"));
+    EXPECT_EQ(-1, size("com.example/cache/atomic/file2"));
+    EXPECT_EQ(-1, size("com.example/cache/atomic/dir/file1"));
+    EXPECT_EQ(-1, size("com.example/cache/atomic/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/atomic/tomb/file1"));
+    EXPECT_EQ(0, size("com.example/cache/atomic/tomb/file2"));
+    EXPECT_EQ(0, size("com.example/cache/atomic/tomb/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/atomic/tomb/dir/file2"));
+
+    EXPECT_EQ(0, size("com.example/cache/tomb/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/dir/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/file2"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file1"));
+    EXPECT_EQ(0, size("com.example/cache/tomb/atomic/dir/file2"));
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 947cc0d..6a8755e 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -29,6 +29,7 @@
 #define TEST_DATA_DIR "/data/"
 #define TEST_APP_DIR "/data/app/"
 #define TEST_APP_PRIVATE_DIR "/data/app-private/"
+#define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/"
 #define TEST_ASEC_DIR "/mnt/asec/"
 #define TEST_EXPAND_DIR "/mnt/expand/"
 
@@ -57,6 +58,9 @@
         android_app_private_dir.path = (char*) TEST_APP_PRIVATE_DIR;
         android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR);
 
+        android_app_ephemeral_dir.path = (char*) TEST_APP_EPHEMERAL_DIR;
+        android_app_ephemeral_dir.len = strlen(TEST_APP_EPHEMERAL_DIR);
+
         android_data_dir.path = (char*) TEST_DATA_DIR;
         android_data_dir.len = strlen(TEST_DATA_DIR);
 
@@ -85,19 +89,19 @@
     // Bad prefixes directories
     const char *badprefix1 = "/etc/passwd";
     EXPECT_EQ(-1, validate_apk_path(badprefix1))
-            << badprefix1 << " should be allowed as a valid path";
+            << badprefix1 << " should not be allowed as a valid path";
 
     const char *badprefix2 = "../.." TEST_APP_DIR "../../../blah";
     EXPECT_EQ(-1, validate_apk_path(badprefix2))
-            << badprefix2 << " should be allowed as a valid path";
+            << badprefix2 << " should not be allowed as a valid path";
 
     const char *badprefix3 = "init.rc";
     EXPECT_EQ(-1, validate_apk_path(badprefix3))
-            << badprefix3 << " should be allowed as a valid path";
+            << badprefix3 << " should not be allowed as a valid path";
 
     const char *badprefix4 = "/init.rc";
     EXPECT_EQ(-1, validate_apk_path(badprefix4))
-            << badprefix4 << " should be allowed as a valid path";
+            << badprefix4 << " should not be allowed as a valid path";
 }
 
 TEST_F(UtilsTest, IsValidApkPath_Internal) {
@@ -317,6 +321,7 @@
     size_t pkgnameSize = PKG_NAME_MAX;
     char pkgname[pkgnameSize + 1];
     memset(pkgname, 'a', pkgnameSize);
+    pkgname[1] = '.';
     pkgname[pkgnameSize] = '\0';
 
     EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0))
@@ -508,5 +513,22 @@
             create_data_user_ce_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10, "com.example"));
 }
 
+TEST_F(UtilsTest, IsValidPackageName) {
+    EXPECT_EQ(true, is_valid_package_name("com.example"));
+    EXPECT_EQ(true, is_valid_package_name("com.example-1"));
+    EXPECT_EQ(true, is_valid_package_name("com.example-1024"));
+    EXPECT_EQ(true, is_valid_package_name("com.example.foo---KiJFj4a_tePVw95pSrjg=="));
+    EXPECT_EQ(true, is_valid_package_name("really_LONG.a1234.package_name"));
+
+    EXPECT_EQ(false, is_valid_package_name("1234.package"));
+    EXPECT_EQ(false, is_valid_package_name("com.1234.package"));
+    EXPECT_EQ(false, is_valid_package_name("package"));
+    EXPECT_EQ(false, is_valid_package_name(""));
+    EXPECT_EQ(false, is_valid_package_name("."));
+    EXPECT_EQ(false, is_valid_package_name("com.example/../com.evil/"));
+    EXPECT_EQ(false, is_valid_package_name("com.example-1/../com.evil/"));
+    EXPECT_EQ(false, is_valid_package_name("/com.evil"));
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 7c40ef7..b379037 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -33,6 +33,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
+#include <cutils/properties.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 
@@ -155,6 +156,9 @@
 std::string create_data_path(const char* volume_uuid) {
     if (volume_uuid == nullptr) {
         return "/data";
+    } else if (!strcmp(volume_uuid, "TEST")) {
+        CHECK(property_get_bool("ro.debuggable", false));
+        return "/data/local/tmp";
     } else {
         CHECK(is_valid_filename(volume_uuid));
         return StringPrintf("/mnt/expand/%s", volume_uuid);
@@ -346,46 +350,42 @@
  * 0 on success.
  */
 bool is_valid_package_name(const std::string& packageName) {
-    const char* pkgname = packageName.c_str();
-    const char *x = pkgname;
-    int alpha = -1;
+    // This logic is borrowed from PackageParser.java
+    bool hasSep = false;
+    bool front = true;
 
-    if (strlen(pkgname) > PKG_NAME_MAX) {
+    auto it = packageName.begin();
+    for (; it != packageName.end() && *it != '-'; it++) {
+        char c = *it;
+        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+            front = false;
+            continue;
+        }
+        if (!front) {
+            if ((c >= '0' && c <= '9') || c == '_') {
+                continue;
+            }
+        }
+        if (c == '.') {
+            hasSep = true;
+            front = true;
+            continue;
+        }
+        LOG(WARNING) << "Bad package character " << c << " in " << packageName;
         return false;
     }
 
-    while (*x) {
-        if (isalnum(*x) || (*x == '_')) {
-                /* alphanumeric or underscore are fine */
-        } else if (*x == '.') {
-            if ((x == pkgname) || (x[1] == '.') || (x[1] == 0)) {
-                    /* periods must not be first, last, or doubled */
-                ALOGE("invalid package name '%s'\n", pkgname);
-                return false;
-            }
-        } else if (*x == '-') {
-            /* Suffix -X is fine to let versioning of packages.
-               But whatever follows should be alphanumeric.*/
-            alpha = 1;
-        } else {
-                /* anything not A-Z, a-z, 0-9, _, or . is invalid */
-            ALOGE("invalid package name '%s'\n", pkgname);
-            return false;
-        }
-
-        x++;
+    if (!hasSep || front) {
+        LOG(WARNING) << "Missing separator in " << packageName;
+        return false;
     }
 
-    if (alpha == 1) {
-        // Skip current character
-        x++;
-        while (*x) {
-            if (!isalnum(*x)) {
-                ALOGE("invalid package name '%s' should include only numbers after -\n", pkgname);
-                return false;
-            }
-            x++;
-        }
+    for (; it != packageName.end(); it++) {
+        char c = *it;
+        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) continue;
+        if ((c >= '0' && c <= '9') || c == '_' || c == '-' || c == '=') continue;
+        LOG(WARNING) << "Bad suffix character " << c << " in " << packageName;
+        return false;
     }
 
     return true;
@@ -999,6 +999,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;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 425b675..d99b445 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -66,6 +66,8 @@
 
 constexpr const char* kXattrInodeCache = "user.inode_cache";
 constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache";
+constexpr const char* kXattrCacheAtomic = "user.cache_atomic";
+constexpr const char* kXattrCacheTombstone = "user.cache_tombstone";
 
 int create_pkg_path(char path[PKG_PATH_MAX],
                     const char *pkgname,
@@ -142,6 +144,8 @@
 
 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);
diff --git a/cmds/surfacereplayer/proto/Android.mk b/cmds/surfacereplayer/proto/Android.mk
new file mode 100644
index 0000000..3cf1148
--- /dev/null
+++ b/cmds/surfacereplayer/proto/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-proto-files-under, src)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+
+LOCAL_MODULE := libtrace_proto
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
new file mode 100644
index 0000000..0bc08a9
--- /dev/null
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -0,0 +1,179 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message Trace {
+    repeated Increment increment = 1;
+}
+
+message Increment {
+    required int64 time_stamp = 1;
+
+    oneof increment {
+        Transaction        transaction          = 2;
+        SurfaceCreation    surface_creation     = 3;
+        SurfaceDeletion    surface_deletion     = 4;
+        BufferUpdate       buffer_update        = 5;
+        VSyncEvent         vsync_event          = 6;
+        DisplayCreation    display_creation     = 7;
+        DisplayDeletion    display_deletion     = 8;
+        PowerModeUpdate    power_mode_update    = 9;
+    }
+}
+
+message Transaction {
+    repeated SurfaceChange surface_change = 1;
+    repeated DisplayChange display_change = 2;
+
+    required bool synchronous = 3;
+    required bool animation   = 4;
+}
+
+message SurfaceChange {
+    required int32 id = 1;
+
+    oneof SurfaceChange {
+        PositionChange              position                = 2;
+        SizeChange                  size                    = 3;
+        AlphaChange                 alpha                   = 4;
+        LayerChange                 layer                   = 5;
+        CropChange                  crop                    = 6;
+        FinalCropChange             final_crop              = 7;
+        MatrixChange                matrix                  = 8;
+        OverrideScalingModeChange   override_scaling_mode   = 9;
+        TransparentRegionHintChange transparent_region_hint = 10;
+        LayerStackChange            layer_stack             = 11;
+        HiddenFlagChange            hidden_flag             = 12;
+        OpaqueFlagChange            opaque_flag             = 13;
+        SecureFlagChange            secure_flag             = 14;
+        DeferredTransactionChange   deferred_transaction    = 15;
+    }
+}
+
+message PositionChange {
+    required float x = 1;
+    required float y = 2;
+}
+
+message SizeChange {
+    required uint32 w = 1;
+    required uint32 h = 2;
+}
+
+message AlphaChange {
+    required float alpha = 1;
+}
+
+message LayerChange {
+    required uint32 layer = 1;
+}
+
+message CropChange {
+    required Rectangle rectangle = 1;
+}
+
+message FinalCropChange {
+    required Rectangle rectangle = 1;
+}
+
+message MatrixChange {
+    required float dsdx = 1;
+    required float dtdx = 2;
+    required float dsdy = 3;
+    required float dtdy = 4;
+}
+
+message OverrideScalingModeChange {
+    required int32 override_scaling_mode = 1;
+}
+
+message TransparentRegionHintChange {
+    repeated Rectangle region = 1;
+}
+
+message LayerStackChange {
+    required uint32 layer_stack = 1;
+}
+
+message HiddenFlagChange {
+    required bool hidden_flag = 1;
+}
+
+message OpaqueFlagChange {
+    required bool opaque_flag = 1;
+}
+
+message SecureFlagChange {
+    required bool secure_flag = 1;
+}
+
+message DeferredTransactionChange {
+    required int32  layer_id     = 1;
+    required uint64 frame_number = 2;
+}
+
+message DisplayChange {
+    required int32 id = 1;
+
+    oneof DisplayChange {
+        DispSurfaceChange surface     = 2;
+        LayerStackChange  layer_stack = 3;
+        SizeChange        size        = 4;
+        ProjectionChange  projection  = 5;
+    }
+}
+
+message DispSurfaceChange {
+    required uint64 buffer_queue_id   = 1;
+    required string buffer_queue_name = 2;
+}
+
+message ProjectionChange {
+    required int32     orientation = 1;
+    required Rectangle viewport    = 2;
+    required Rectangle frame       = 3;
+}
+
+message Rectangle {
+    required int32 left   = 1;
+    required int32 top    = 2;
+    required int32 right  = 3;
+    required int32 bottom = 4;
+}
+
+message SurfaceCreation {
+    required int32  id   = 1;
+    required string name = 2;
+    required uint32 w    = 3;
+    required uint32 h    = 4;
+}
+
+message SurfaceDeletion {
+    required int32 id = 1;
+}
+
+message BufferUpdate {
+    required int32  id           = 1;
+    required uint32 w            = 2;
+    required uint32 h            = 3;
+    required uint64 frame_number = 4;
+}
+
+message VSyncEvent {
+    required int64 when = 1;
+}
+
+message DisplayCreation {
+    required int32     id                = 1;
+    required string    name              = 2;
+    required int32     type              = 3;
+    required bool      is_secure         = 4;
+}
+
+message DisplayDeletion {
+    required int32 id = 1;
+}
+
+message PowerModeUpdate {
+    required int32  id   = 1;
+    required int32  mode = 2;
+}
diff --git a/cmds/surfacereplayer/replayer/Android.mk b/cmds/surfacereplayer/replayer/Android.mk
new file mode 100644
index 0000000..1dd926c
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -0,0 +1,75 @@
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_TARGET_DIR := $(TARGET_OUT_DATA)/local/tmp
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call first-makefiles-under, /frameworks/native/cmds/surfacereplayer/proto)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPPFLAGS := -Weverything -Werror
+LOCAL_CPPFLAGS := -Wno-unused-parameter
+LOCAL_CPPFLAGS := -Wno-format
+
+LOCAL_MODULE := libsurfacereplayer
+
+LOCAL_SRC_FILES := \
+    BufferQueueScheduler.cpp \
+    Event.cpp \
+    Replayer.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+    libEGL \
+    libGLESv2 \
+    libbinder \
+    liblog \
+    libcutils \
+    libgui \
+    libui \
+    libutils \
+    libprotobuf-cpp-lite \
+    libbase \
+    libnativewindow \
+
+LOCAL_STATIC_LIBRARIES := \
+    libtrace_proto \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/..
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := surfacereplayer
+
+LOCAL_SRC_FILES := \
+    Main.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+    libprotobuf-cpp-lite \
+    libsurfacereplayer \
+    libutils \
+    libgui \
+
+LOCAL_STATIC_LIBRARIES := \
+    libtrace_proto \
+
+LOCAL_CPPFLAGS := -Weverything -Werror
+LOCAL_CPPFLAGS := -Wno-unused-parameter
+
+LOCAL_MODULE_PATH := $(LOCAL_TARGET_DIR)
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
new file mode 100644
index 0000000..77de8dc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "BufferQueueScheduler"
+
+#include "BufferQueueScheduler.h"
+
+#include <android/native_window.h>
+#include <gui/Surface.h>
+
+using namespace android;
+
+BufferQueueScheduler::BufferQueueScheduler(
+        const sp<SurfaceControl>& surfaceControl, const HSV& color, int id)
+      : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {}
+
+void BufferQueueScheduler::startScheduling() {
+    ALOGV("Starting Scheduler for %d Layer", mSurfaceId);
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mSurfaceControl == nullptr) {
+        mCondition.wait(lock, [&] { return (mSurfaceControl != nullptr); });
+    }
+
+    while (mContinueScheduling) {
+        while (true) {
+            if (mBufferEvents.empty()) {
+                break;
+            }
+
+            BufferEvent event = mBufferEvents.front();
+            lock.unlock();
+
+            bufferUpdate(event.dimensions);
+            fillSurface(event.event);
+            mColor.modulate();
+            lock.lock();
+            mBufferEvents.pop();
+        }
+        mCondition.wait(lock);
+    }
+}
+
+void BufferQueueScheduler::addEvent(const BufferEvent& event) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mBufferEvents.push(event);
+    mCondition.notify_one();
+}
+
+void BufferQueueScheduler::stopScheduling() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mContinueScheduling = false;
+    mCondition.notify_one();
+}
+
+void BufferQueueScheduler::setSurfaceControl(
+        const sp<SurfaceControl>& surfaceControl, const HSV& color) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mSurfaceControl = surfaceControl;
+    mColor = color;
+    mCondition.notify_one();
+}
+
+void BufferQueueScheduler::bufferUpdate(const Dimensions& dimensions) {
+    sp<Surface> s = mSurfaceControl->getSurface();
+    s->setBuffersDimensions(dimensions.width, dimensions.height);
+}
+
+void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) {
+    ANativeWindow_Buffer outBuffer;
+    sp<Surface> s = mSurfaceControl->getSurface();
+
+    status_t status = s->lock(&outBuffer, nullptr);
+
+    if (status != NO_ERROR) {
+        ALOGE("fillSurface: failed to lock buffer, (%d)", status);
+        return;
+    }
+
+    auto color = mColor.getRGB();
+
+    auto img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+    for (int y = 0; y < outBuffer.height; y++) {
+        for (int x = 0; x < outBuffer.width; x++) {
+            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+            pixel[0] = color.r;
+            pixel[1] = color.g;
+            pixel[2] = color.b;
+            pixel[3] = LAYER_ALPHA;
+        }
+    }
+
+    event->readyToExecute();
+
+    status = s->unlockAndPost();
+
+    ALOGE_IF(status != NO_ERROR, "fillSurface: failed to unlock and post buffer, (%d)", status);
+}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
new file mode 100644
index 0000000..cb20fcc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
+#define ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
+
+#include "Color.h"
+#include "Event.h"
+
+#include <gui/SurfaceControl.h>
+
+#include <utils/StrongPointer.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <utility>
+
+namespace android {
+
+auto constexpr LAYER_ALPHA = 190;
+
+struct Dimensions {
+    Dimensions() = default;
+    Dimensions(int w, int h) : width(w), height(h) {}
+
+    int width = 0;
+    int height = 0;
+};
+
+struct BufferEvent {
+    BufferEvent() = default;
+    BufferEvent(std::shared_ptr<Event> e, Dimensions d) : event(e), dimensions(d) {}
+
+    std::shared_ptr<Event> event;
+    Dimensions dimensions;
+};
+
+class BufferQueueScheduler {
+  public:
+    BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id);
+
+    void startScheduling();
+    void addEvent(const BufferEvent&);
+    void stopScheduling();
+
+    void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color);
+
+  private:
+    void bufferUpdate(const Dimensions& dimensions);
+
+    // Lock and fill the surface, block until the event is signaled by the main loop,
+    // then unlock and post the buffer.
+    void fillSurface(const std::shared_ptr<Event>& event);
+
+    sp<SurfaceControl> mSurfaceControl;
+    HSV mColor;
+    const int mSurfaceId;
+
+    bool mContinueScheduling;
+
+    std::queue<BufferEvent> mBufferEvents;
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+};
+
+}  // namespace android
+#endif
diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h
new file mode 100644
index 0000000..ce644be
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Color.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_COLOR_H
+#define ANDROID_SURFACEREPLAYER_COLOR_H
+
+#include <cmath>
+#include <cstdlib>
+
+namespace android {
+
+constexpr double modulateFactor = .0001;
+constexpr double modulateLimit = .80;
+
+struct RGB {
+    RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {}
+
+    uint8_t r = 0;
+    uint8_t g = 0;
+    uint8_t b = 0;
+};
+
+struct HSV {
+    HSV() = default;
+    HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {}
+
+    double h = 0;
+    double s = 0;
+    double v = 0;
+
+    RGB getRGB() const;
+
+    bool modulateUp = false;
+
+    void modulate();
+};
+
+void inline HSV::modulate() {
+    if(modulateUp) {
+        v += modulateFactor;
+    } else {
+        v -= modulateFactor;
+    }
+
+    if(v <= modulateLimit || v >= 1) {
+        modulateUp = !modulateUp;
+    }
+}
+
+inline RGB HSV::getRGB() const {
+    using namespace std;
+    double r = 0, g = 0, b = 0;
+
+    if (s == 0) {
+        r = v;
+        g = v;
+        b = v;
+    } else {
+        auto tempHue = static_cast<int>(h) % 360;
+        tempHue = tempHue / 60;
+
+        int i = static_cast<int>(trunc(tempHue));
+        double f = h - i;
+
+        double x = v * (1.0 - s);
+        double y = v * (1.0 - (s * f));
+        double z = v * (1.0 - (s * (1.0 - f)));
+
+        switch (i) {
+            case 0:
+                r = v;
+                g = z;
+                b = x;
+                break;
+
+            case 1:
+                r = y;
+                g = v;
+                b = x;
+                break;
+
+            case 2:
+                r = x;
+                g = v;
+                b = z;
+                break;
+
+            case 3:
+                r = x;
+                g = y;
+                b = v;
+                break;
+
+            case 4:
+                r = z;
+                g = x;
+                b = v;
+                break;
+
+            default:
+                r = v;
+                g = x;
+                b = y;
+                break;
+        }
+    }
+
+    return RGB(round(r * 255), round(g * 255), round(b * 255));
+}
+}
+#endif
diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp
new file mode 100644
index 0000000..390d398
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Event.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Event.h"
+
+using namespace android;
+
+Event::Event(Increment::IncrementCase type) : mIncrementType(type) {}
+
+void Event::readyToExecute() {
+    changeState(Event::EventState::Waiting);
+    waitUntil(Event::EventState::Signaled);
+    changeState(Event::EventState::Running);
+}
+
+void Event::complete() {
+    waitUntil(Event::EventState::Waiting);
+    changeState(Event::EventState::Signaled);
+    waitUntil(Event::EventState::Running);
+}
+
+void Event::waitUntil(Event::EventState state) {
+    std::unique_lock<std::mutex> lock(mLock);
+    mCond.wait(lock, [this, state] { return (mState == state); });
+}
+
+void Event::changeState(Event::EventState state) {
+    std::unique_lock<std::mutex> lock(mLock);
+    mState = state;
+    lock.unlock();
+
+    mCond.notify_one();
+}
+
+Increment::IncrementCase Event::getIncrementType() {
+    return mIncrementType;
+}
diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h
new file mode 100644
index 0000000..44b60f5
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Event.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_EVENT_H
+#define ANDROID_SURFACEREPLAYER_EVENT_H
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <condition_variable>
+#include <mutex>
+
+namespace android {
+
+class Event {
+  public:
+    Event(Increment::IncrementCase);
+
+    enum class EventState {
+        SettingUp,  // Completing as much time-independent work as possible
+        Waiting,    // Waiting for signal from main thread to finish execution
+        Signaled,   // Signaled by main thread, about to immediately switch to Running
+        Running     // Finishing execution of rest of work
+    };
+
+    void readyToExecute();
+    void complete();
+
+    Increment::IncrementCase getIncrementType();
+
+  private:
+    void waitUntil(EventState state);
+    void changeState(EventState state);
+
+    std::mutex mLock;
+    std::condition_variable mCond;
+
+    EventState mState = EventState::SettingUp;
+
+    Increment::IncrementCase mIncrementType;
+};
+}
+#endif
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp
new file mode 100644
index 0000000..dd1dd7d
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Main.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Replayer - Main.cpp
+ *
+ * 1. Get flags from command line
+ * 2. Commit actions or settings based on the flags
+ * 3. Initalize a replayer object with the filename passed in
+ * 4. Replay
+ * 5. Exit successfully or print error statement
+ */
+
+#include <replayer/Replayer.h>
+
+#include <csignal>
+#include <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace android;
+
+void printHelpMenu() {
+    std::cout << "SurfaceReplayer options:\n";
+    std::cout << "Usage: surfacereplayer [OPTIONS...] <TRACE FILE>\n";
+    std::cout << "  File path must be absolute" << std::endl << std::endl;
+
+    std::cout << "  -m  Stops the replayer at the start of the trace and switches ";
+                 "to manual replay\n";
+
+    std::cout << "\n  -t [Number of Threads]  Specifies the number of threads to be used while "
+                 "replaying (default is " << android::DEFAULT_THREADS << ")\n";
+
+    std::cout << "\n  -s [Timestamp]  Specify at what timestamp should the replayer switch "
+                 "to manual replay\n";
+
+    std::cout << "  -n  Ignore timestamps and run through trace as fast as possible\n";
+
+    std::cout << "  -l  Indefinitely loop the replayer\n";
+
+    std::cout << "  -h  Display help menu\n";
+
+    std::cout << std::endl;
+}
+
+int main(int argc, char** argv) {
+    std::string filename;
+    bool loop = false;
+    bool wait = true;
+    bool pauseBeginning = false;
+    int numThreads = DEFAULT_THREADS;
+    long stopHere = -1;
+
+    int opt = 0;
+    while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) {
+        switch (opt) {
+            case 'm':
+                pauseBeginning = true;
+                break;
+            case 't':
+                numThreads = atoi(optarg);
+                break;
+            case 's':
+                stopHere = atol(optarg);
+                break;
+            case 'n':
+                wait = false;
+                break;
+            case 'l':
+                loop = true;
+                break;
+            case 'h':
+            case '?':
+                printHelpMenu();
+                exit(0);
+            default:
+                std::cerr << "Invalid argument...exiting" << std::endl;
+                printHelpMenu();
+                exit(0);
+        }
+    }
+
+    char** input = argv + optind;
+    if (input[0] == NULL) {
+        std::cerr << "No trace file provided...exiting" << std::endl;
+        abort();
+    }
+    filename.assign(input[0]);
+
+    status_t status = NO_ERROR;
+    do {
+        android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere);
+        status = r.replay();
+    } while(loop);
+
+    if (status == NO_ERROR) {
+        std::cout << "Successfully finished replaying trace" << std::endl;
+    } else {
+        std::cerr << "Trace replayer returned error: " << status << std::endl;
+    }
+
+    return 0;
+}
diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md
new file mode 100644
index 0000000..893f0dc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/README.md
@@ -0,0 +1,262 @@
+SurfaceReplayer Documentation
+===================
+
+[go/SurfaceReplayer](go/SurfaceReplayer)
+
+SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by
+[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays
+
+* Creation and deletion of surfaces/displays
+* Alterations to the surfaces/displays called Transactions
+* Buffer Updates to surfaces
+* VSync events
+
+At their specified times to be as close to the original trace.
+
+Usage
+--------
+
+###Creating a trace
+
+SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to
+utilize it. To allow it to write to the device, run
+
+`setenforce 0`
+
+To start recording a trace, run
+
+`service call SurfaceFlinger 1020 i32 1`
+
+To stop recording, run
+
+`service call SurfaceFlinger 1020 i32 0`
+
+The default location for the trace is `/data/SurfaceTrace.dat`
+
+###Executable
+
+To replay a specific trace, execute
+
+`/data/local/tmp/surfacereplayer /absolute/path/to/trace`
+
+inside the android shell. This will replay the full trace and then exit. Running this command
+outside of the shell by prepending `adb shell` will not allow for manual control and will not turn
+off VSync injections if it interrupted in any way other than fully replaying the trace
+
+The replay will not fill surfaces with their contents during the capture. Rather they are given a
+random color which will be the same every time the trace is replayed. Surfaces modulate their color
+at buffer updates.
+
+**Options:**
+
+- -m    pause the replayer at the start of the trace for manual replay
+- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3)
+- -s [Timestamp] switches to manual replay at specified timestamp
+- -n    Ignore timestamps and run through trace as fast as possible
+- -l    Indefinitely loop the replayer
+- -h    displays help menu
+
+**Manual Replay:**
+When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled
+by the user. Pressing CTRL-C again will exit the replayer.
+
+Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to
+input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter
+without inputting a command repeats the previous command.
+
+- n  - steps the replayer to the next VSync event
+- ni - steps the replayer to the next increment
+- c  - continues normal replaying
+- c [milliseconds] - continue until specified number of milliseconds have passed
+- s [timestamp]    - continue and stop at specified timestamp
+- l  - list out timestamp of current increment
+- h  - displays help menu
+
+###Shared Library
+
+To use the shared library include these shared libraries
+
+`libsurfacereplayer`
+`libprotobuf-cpp-full`
+`libutils`
+
+And the static library
+
+`libtrace_proto`
+
+Include the replayer header at the top of your file
+
+`#include <replayer/Replayer.h>`
+
+There are two constructors for the replayer
+
+`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)`
+`Replayer(Trace& trace, ... ditto ...)`
+
+The first constructor takes in the filepath where the trace is located and loads in the trace
+object internally.
+- replayManually - **True**: if the replayer will immediately switch to manual replay at the start
+- numThreads - Number of worker threads the replayer will use.
+- wait - **False**: Replayer ignores waits in between increments
+- stopHere - Time stamp of where the replayer should run to then switch to manual replay
+
+The second constructor includes all of the same parameters but takes in a preloaded trace object.
+To use add
+
+`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>`
+
+To your file
+
+After initializing the Replayer call
+
+    replayer.replay();
+
+And the trace will start replaying. Once the trace is finished replaying, the function will return.
+The layers that are visible at the end of the trace will remain on screen until the program
+terminates.
+
+
+**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was
+never executed. This can be fixed by executing
+
+`service call SurfaceFlinger 23 i32 0`
+
+in the android shell
+
+Code Breakdown
+-------------
+
+The Replayer is composed of 5 components.
+
+- The data format of the trace (Trace.proto)
+- The Replayer object (Replayer.cpp)
+- The synchronization mechanism to signal threads within the Replayer (Event.cpp)
+- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp)
+- The Main executable (Main.cpp)
+
+### Traces
+
+Traces are represented as a protobuf message located in surfacereplayer/proto/src.
+
+**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger).
+**Increments** contain the time stamp of when it occurred and a *oneof* which can be a
+
+ - Transaction
+ - SurfaceCreation
+ - SurfaceDeletion
+ - DisplayCreation
+ - DisplayDeleteion
+ - BufferUpdate
+ - VSyncEvent
+ - PowerModeUpdate
+
+**Transactions** contain whether the transaction was synchronous or animated and *repeated*
+**SurfaceChanges** and **DisplayChanges**
+
+- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as
+position, alpha, hidden, size, etc.
+- **DisplayChanges** contain the id of the display being manipulated and can be changes such as
+size, layer stack, projection, etc.
+
+**Surface/Display Creation** contain the id of the surface/display and the name of the
+surface/display
+
+**Surface/Display Deletion** contain the id of the surface/display to be deleted
+
+**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the
+buffer, and the frame number.
+
+**VSyncEvents** contain when the VSync event has occurred.
+
+**PowerModeUpdates** contain the id of the display being updated and what mode it is being
+changed to.
+
+To output the contents of a trace in a readable format, execute
+
+`**aprotoc** --decode=Trace \
+-I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \
+$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \
+ < **YourTraceFile.dat** > **YourOutputName.txt**`
+
+
+###Replayer
+
+Fundamentally the replayer loads a trace and iterates through each increment, waiting the required
+amount of time until the increment should be executed, then executing the increment. The first
+increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and
+goes from there.
+
+Increments from the trace are played asynchronously rather than one by one, being dispatched by
+the main thread, queued up in a thread pool and completed when the main thread deems they are
+ready to finish execution.
+
+When an increment is dispatched, it completes as much work as it can before it has to be
+synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action
+(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event
+object. The main thread holds a queue of these Event objects and completes the
+corresponding Event base on its time stamp. After completing an increment, the main thread will
+dispatch another increment and continue.
+
+The main thread's execution flow is outlined below
+
+    initReplay() //queue up the initial increments
+    while(!pendingIncrements.empty()) { //while increments remaining
+        event = pendingIncrement.pop();
+        wait(event.time_stamp(); //waitUntil it is time to complete this increment
+
+        event.complete() //signal to let event finish
+        if(increments remaing()) {
+            dispatchEvent() //queue up another increment
+        }
+    }
+
+A worker thread's flow looks like so
+
+    //dispatched!
+    Execute non-time sensitive work here
+    ...
+    event.readyToExecute() //time sensitive point...waiting for Main Thread
+    ...
+    Finish execution
+
+
+### Event
+
+An Event is a simple synchronization mechanism used to facilitate communication between the main
+and worker threads. Every time an increment is dispatched, an Event object is also created.
+
+An Event can be in 4 different states:
+
+- **SettingUp** - The worker is in the process of completing all non-time sensitive work
+- **Waiting** - The worker is waiting on the main thread to signal it.
+- **Signaled** - The worker has just been signaled by the main thread
+- **Running** - The worker is running again and finishing the rest of its work.
+
+When the main thread wants to finish the execution of a worker, the worker can either still be
+**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the
+main thread will **Signal** it to complete. The worker thread changes itself to the **Running**
+state once **Signaled**. This last step exists in order to communicate back to the main thread that
+the worker thread has actually started completing its execution, rather than being preempted right
+after signalling. Once this happens, the main thread schedules the next worker. This makes sure
+there is a constant amount of workers running at one time.
+
+This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the
+worker and main thread respectively.
+
+### BufferQueueScheduler
+
+During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a
+buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential
+**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it,
+which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by
+handling when **BufferUpdates** should be scheduled, making sure that they don't overlap.
+
+When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a
+**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one
+every time an Event is completed.
+
+### Main
+
+The main exectuable reads in the command line arguments. Creates the Replayer using those
+arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit
+gracefully, if there are then it will report the error and then exit.
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
new file mode 100644
index 0000000..35b63ec
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -0,0 +1,702 @@
+/* Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceReplayer"
+
+#include "Replayer.h"
+
+#include <android/native_window.h>
+
+#include <android-base/file.h>
+
+#include <gui/BufferQueue.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+
+#include <ui/DisplayInfo.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include <chrono>
+#include <cmath>
+#include <condition_variable>
+#include <cstdlib>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+
+using namespace android;
+
+std::atomic_bool Replayer::sReplayingManually(false);
+
+Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads, bool wait,
+        nsecs_t stopHere)
+      : mTrace(),
+        mLoaded(false),
+        mIncrementIndex(0),
+        mCurrentTime(0),
+        mNumThreads(numThreads),
+        mWaitForTimeStamps(wait),
+        mStopTimeStamp(stopHere) {
+    srand(RAND_COLOR_SEED);
+
+    std::string input;
+    if (!android::base::ReadFileToString(filename, &input, true)) {
+        std::cerr << "Trace did not load. Does " << filename << " exist?" << std::endl;
+        abort();
+    }
+
+    mLoaded = mTrace.ParseFromString(input);
+    if (!mLoaded) {
+        std::cerr << "Trace did not load." << std::endl;
+        abort();
+    }
+
+    mCurrentTime = mTrace.increment(0).time_stamp();
+
+    sReplayingManually.store(replayManually);
+
+    if (stopHere < 0) {
+        mHasStopped = true;
+    }
+}
+
+Replayer::Replayer(const Trace& t, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)
+      : mTrace(t),
+        mLoaded(true),
+        mIncrementIndex(0),
+        mCurrentTime(0),
+        mNumThreads(numThreads),
+        mWaitForTimeStamps(wait),
+        mStopTimeStamp(stopHere) {
+    srand(RAND_COLOR_SEED);
+    mCurrentTime = mTrace.increment(0).time_stamp();
+
+    sReplayingManually.store(replayManually);
+
+    if (stopHere < 0) {
+        mHasStopped = true;
+    }
+}
+
+status_t Replayer::replay() {
+    signal(SIGINT, Replayer::stopAutoReplayHandler); //for manual control
+
+    ALOGV("There are %d increments.", mTrace.increment_size());
+
+    status_t status = loadSurfaceComposerClient();
+
+    if (status != NO_ERROR) {
+        ALOGE("Couldn't create SurfaceComposerClient (%d)", status);
+        return status;
+    }
+
+    SurfaceComposerClient::enableVSyncInjections(true);
+
+    initReplay();
+
+    ALOGV("Starting actual Replay!");
+    while (!mPendingIncrements.empty()) {
+        mCurrentIncrement = mTrace.increment(mIncrementIndex);
+
+        if (mHasStopped == false && mCurrentIncrement.time_stamp() >= mStopTimeStamp) {
+            mHasStopped = true;
+            sReplayingManually.store(true);
+        }
+
+        waitForConsoleCommmand();
+
+        if (mWaitForTimeStamps) {
+            waitUntilTimestamp(mCurrentIncrement.time_stamp());
+        }
+
+        auto event = mPendingIncrements.front();
+        mPendingIncrements.pop();
+
+        event->complete();
+
+        if (event->getIncrementType() == Increment::kVsyncEvent) {
+            mWaitingForNextVSync = false;
+        }
+
+        if (mIncrementIndex + mNumThreads < mTrace.increment_size()) {
+            status = dispatchEvent(mIncrementIndex + mNumThreads);
+
+            if (status != NO_ERROR) {
+                SurfaceComposerClient::enableVSyncInjections(false);
+                return status;
+            }
+        }
+
+        mIncrementIndex++;
+        mCurrentTime = mCurrentIncrement.time_stamp();
+    }
+
+    SurfaceComposerClient::enableVSyncInjections(false);
+
+    return status;
+}
+
+status_t Replayer::initReplay() {
+    for (int i = 0; i < mNumThreads && i < mTrace.increment_size(); i++) {
+        status_t status = dispatchEvent(i);
+
+        if (status != NO_ERROR) {
+            ALOGE("Unable to dispatch event (%d)", status);
+            return status;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+void Replayer::stopAutoReplayHandler(int /*signal*/) {
+    if (sReplayingManually) {
+        SurfaceComposerClient::enableVSyncInjections(false);
+        exit(0);
+    }
+
+    sReplayingManually.store(true);
+}
+
+std::vector<std::string> split(const std::string& s, const char delim) {
+    std::vector<std::string> elems;
+    std::stringstream ss(s);
+    std::string item;
+    while (getline(ss, item, delim)) {
+        elems.push_back(item);
+    }
+    return elems;
+}
+
+bool isNumber(const std::string& s) {
+    return !s.empty() &&
+           std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
+}
+
+void Replayer::waitForConsoleCommmand() {
+    if (!sReplayingManually || mWaitingForNextVSync) {
+        return;
+    }
+
+    while (true) {
+        std::string input = "";
+        std::cout << "> ";
+        getline(std::cin, input);
+
+        if (input.empty()) {
+            input = mLastInput;
+        } else {
+            mLastInput = input;
+        }
+
+        if (mLastInput.empty()) {
+            continue;
+        }
+
+        std::vector<std::string> inputs = split(input, ' ');
+
+        if (inputs[0] == "n") {  // next vsync
+            mWaitingForNextVSync = true;
+            break;
+
+        } else if (inputs[0] == "ni") {  // next increment
+            break;
+
+        } else if (inputs[0] == "c") {  // continue
+            if (inputs.size() > 1 && isNumber(inputs[1])) {
+                long milliseconds = stoi(inputs[1]);
+                std::thread([&] {
+                    std::cout << "Started!" << std::endl;
+                    std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+                    sReplayingManually.store(true);
+                    std::cout << "Should have stopped!" << std::endl;
+                }).detach();
+            }
+            sReplayingManually.store(false);
+            mWaitingForNextVSync = false;
+            break;
+
+        } else if (inputs[0] == "s") {  // stop at this timestamp
+            if (inputs.size() < 1) {
+                std::cout << "No time stamp given" << std::endl;
+                continue;
+            }
+            sReplayingManually.store(false);
+            mStopTimeStamp = stol(inputs[1]);
+            mHasStopped = false;
+            break;
+        } else if (inputs[0] == "l") {  // list
+            std::cout << "Time stamp: " << mCurrentIncrement.time_stamp() << "\n";
+            continue;
+        } else if (inputs[0] == "q") {  // quit
+            SurfaceComposerClient::enableVSyncInjections(false);
+            exit(0);
+
+        } else if (inputs[0] == "h") {  // help
+                                        // add help menu
+            std::cout << "Manual Replay options:\n";
+            std::cout << " n  - Go to next VSync\n";
+            std::cout << " ni - Go to next increment\n";
+            std::cout << " c  - Continue\n";
+            std::cout << " c [milliseconds] - Continue until specified number of milliseconds\n";
+            std::cout << " s [timestamp]    - Continue and stop at specified timestamp\n";
+            std::cout << " l  - List out timestamp of current increment\n";
+            std::cout << " h  - Display help menu\n";
+            std::cout << std::endl;
+            continue;
+        }
+
+        std::cout << "Invalid Command" << std::endl;
+    }
+}
+
+status_t Replayer::dispatchEvent(int index) {
+    auto increment = mTrace.increment(index);
+    std::shared_ptr<Event> event = std::make_shared<Event>(increment.increment_case());
+    mPendingIncrements.push(event);
+
+    status_t status = NO_ERROR;
+    switch (increment.increment_case()) {
+        case increment.kTransaction: {
+            std::thread(&Replayer::doTransaction, this, increment.transaction(), event).detach();
+        } break;
+        case increment.kSurfaceCreation: {
+            std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
+                    .detach();
+        } break;
+        case increment.kSurfaceDeletion: {
+            std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event)
+                    .detach();
+        } break;
+        case increment.kBufferUpdate: {
+            std::lock_guard<std::mutex> lock1(mLayerLock);
+            std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
+
+            Dimensions dimensions(increment.buffer_update().w(), increment.buffer_update().h());
+            BufferEvent bufferEvent(event, dimensions);
+
+            auto layerId = increment.buffer_update().id();
+            if (mBufferQueueSchedulers.count(layerId) == 0) {
+                mBufferQueueSchedulers[layerId] = std::make_shared<BufferQueueScheduler>(
+                        mLayers[layerId], mColors[layerId], layerId);
+                mBufferQueueSchedulers[layerId]->addEvent(bufferEvent);
+
+                std::thread(&BufferQueueScheduler::startScheduling,
+                        mBufferQueueSchedulers[increment.buffer_update().id()].get())
+                        .detach();
+            } else {
+                auto bqs = mBufferQueueSchedulers[increment.buffer_update().id()];
+                bqs->addEvent(bufferEvent);
+            }
+        } break;
+        case increment.kVsyncEvent: {
+            std::thread(&Replayer::injectVSyncEvent, this, increment.vsync_event(), event).detach();
+        } break;
+        case increment.kDisplayCreation: {
+            std::thread(&Replayer::createDisplay, this, increment.display_creation(), event)
+                    .detach();
+        } break;
+        case increment.kDisplayDeletion: {
+            std::thread(&Replayer::deleteDisplay, this, increment.display_deletion(), event)
+                    .detach();
+        } break;
+        case increment.kPowerModeUpdate: {
+            std::thread(&Replayer::updatePowerMode, this, increment.power_mode_update(), event)
+                    .detach();
+        } break;
+        default:
+            ALOGE("Unknown Increment Type: %d", increment.increment_case());
+            status = BAD_VALUE;
+            break;
+    }
+
+    return status;
+}
+
+status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr<Event>& event) {
+    ALOGV("Started Transaction");
+
+    SurfaceComposerClient::openGlobalTransaction();
+
+    status_t status = NO_ERROR;
+
+    status = doSurfaceTransaction(t.surface_change());
+    doDisplayTransaction(t.display_change());
+
+    if (t.animation()) {
+        SurfaceComposerClient::setAnimationTransaction();
+    }
+
+    event->readyToExecute();
+
+    SurfaceComposerClient::closeGlobalTransaction(t.synchronous());
+
+    ALOGV("Ended Transaction");
+
+    return status;
+}
+
+status_t Replayer::doSurfaceTransaction(const SurfaceChanges& surfaceChanges) {
+    status_t status = NO_ERROR;
+
+    for (const SurfaceChange& change : surfaceChanges) {
+        std::unique_lock<std::mutex> lock(mLayerLock);
+        if (mLayers[change.id()] == nullptr) {
+            mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
+        }
+
+        switch (change.SurfaceChange_case()) {
+            case SurfaceChange::SurfaceChangeCase::kPosition:
+                status = setPosition(change.id(), change.position());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kSize:
+                status = setSize(change.id(), change.size());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kAlpha:
+                status = setAlpha(change.id(), change.alpha());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kLayer:
+                status = setLayer(change.id(), change.layer());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kCrop:
+                status = setCrop(change.id(), change.crop());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kMatrix:
+                status = setMatrix(change.id(), change.matrix());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+                status = setFinalCrop(change.id(), change.final_crop());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+                status = setOverrideScalingMode(change.id(), change.override_scaling_mode());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+                status = setTransparentRegionHint(change.id(), change.transparent_region_hint());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kLayerStack:
+                status = setLayerStack(change.id(), change.layer_stack());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+                status = setHiddenFlag(change.id(), change.hidden_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+                status = setOpaqueFlag(change.id(), change.opaque_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+                status = setSecureFlag(change.id(), change.secure_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+                waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
+                status = setDeferredTransaction(change.id(), change.deferred_transaction());
+                break;
+            default:
+                status = NO_ERROR;
+                break;
+        }
+
+        if (status != NO_ERROR) {
+            ALOGE("SET TRANSACTION FAILED");
+            return status;
+        }
+    }
+    return status;
+}
+
+void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) {
+    for (const DisplayChange& change : displayChanges) {
+        ALOGV("Doing display transaction");
+        std::unique_lock<std::mutex> lock(mDisplayLock);
+        if (mDisplays[change.id()] == nullptr) {
+            mDisplayCond.wait(lock, [&] { return (mDisplays[change.id()] != nullptr); });
+        }
+
+        switch (change.DisplayChange_case()) {
+            case DisplayChange::DisplayChangeCase::kSurface:
+                setDisplaySurface(change.id(), change.surface());
+                break;
+            case DisplayChange::DisplayChangeCase::kLayerStack:
+                setDisplayLayerStack(change.id(), change.layer_stack());
+                break;
+            case DisplayChange::DisplayChangeCase::kSize:
+                setDisplaySize(change.id(), change.size());
+                break;
+            case DisplayChange::DisplayChangeCase::kProjection:
+                setDisplayProjection(change.id(), change.projection());
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+status_t Replayer::setPosition(layer_id id, const PositionChange& pc) {
+    ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y());
+    return mLayers[id]->setPosition(pc.x(), pc.y());
+}
+
+status_t Replayer::setSize(layer_id id, const SizeChange& sc) {
+    ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h());
+    return mLayers[id]->setSize(sc.w(), sc.h());
+}
+
+status_t Replayer::setLayer(layer_id id, const LayerChange& lc) {
+    ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer());
+    return mLayers[id]->setLayer(lc.layer());
+}
+
+status_t Replayer::setAlpha(layer_id id, const AlphaChange& ac) {
+    ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha());
+    return mLayers[id]->setAlpha(ac.alpha());
+}
+
+status_t Replayer::setCrop(layer_id id, const CropChange& cc) {
+    ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
+            cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
+            cc.rectangle().bottom());
+
+    Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
+            cc.rectangle().bottom());
+    return mLayers[id]->setCrop(r);
+}
+
+status_t Replayer::setFinalCrop(layer_id id, const FinalCropChange& fcc) {
+    ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
+            fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
+            fcc.rectangle().bottom());
+    Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
+            fcc.rectangle().bottom());
+    return mLayers[id]->setFinalCrop(r);
+}
+
+status_t Replayer::setMatrix(layer_id id, const MatrixChange& mc) {
+    ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
+            mc.dtdx(), mc.dsdy(), mc.dtdy());
+    return mLayers[id]->setMatrix(mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
+}
+
+status_t Replayer::setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc) {
+    ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
+    return mLayers[id]->setOverrideScalingMode(osmc.override_scaling_mode());
+}
+
+status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trhc) {
+    ALOGV("Setting Transparent Region Hint");
+    Region re = Region();
+
+    for (auto r : trhc.region()) {
+        Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom());
+        re.merge(rect);
+    }
+
+    return mLayers[id]->setTransparentRegionHint(re);
+}
+
+status_t Replayer::setLayerStack(layer_id id, const LayerStackChange& lsc) {
+    ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
+    return mLayers[id]->setLayerStack(lsc.layer_stack());
+}
+
+status_t Replayer::setHiddenFlag(layer_id id, const HiddenFlagChange& hfc) {
+    ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag());
+    layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
+
+    return mLayers[id]->setFlags(flag, layer_state_t::eLayerHidden);
+}
+
+status_t Replayer::setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc) {
+    ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag());
+    layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
+
+    return mLayers[id]->setFlags(flag, layer_state_t::eLayerOpaque);
+}
+
+status_t Replayer::setSecureFlag(layer_id id, const SecureFlagChange& sfc) {
+    ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag());
+    layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
+
+    return mLayers[id]->setFlags(flag, layer_state_t::eLayerSecure);
+}
+
+status_t Replayer::setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc) {
+    ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, "
+          "frame_number=%llu",
+            id, dtc.layer_id(), dtc.frame_number());
+    if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
+        ALOGE("Layer %d not found in Deferred Transaction", dtc.layer_id());
+        return BAD_VALUE;
+    }
+
+    auto handle = mLayers[dtc.layer_id()]->getHandle();
+
+    return mLayers[id]->deferTransactionUntil(handle, dtc.frame_number());
+}
+
+void Replayer::setDisplaySurface(display_id id, const DispSurfaceChange& /*dsc*/) {
+    sp<IGraphicBufferProducer> outProducer;
+    sp<IGraphicBufferConsumer> outConsumer;
+    BufferQueue::createBufferQueue(&outProducer, &outConsumer);
+
+    SurfaceComposerClient::setDisplaySurface(mDisplays[id], outProducer);
+}
+
+void Replayer::setDisplayLayerStack(display_id id, const LayerStackChange& lsc) {
+    SurfaceComposerClient::setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+}
+
+void Replayer::setDisplaySize(display_id id, const SizeChange& sc) {
+    SurfaceComposerClient::setDisplaySize(mDisplays[id], sc.w(), sc.h());
+}
+
+void Replayer::setDisplayProjection(display_id id, const ProjectionChange& pc) {
+    Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(),
+            pc.viewport().bottom());
+    Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom());
+
+    SurfaceComposerClient::setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame);
+}
+
+status_t Replayer::createSurfaceControl(
+        const SurfaceCreation& create, const std::shared_ptr<Event>& event) {
+    event->readyToExecute();
+
+    ALOGV("Creating Surface Control: ID: %d", create.id());
+    sp<SurfaceControl> surfaceControl = mComposerClient->createSurface(
+            String8(create.name().c_str()), create.w(), create.h(), PIXEL_FORMAT_RGBA_8888, 0);
+
+    if (surfaceControl == nullptr) {
+        ALOGE("CreateSurfaceControl: unable to create surface control");
+        return BAD_VALUE;
+    }
+
+    std::lock_guard<std::mutex> lock1(mLayerLock);
+    auto& layer = mLayers[create.id()];
+    layer = surfaceControl;
+
+    mColors[create.id()] = HSV(rand() % 360, 1, 1);
+
+    mLayerCond.notify_all();
+
+    std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
+    if (mBufferQueueSchedulers.count(create.id()) != 0) {
+        mBufferQueueSchedulers[create.id()]->setSurfaceControl(
+                mLayers[create.id()], mColors[create.id()]);
+    }
+
+    return NO_ERROR;
+}
+
+status_t Replayer::deleteSurfaceControl(
+        const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) {
+    ALOGV("Deleting %d Surface Control", delete_.id());
+    event->readyToExecute();
+
+    std::lock_guard<std::mutex> lock1(mPendingLayersLock);
+
+    mLayersPendingRemoval.push_back(delete_.id());
+
+    const auto& iterator = mBufferQueueSchedulers.find(delete_.id());
+    if (iterator != mBufferQueueSchedulers.end()) {
+        (*iterator).second->stopScheduling();
+    }
+
+    std::lock_guard<std::mutex> lock2(mLayerLock);
+    if (mLayers[delete_.id()] != nullptr) {
+        mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
+    }
+
+    return NO_ERROR;
+}
+
+void Replayer::doDeleteSurfaceControls() {
+    std::lock_guard<std::mutex> lock1(mPendingLayersLock);
+    std::lock_guard<std::mutex> lock2(mLayerLock);
+    if (!mLayersPendingRemoval.empty()) {
+        for (int id : mLayersPendingRemoval) {
+            mLayers.erase(id);
+            mColors.erase(id);
+            mBufferQueueSchedulers.erase(id);
+        }
+        mLayersPendingRemoval.clear();
+    }
+}
+
+status_t Replayer::injectVSyncEvent(
+        const VSyncEvent& vSyncEvent, const std::shared_ptr<Event>& event) {
+    ALOGV("Injecting VSync Event");
+
+    doDeleteSurfaceControls();
+
+    event->readyToExecute();
+
+    SurfaceComposerClient::injectVSync(vSyncEvent.when());
+
+    return NO_ERROR;
+}
+
+void Replayer::createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event) {
+    ALOGV("Creating display");
+    event->readyToExecute();
+
+    std::lock_guard<std::mutex> lock(mDisplayLock);
+    sp<IBinder> display = SurfaceComposerClient::createDisplay(
+            String8(create.name().c_str()), create.is_secure());
+    mDisplays[create.id()] = display;
+
+    mDisplayCond.notify_all();
+
+    ALOGV("Done creating display");
+}
+
+void Replayer::deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event) {
+    ALOGV("Delete display");
+    event->readyToExecute();
+
+    std::lock_guard<std::mutex> lock(mDisplayLock);
+    SurfaceComposerClient::destroyDisplay(mDisplays[delete_.id()]);
+    mDisplays.erase(delete_.id());
+}
+
+void Replayer::updatePowerMode(const PowerModeUpdate& pmu, const std::shared_ptr<Event>& event) {
+    ALOGV("Updating power mode");
+    event->readyToExecute();
+    SurfaceComposerClient::setDisplayPowerMode(mDisplays[pmu.id()], pmu.mode());
+}
+
+void Replayer::waitUntilTimestamp(int64_t timestamp) {
+    ALOGV("Waiting for %lld nanoseconds...", static_cast<int64_t>(timestamp - mCurrentTime));
+    std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
+}
+
+void Replayer::waitUntilDeferredTransactionLayerExists(
+        const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock) {
+    if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) {
+        mLayerCond.wait(lock, [&] { return (mLayers[dtc.layer_id()] != nullptr); });
+    }
+}
+
+status_t Replayer::loadSurfaceComposerClient() {
+    mComposerClient = new SurfaceComposerClient;
+    return mComposerClient->initCheck();
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
new file mode 100644
index 0000000..f36c9fd
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEREPLAYER_H
+#define ANDROID_SURFACEREPLAYER_H
+
+#include "BufferQueueScheduler.h"
+#include "Color.h"
+#include "Event.h"
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include <stdatomic.h>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <unordered_map>
+#include <utility>
+
+namespace android {
+
+const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
+const auto RAND_COLOR_SEED = 700;
+const auto DEFAULT_THREADS = 3;
+
+typedef int32_t layer_id;
+typedef int32_t display_id;
+
+typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
+typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
+
+class Replayer {
+  public:
+    Replayer(const std::string& filename, bool replayManually = false,
+            int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
+    Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
+            bool wait = true, nsecs_t stopHere = -1);
+
+    status_t replay();
+
+  private:
+    status_t initReplay();
+
+    void waitForConsoleCommmand();
+    static void stopAutoReplayHandler(int signal);
+
+    status_t dispatchEvent(int index);
+
+    status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
+    status_t createSurfaceControl(const SurfaceCreation& create,
+            const std::shared_ptr<Event>& event);
+    status_t deleteSurfaceControl(const SurfaceDeletion& delete_,
+            const std::shared_ptr<Event>& event);
+    status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
+    void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
+    void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
+    void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
+
+    status_t doSurfaceTransaction(const SurfaceChanges& surfaceChange);
+    void doDisplayTransaction(const DisplayChanges& displayChange);
+
+    status_t setPosition(layer_id id, const PositionChange& pc);
+    status_t setSize(layer_id id, const SizeChange& sc);
+    status_t setAlpha(layer_id id, const AlphaChange& ac);
+    status_t setLayer(layer_id id, const LayerChange& lc);
+    status_t setCrop(layer_id id, const CropChange& cc);
+    status_t setFinalCrop(layer_id id, const FinalCropChange& fcc);
+    status_t setMatrix(layer_id id, const MatrixChange& mc);
+    status_t setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc);
+    status_t setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trgc);
+    status_t setLayerStack(layer_id id, const LayerStackChange& lsc);
+    status_t setHiddenFlag(layer_id id, const HiddenFlagChange& hfc);
+    status_t setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc);
+    status_t setSecureFlag(layer_id id, const SecureFlagChange& sfc);
+    status_t setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc);
+
+    void setDisplaySurface(display_id id, const DispSurfaceChange& dsc);
+    void setDisplayLayerStack(display_id id, const LayerStackChange& lsc);
+    void setDisplaySize(display_id id, const SizeChange& sc);
+    void setDisplayProjection(display_id id, const ProjectionChange& pc);
+
+    void doDeleteSurfaceControls();
+    void waitUntilTimestamp(int64_t timestamp);
+    void waitUntilDeferredTransactionLayerExists(
+            const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
+    status_t loadSurfaceComposerClient();
+
+    Trace mTrace;
+    bool mLoaded = false;
+    int32_t mIncrementIndex = 0;
+    int64_t mCurrentTime = 0;
+    int32_t mNumThreads = DEFAULT_THREADS;
+
+    Increment mCurrentIncrement;
+
+    std::string mLastInput;
+
+    static atomic_bool sReplayingManually;
+    bool mWaitingForNextVSync;
+    bool mWaitForTimeStamps;
+    nsecs_t mStopTimeStamp;
+    bool mHasStopped;
+
+    std::mutex mLayerLock;
+    std::condition_variable mLayerCond;
+    std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
+    std::unordered_map<layer_id, HSV> mColors;
+
+    std::mutex mPendingLayersLock;
+    std::vector<layer_id> mLayersPendingRemoval;
+
+    std::mutex mBufferQueueSchedulerLock;
+    std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
+
+    std::mutex mDisplayLock;
+    std::condition_variable mDisplayCond;
+    std::unordered_map<display_id, sp<IBinder>> mDisplays;
+
+    sp<SurfaceComposerClient> mComposerClient;
+    std::queue<std::shared_ptr<Event>> mPendingIncrements;
+};
+
+}  // namespace android
+#endif
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
new file mode 100644
index 0000000..a892e46
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -0,0 +1,294 @@
+#!/usr/bin/python
+from subprocess import call
+import os
+proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/"
+call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"])
+
+from trace_pb2 import *
+
+trace = Trace()
+
+def main():
+    global trace
+    while(1):
+        option = main_menu()
+
+        if option == 0:
+            break
+
+        increment = trace.increment.add()
+        increment.time_stamp  = int(input("Time stamp of action: "))
+
+        if option == 1:
+           transaction(increment)
+        elif option == 2:
+            surface_create(increment)
+        elif option == 3:
+            surface_delete(increment)
+        elif option == 4:
+            display_create(increment)
+        elif option == 5:
+            display_delete(increment)
+        elif option == 6:
+            buffer_update(increment)
+        elif option == 7:
+            vsync_event(increment)
+        elif option == 8:
+            power_mode_update(increment)
+
+    seralizeTrace()
+
+def seralizeTrace():
+    with open("trace.dat", 'wb') as f:
+        f.write(trace.SerializeToString())
+
+
+def main_menu():
+    print ("")
+    print ("What would you like to do?")
+    print ("1. Add transaction")
+    print ("2. Add surface creation")
+    print ("3. Add surface deletion")
+    print ("4. Add display creation")
+    print ("5. Add display deletion")
+    print ("6. Add buffer update")
+    print ("7. Add VSync event")
+    print ("8. Add power mode update")
+    print ("0. Finish and serialize")
+    print ("")
+
+    return int(input("> "))
+
+def transaction_menu():
+    print ("")
+    print ("What kind of transaction?")
+    print ("1. Position Change")
+    print ("2. Size Change")
+    print ("3. Alpha Change")
+    print ("4. Layer Change")
+    print ("5. Crop Change")
+    print ("6. Final Crop Change")
+    print ("7. Matrix Change")
+    print ("8. Override Scaling Mode Change")
+    print ("9. Transparent Region Hint Change")
+    print ("10. Layer Stack Change")
+    print ("11. Hidden Flag Change")
+    print ("12. Opaque Flag Change")
+    print ("13. Secure Flag Change")
+    print ("14. Deferred Transaction Change")
+    print ("15. Display - Surface Change")
+    print ("16. Display - Layer Stack Change")
+    print ("17. Display - Size Change")
+    print ("18. Display - Projection Change")
+    print ("0. Finished adding Changes to this transaction")
+    print ("")
+
+    return int(input("> "))
+
+def transaction(increment):
+    global trace
+
+    increment.transaction.synchronous \
+            = bool(input("Is transaction synchronous (True/False): "))
+    increment.transaction.animation \
+            = bool(input("Is transaction animated (True/False): "))
+
+    while(1):
+        option = transaction_menu()
+
+        if option == 0:
+            break
+
+        change = None
+        if option <= 14:
+            change = increment.transaction.surface_change.add()
+        elif option >= 15 and option <= 18:
+            change = increment.transaction.display_change.add()
+
+        change.id = int(input("ID of layer/display to undergo a change: "))
+
+        if option == 1:
+            change.position.x, change.position.y = position()
+        elif option == 2:
+            change.size.w, change.size.h = size()
+        elif option == 3:
+            change.alpha.alpha = alpha()
+        elif option == 4:
+            change.layer.layer = layer()
+        elif option == 5:
+            change.crop.rectangle.left,  change.crop.rectangle.top, \
+            change.crop.rectangle.right, change.crop.rectangle.bottom = crop()
+        elif option == 6:
+            change.final_crop.rectangle.left, \
+            change.final_crop.rectangle.top,  \
+            change.final_crop.rectangle.right,\
+            change.final_crop.rectangle.bottom = final_crop()
+        elif option == 7:
+            change.matrix.dsdx,\
+            change.matrix.dtdx,\
+            change.matrix.dsdy,\
+            change.matrix.dtdy = layer()
+        elif option == 8:
+            change.override_scaling_mode.override_scaling_mode \
+                                     = override_scaling_mode()
+        elif option == 9:
+            for rect in transparent_region_hint():
+                new = increment.transparent_region_hint.region.add()
+                new.left = rect[0]
+                new.top = rect[1]
+                new.right = rect[2]
+                new.bottom = rect[3]
+        elif option == 10:
+            change.layer_stack.layer_stack = layer_stack()
+        elif option == 11:
+            change.hidden_flag.hidden_flag = hidden_flag()
+        elif option == 12:
+            change.opaque_flag.opaque_flag = opaque_flag()
+        elif option == 13:
+            change.secure_flag.secure_flag = secure_flag()
+        elif option == 14:
+            change.deferred_transaction.layer_id, \
+            change.deferred_transaction.frame_number = deferred_transaction()
+        elif option == 15:
+            change.surface.buffer_queue_id, \
+            change.surface.buffer_queue_name = surface()
+        elif option == 16:
+            change.layer_stack.layer_stack = layer_stack()
+        elif option == 17:
+            change.size.w, change.size.h = size()
+        elif option == 18:
+            projection(change)
+
+def surface_create(increment):
+    increment.surface_creation.id = int(input("Enter id: "))
+    n = str(raw_input("Enter name: "))
+    increment.surface_creation.name = n
+    increment.surface_creation.w = input("Enter w: ")
+    increment.surface_creation.h = input("Enter h: ")
+
+def surface_delete(increment):
+    increment.surface_deletion.id = int(input("Enter id: "))
+
+def display_create(increment):
+    increment.display_creation.id = int(input("Enter id: "))
+    increment.display_creation.name = str(raw_input("Enter name: "))
+    increment.display_creation.type = int(input("Enter type: "))
+    increment.display_creation.is_secure = bool(input("Enter if secure: "))
+
+def display_delete(increment):
+    increment.surface_deletion.id = int(input("Enter id: "))
+
+def buffer_update(increment):
+    increment.buffer_update.id = int(input("Enter id: "))
+    increment.buffer_update.w = int(input("Enter w: "))
+    increment.buffer_update.h = int(input("Enter h: "))
+    increment.buffer_update.frame_number = int(input("Enter frame_number: "))
+
+def vsync_event(increment):
+    increment.vsync_event.when = int(input("Enter when: "))
+
+def power_mode_update(increment):
+    increment.power_mode_update.id = int(input("Enter id: "))
+    increment.power_mode_update.mode = int(input("Enter mode: "))
+
+def position():
+    x = input("Enter x: ")
+    y = input("Enter y: ")
+
+    return float(x), float(y)
+
+def size():
+    w = input("Enter w: ")
+    h = input("Enter h: ")
+
+    return int(w), int(h)
+
+def alpha():
+    alpha = input("Enter alpha: ")
+
+    return float(alpha)
+
+def layer():
+    layer = input("Enter layer: ")
+
+    return int(layer)
+
+def crop():
+    return rectangle()
+
+def final_crop():
+    return rectangle()
+
+def matrix():
+    dsdx = input("Enter dsdx: ")
+    dtdx = input("Enter dtdx: ")
+    dsdy = input("Enter dsdy: ")
+    dtdy = input("Enter dtdy: ")
+
+    return float(dsdx)
+
+def override_scaling_mode():
+    mode = input("Enter override scaling mode: ")
+
+    return int(mode)
+
+def transparent_region_hint():
+    num = input("Enter number of rectangles in region: ")
+
+    return [rectangle() in range(x)]
+
+def layer_stack():
+    layer_stack = input("Enter layer stack: ")
+
+    return int(layer_stack)
+
+def hidden_flag():
+    flag = input("Enter hidden flag state (True/False): ")
+
+    return bool(flag)
+
+def opaque_flag():
+    flag = input("Enter opaque flag state (True/False): ")
+
+    return bool(flag)
+
+def secure_flag():
+    flag = input("Enter secure flag state (True/False): ")
+
+    return bool(flag)
+
+def deferred_transaction():
+    layer_id = input("Enter layer_id: ")
+    frame_number = input("Enter frame_number: ")
+
+    return int(layer_id), int(frame_number)
+
+def surface():
+    id = input("Enter id: ")
+    name = raw_input("Enter name: ")
+
+    return int(id), str(name)
+
+def projection(change):
+    change.projection.orientation = input("Enter orientation: ")
+    print("Enter rectangle for viewport")
+    change.projection.viewport.left, \
+    change.projection.viewport.top,  \
+    change.projection.viewport.right,\
+    change.projection.viewport.bottom = rectangle()
+    print("Enter rectangle for frame")
+    change.projection.frame.left, \
+    change.projection.frame.top,  \
+    change.projection.frame.right,\
+    change.projection.frame.bottom = rectangle()
+
+def rectangle():
+    left = input("Enter left: ")
+    top = input("Enter top: ")
+    right = input("Enter right: ")
+    bottom = input("Enter bottom: ")
+
+    return int(left), int(top), int(right), int(bottom)
+
+if __name__ == "__main__":
+    main()
diff --git a/cmds/vr/.clang-format b/cmds/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/cmds/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/cmds/vr/pose/Android.mk b/cmds/vr/pose/Android.mk
new file mode 100644
index 0000000..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/android.hardware.vulkan.compute-0.xml b/data/etc/android.hardware.vulkan.compute-0.xml
new file mode 100644
index 0000000..bac2fde
--- /dev/null
+++ b/data/etc/android.hardware.vulkan.compute-0.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device supports Vulkan
+     compute level 0. -->
+<permissions>
+    <feature name="android.hardware.vulkan.compute" version="0" />
+</permissions>
diff --git a/data/etc/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/docs/Doxyfile b/docs/Doxyfile
index 3ea453f..bb0ca32 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -14,90 +14,90 @@
 # Project related configuration options
 #---------------------------------------------------------------------------
 
-# This tag specifies the encoding used for all characters in the config file 
-# that follow. The default is UTF-8 which is also the encoding used for all 
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
-# iconv built into libc) for the transcoding. See 
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
 # http://www.gnu.org/software/libiconv for the list of possible encodings.
 
 DOXYFILE_ENCODING      = UTF-8
 
-# The PROJECT_NAME tag is a single word (or sequence of words) that should 
-# identify the project. Note that if you do not use Doxywizard you need 
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
 # to put quotes around the project name if it contains spaces.
 
 PROJECT_NAME           = "NDK API"
 
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
-# This could be handy for archiving the generated documentation or 
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 
+PROJECT_NUMBER         =
 
-# Using the PROJECT_BRIEF tag one can provide an optional one line description 
-# for a project that appears at the top of each page and should give viewer 
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
 # a quick idea about the purpose of the project. Keep the description short.
 
 PROJECT_BRIEF          = ""
 
-# With the PROJECT_LOGO tag one can specify an logo or icon that is 
-# included in the documentation. The maximum height of the logo should not 
-# exceed 55 pixels and the maximum width should not exceed 200 pixels. 
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
 # Doxygen will copy the logo to the output directory.
 
 PROJECT_LOGO           = logo.png
 
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
-# base path where the generated documentation will be put. 
-# If a relative path is entered, it will be relative to the location 
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
 # where doxygen was started. If left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = 
+OUTPUT_DIRECTORY       =
 
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
-# 4096 sub-directories (in 2 levels) under the output directory of each output 
-# format and will distribute the generated files over these directories. 
-# Enabling this option can be useful when feeding doxygen a huge amount of 
-# source files, where putting all generated files in the same directory would 
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
 # otherwise cause performance problems for the file system.
 
 CREATE_SUBDIRS         = NO
 
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
-# documentation generated by doxygen is written. Doxygen will use this 
-# information to generate all constant output in the proper language. 
-# The default language is English, other supported languages are: 
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, 
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English 
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, 
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, 
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
 # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
 
 OUTPUT_LANGUAGE        = English
 
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
-# include brief member descriptions after the members that are listed in 
-# the file and class documentation (similar to JavaDoc). 
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
 # Set to NO to disable this.
 
 BRIEF_MEMBER_DESC      = YES
 
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
-# the brief description of a member or function before the detailed description. 
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
 # brief descriptions will be completely suppressed.
 
 REPEAT_BRIEF           = YES
 
-# This tag implements a quasi-intelligent brief description abbreviator 
-# that is used to form the text in various listings. Each string 
-# in this list, if found as the leading text of the brief description, will be 
-# stripped from the text and the result after processing the whole list, is 
-# used as the annotated text. Otherwise, the brief description is used as-is. 
-# If left blank, the following values are used ("$name" is automatically 
-# replaced with the name of the entity): "The $name class" "The $name widget" 
-# "The $name file" "is" "provides" "specifies" "contains" 
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
 # "represents" "a" "an" "the"
 
 ABBREVIATE_BRIEF       = "The $name class" \
@@ -112,256 +112,256 @@
                          an \
                          the
 
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
-# Doxygen will generate a detailed section even if there is only a brief 
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
 # description.
 
 ALWAYS_DETAILED_SEC    = NO
 
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
-# inherited members of a class in the documentation of that class as if those 
-# members were ordinary class members. Constructors, destructors and assignment 
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
 # operators of the base classes will not be shown.
 
 INLINE_INHERITED_MEMB  = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
-# path before files name in the file list and in the header files. If set 
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
 # to NO the shortest path that makes the file name unique will be used.
 
 FULL_PATH_NAMES        = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
-# can be used to strip a user-defined part of the path. Stripping is 
-# only done if one of the specified strings matches the left-hand part of 
-# the path. The tag can be used to show relative paths in the file list. 
-# If left blank the directory from which doxygen is run is used as the 
-# path to strip. Note that you specify absolute paths here, but also 
-# relative paths, which will be relative from the directory where doxygen is 
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
 # started.
 
-STRIP_FROM_PATH        = 
+STRIP_FROM_PATH        =
 
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
-# the path mentioned in the documentation of a class, which tells 
-# the reader which header file to include in order to use a class. 
-# If left blank only the name of the header file containing the class 
-# definition is used. Otherwise one should specify the include paths that 
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
 # are normally passed to the compiler using the -I flag.
 
-STRIP_FROM_INC_PATH    = 
+STRIP_FROM_INC_PATH    =
 
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
-# (but less readable) file names. This can be useful if your file system 
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
 # doesn't support long names like on DOS, Mac, or CD-ROM.
 
 SHORT_NAMES            = NO
 
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
-# will interpret the first line (until the first dot) of a JavaDoc-style 
-# comment as the brief description. If set to NO, the JavaDoc 
-# comments will behave just like regular Qt-style comments 
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
 # (thus requiring an explicit @brief command for a brief description.)
 
 JAVADOC_AUTOBRIEF      = NO
 
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
-# interpret the first line (until the first dot) of a Qt-style 
-# comment as the brief description. If set to NO, the comments 
-# will behave just like regular Qt-style comments (thus requiring 
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
 # an explicit \brief command for a brief description.)
 
 QT_AUTOBRIEF           = NO
 
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
-# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
-# comments) as a brief description. This used to be the default behaviour. 
-# The new default is to treat a multi-line C++ comment block as a detailed 
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
 # description. Set this tag to YES if you prefer the old behaviour instead.
 
 MULTILINE_CPP_IS_BRIEF = NO
 
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
-# member inherits the documentation from any documented member that it 
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
 # re-implements.
 
 INHERIT_DOCS           = YES
 
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
-# a new page for each member. If set to NO, the documentation of a member will 
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
 # be part of the file/class/namespace that contains it.
 
 SEPARATE_MEMBER_PAGES  = NO
 
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
 # Doxygen uses this value to replace tabs by spaces in code fragments.
 
 TAB_SIZE               = 4
 
-# This tag can be used to specify a number of aliases that acts 
-# as commands in the documentation. An alias has the form "name=value". 
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
-# put the command \sideeffect (or @sideeffect) in the documentation, which 
-# will result in a user-defined paragraph with heading "Side Effects:". 
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
 # You can put \n's in the value part of an alias to insert newlines.
 
-ALIASES                = 
+ALIASES                =
 
-# This tag can be used to specify a number of word-keyword mappings (TCL only). 
-# A mapping has the form "name=value". For example adding 
-# "class=itcl::class" will allow you to use the command class in the 
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
 # itcl::class meaning.
 
-TCL_SUBST              = 
+TCL_SUBST              =
 
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
-# sources only. Doxygen will then generate output that is more tailored for C. 
-# For instance, some of the names that are used will be different. The list 
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
 # of all members will be omitted, etc.
 
 OPTIMIZE_OUTPUT_FOR_C  = YES
 
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
-# sources only. Doxygen will then generate output that is more tailored for 
-# Java. For instance, namespaces will be presented as packages, qualified 
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
 # scopes will look different, etc.
 
 OPTIMIZE_OUTPUT_JAVA   = NO
 
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
-# sources only. Doxygen will then generate output that is more tailored for 
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
 # Fortran.
 
 OPTIMIZE_FOR_FORTRAN   = NO
 
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
-# sources. Doxygen will then generate output that is tailored for 
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
 # VHDL.
 
 OPTIMIZE_OUTPUT_VHDL   = NO
 
-# Doxygen selects the parser to use depending on the extension of the files it 
-# parses. With this tag you can assign which parser to use for a given 
-# extension. Doxygen has a built-in mapping, but you can override or extend it 
-# using this tag. The format is ext=language, where ext is a file extension, 
-# and language is one of the parsers supported by doxygen: IDL, Java, 
-# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, 
-# C++. For instance to make doxygen treat .inc files as Fortran files (default 
-# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note 
-# that for custom extensions you also need to set FILE_PATTERNS otherwise the 
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
 # files are not read by doxygen.
 
-EXTENSION_MAPPING      = 
+EXTENSION_MAPPING      =
 
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all 
-# comments according to the Markdown format, which allows for more readable 
-# documentation. See http://daringfireball.net/projects/markdown/ for details. 
-# The output of markdown processing is further processed by doxygen, so you 
-# can mix doxygen, HTML, and XML commands with Markdown formatting. 
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
 # Disable only in case of backward compatibilities issues.
 
 MARKDOWN_SUPPORT       = YES
 
-# When enabled doxygen tries to link words that correspond to documented classes, 
-# or namespaces to their corresponding documentation. Such a link can be 
-# prevented in individual cases by by putting a % sign in front of the word or 
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
 # globally by setting AUTOLINK_SUPPORT to NO.
 
 AUTOLINK_SUPPORT       = YES
 
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
-# to include (a tag file for) the STL sources as input, then you should 
-# set this tag to YES in order to let doxygen match functions declarations and 
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
-# func(std::string) {}). This also makes the inheritance and collaboration 
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
 
 BUILTIN_STL_SUPPORT    = NO
 
-# If you use Microsoft's C++/CLI language, you should set this option to YES to 
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
 # enable parsing support.
 
 CPP_CLI_SUPPORT        = NO
 
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
-# Doxygen will parse them like normal C++ but will assume all classes use public 
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
 # instead of private inheritance when no explicit protection keyword is present.
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate 
-# getter and setter methods for a property. Setting this option to YES (the 
-# default) will make doxygen replace the get and set methods by a property in 
-# the documentation. This will only work if the methods are indeed getting or 
-# setting a simple type. If this is not the case, or you want to show the 
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
 # methods anyway, you should set this option to NO.
 
 IDL_PROPERTY_SUPPORT   = YES
 
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
-# tag is set to YES, then doxygen will reuse the documentation of the first 
-# member in the group (if any) for the other members of the group. By default 
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
 # all members of a group must be documented explicitly.
 
 DISTRIBUTE_GROUP_DOC   = NO
 
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
-# the same type (for instance a group of public functions) to be put as a 
-# subgroup of that type (e.g. under the Public Functions section). Set it to 
-# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
 # the \nosubgrouping command.
 
 SUBGROUPING            = YES
 
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and 
-# unions are shown inside the group in which they are included (e.g. using 
-# @ingroup) instead of on a separate page (for HTML and Man pages) or 
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
 # section (for LaTeX and RTF).
 
 INLINE_GROUPED_CLASSES = NO
 
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and 
-# unions with only public data fields will be shown inline in the documentation 
-# of the scope in which they are defined (i.e. file, namespace, or group 
-# documentation), provided this scope is documented. If set to NO (the default), 
-# structs, classes, and unions are shown on a separate page (for HTML and Man 
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
 # pages) or section (for LaTeX and RTF).
 
 INLINE_SIMPLE_STRUCTS  = NO
 
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
-# is documented as struct, union, or enum with the name of the typedef. So 
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
-# with name TypeT. When disabled the typedef will appear as a member of a file, 
-# namespace, or class. And the struct will be named TypeS. This can typically 
-# be useful for C code in case the coding convention dictates that all compound 
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
 # types are typedef'ed and only the typedef is referenced, never the tag name.
 
 TYPEDEF_HIDES_STRUCT   = NO
 
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
-# determine which symbols to keep in memory and which to flush to disk. 
-# When the cache is full, less often used symbols will be written to disk. 
-# For small to medium size projects (<1000 input files) the default value is 
-# probably good enough. For larger projects a too small cache size can cause 
-# doxygen to be busy swapping symbols to and from disk most of the time 
-# causing a significant performance penalty. 
-# If the system has enough physical memory increasing the cache will improve the 
-# performance by keeping more symbols in memory. Note that the value works on 
-# a logarithmic scale so increasing the size by one will roughly double the 
-# memory usage. The cache size is given by this formula: 
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
 # corresponding to a cache size of 2^16 = 65536 symbols.
 
 SYMBOL_CACHE_SIZE      = 0
 
-# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be 
-# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given 
-# their name and scope. Since this can be an expensive process and often the 
-# same symbol appear multiple times in the code, doxygen keeps a cache of 
-# pre-resolved symbols. If the cache is too small doxygen will become slower. 
-# If the cache is too large, memory is wasted. The cache size is given by this 
-# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
 # corresponding to a cache size of 2^16 = 65536 symbols.
 
 LOOKUP_CACHE_SIZE      = 0
@@ -370,329 +370,329 @@
 # Build related configuration options
 #---------------------------------------------------------------------------
 
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
-# documentation are documented, even if no documentation was available. 
-# Private class members and static file members will be hidden unless 
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
 # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
 
 EXTRACT_ALL            = YES
 
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
 # will be included in the documentation.
 
 EXTRACT_PRIVATE        = NO
 
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal 
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
 # scope will be included in the documentation.
 
 EXTRACT_PACKAGE        = NO
 
-# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
 # will be included in the documentation.
 
 EXTRACT_STATIC         = NO
 
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
-# defined locally in source files will be included in the documentation. 
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
 # If set to NO only classes defined in header files are included.
 
 EXTRACT_LOCAL_CLASSES  = YES
 
-# This flag is only useful for Objective-C code. When set to YES local 
-# methods, which are defined in the implementation section but not in 
-# the interface are included in the documentation. 
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
 # If set to NO (the default) only methods in the interface are included.
 
 EXTRACT_LOCAL_METHODS  = NO
 
-# If this flag is set to YES, the members of anonymous namespaces will be 
-# extracted and appear in the documentation as a namespace called 
-# 'anonymous_namespace{file}', where file will be replaced with the base 
-# name of the file that contains the anonymous namespace. By default 
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
 # anonymous namespaces are hidden.
 
 EXTRACT_ANON_NSPACES   = NO
 
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
-# undocumented members of documented classes, files or namespaces. 
-# If set to NO (the default) these members will be included in the 
-# various overviews, but no documentation section is generated. 
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
 # This option has no effect if EXTRACT_ALL is enabled.
 
 HIDE_UNDOC_MEMBERS     = NO
 
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
-# undocumented classes that are normally visible in the class hierarchy. 
-# If set to NO (the default) these classes will be included in the various 
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
 # overviews. This option has no effect if EXTRACT_ALL is enabled.
 
 HIDE_UNDOC_CLASSES     = NO
 
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
-# friend (class|struct|union) declarations. 
-# If set to NO (the default) these declarations will be included in the 
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
 # documentation.
 
 HIDE_FRIEND_COMPOUNDS  = NO
 
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
-# documentation blocks found inside the body of a function. 
-# If set to NO (the default) these blocks will be appended to the 
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
 # function's detailed documentation block.
 
 HIDE_IN_BODY_DOCS      = NO
 
-# The INTERNAL_DOCS tag determines if documentation 
-# that is typed after a \internal command is included. If the tag is set 
-# to NO (the default) then the documentation will be excluded. 
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
 # Set it to YES to include the internal documentation.
 
 INTERNAL_DOCS          = NO
 
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
-# file names in lower-case letters. If set to YES upper-case letters are also 
-# allowed. This is useful if you have classes or files whose names only differ 
-# in case and if your file system supports case sensitive file names. Windows 
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
 # and Mac users are advised to set this option to NO.
 
 CASE_SENSE_NAMES       = NO
 
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
-# will show members with their full class and namespace scopes in the 
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
 # documentation. If set to YES the scope will be hidden.
 
 HIDE_SCOPE_NAMES       = YES
 
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
-# will put a list of the files that are included by a file in the documentation 
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
 # of that file.
 
 SHOW_INCLUDE_FILES     = YES
 
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen 
-# will list include files with double quotes in the documentation 
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
 # rather than with sharp brackets.
 
 FORCE_LOCAL_INCLUDES   = NO
 
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
 # is inserted in the documentation for inline members.
 
 INLINE_INFO            = YES
 
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
-# will sort the (detailed) documentation of file and class members 
-# alphabetically by member name. If set to NO the members will appear in 
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
 # declaration order.
 
 SORT_MEMBER_DOCS       = YES
 
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
-# brief documentation of file, namespace and class members alphabetically 
-# by member name. If set to NO (the default) the members will appear in 
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
 # declaration order.
 
 SORT_BRIEF_DOCS        = NO
 
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen 
-# will sort the (brief and detailed) documentation of class members so that 
-# constructors and destructors are listed first. If set to NO (the default) 
-# the constructors will appear in the respective orders defined by 
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. 
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO 
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
 # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
 
 SORT_MEMBERS_CTORS_1ST = NO
 
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
-# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
 # the group names will appear in their defined order.
 
 SORT_GROUP_NAMES       = NO
 
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
-# sorted by fully-qualified names, including namespaces. If set to 
-# NO (the default), the class list will be sorted only by class name, 
-# not including the namespace part. 
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 
-# Note: This option applies only to the class list, not to the 
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
 # alphabetical list.
 
 SORT_BY_SCOPE_NAME     = NO
 
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to 
-# do proper type resolution of all parameters of a function it will reject a 
-# match between the prototype and the implementation of a member function even 
-# if there is only one candidate or it is obvious which candidate to choose 
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen 
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
 # will still accept a match between prototype and implementation in such cases.
 
 STRICT_PROTO_MATCHING  = NO
 
-# The GENERATE_TODOLIST tag can be used to enable (YES) or 
-# disable (NO) the todo list. This list is created by putting \todo 
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
 # commands in the documentation.
 
 GENERATE_TODOLIST      = YES
 
-# The GENERATE_TESTLIST tag can be used to enable (YES) or 
-# disable (NO) the test list. This list is created by putting \test 
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
 # commands in the documentation.
 
 GENERATE_TESTLIST      = YES
 
-# The GENERATE_BUGLIST tag can be used to enable (YES) or 
-# disable (NO) the bug list. This list is created by putting \bug 
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
 # commands in the documentation.
 
 GENERATE_BUGLIST       = YES
 
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
-# disable (NO) the deprecated list. This list is created by putting 
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
 # \deprecated commands in the documentation.
 
 GENERATE_DEPRECATEDLIST= YES
 
-# The ENABLED_SECTIONS tag can be used to enable conditional 
-# documentation sections, marked by \if section-label ... \endif 
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if section-label ... \endif
 # and \cond section-label ... \endcond blocks.
 
-ENABLED_SECTIONS       = 
+ENABLED_SECTIONS       =
 
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
-# the initial value of a variable or macro consists of for it to appear in 
-# the documentation. If the initializer consists of more lines than specified 
-# here it will be hidden. Use a value of 0 to hide initializers completely. 
-# The appearance of the initializer of individual variables and macros in the 
-# documentation can be controlled using \showinitializer or \hideinitializer 
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
 # command in the documentation regardless of this setting.
 
 MAX_INITIALIZER_LINES  = 26
 
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
-# at the bottom of the documentation of classes and structs. If set to YES the 
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
 # list will mention the files that were used to generate the documentation.
 
 SHOW_USED_FILES        = YES
 
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page. 
-# This will remove the Files entry from the Quick Index and from the 
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
 # Folder Tree View (if specified). The default is YES.
 
 SHOW_FILES             = YES
 
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
-# Namespaces page.  This will remove the Namespaces entry from the Quick Index 
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
 # and from the Folder Tree View (if specified). The default is YES.
 
 SHOW_NAMESPACES        = YES
 
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
-# doxygen should invoke to get the current version for each file (typically from 
-# the version control system). Doxygen will invoke the program by executing (via 
-# popen()) the command <command> <input-file>, where <command> is the value of 
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
-# provided by doxygen. Whatever the program writes to standard output 
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
 # is used as the file version. See the manual for examples.
 
-FILE_VERSION_FILTER    = 
+FILE_VERSION_FILTER    =
 
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed 
-# by doxygen. The layout file controls the global structure of the generated 
-# output files in an output format independent way. To create the layout file 
-# that represents doxygen's defaults, run doxygen with the -l option. 
-# You can optionally specify a file name after the option, if omitted 
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
 # DoxygenLayout.xml will be used as the name of the layout file.
 
-LAYOUT_FILE            = 
+LAYOUT_FILE            =
 
-# The CITE_BIB_FILES tag can be used to specify one or more bib files 
-# containing the references data. This must be a list of .bib files. The 
-# .bib extension is automatically appended if omitted. Using this command 
-# requires the bibtex tool to be installed. See also 
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style 
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this 
-# feature you need bibtex and perl available in the search path. Do not use 
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
 # file names with spaces, bibtex cannot handle them.
 
-CITE_BIB_FILES         = 
+CITE_BIB_FILES         =
 
 #---------------------------------------------------------------------------
 # configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 
-# The QUIET tag can be used to turn on/off the messages that are generated 
+# The QUIET tag can be used to turn on/off the messages that are generated
 # by doxygen. Possible values are YES and NO. If left blank NO is used.
 
 QUIET                  = NO
 
-# The WARNINGS tag can be used to turn on/off the warning messages that are 
-# generated by doxygen. Possible values are YES and NO. If left blank 
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
 # NO is used.
 
 WARNINGS               = YES
 
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
 # automatically be disabled.
 
 WARN_IF_UNDOCUMENTED   = YES
 
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
-# potential errors in the documentation, such as not documenting some 
-# parameters in a documented function, or documenting parameters that 
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
 # don't exist or using markup commands wrongly.
 
 WARN_IF_DOC_ERROR      = YES
 
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for 
-# functions that are documented, but have no documentation for their parameters 
-# or return value. If set to NO (the default) doxygen will only warn about 
-# wrong or incomplete parameter documentation, but not about the absence of 
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
 # documentation.
 
 WARN_NO_PARAMDOC       = NO
 
-# The WARN_FORMAT tag determines the format of the warning messages that 
-# doxygen can produce. The string should contain the $file, $line, and $text 
-# tags, which will be replaced by the file and line number from which the 
-# warning originated and the warning text. Optionally the format may contain 
-# $version, which will be replaced by the version of the file (if it could 
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
 # be obtained via FILE_VERSION_FILTER)
 
 WARN_FORMAT            = "$file:$line: $text"
 
-# The WARN_LOGFILE tag can be used to specify a file to which warning 
-# and error messages should be written. If left blank the output is written 
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
 # to stderr.
 
-WARN_LOGFILE           = 
+WARN_LOGFILE           =
 
 #---------------------------------------------------------------------------
 # configuration options related to the input files
 #---------------------------------------------------------------------------
 
-# The INPUT tag can be used to specify the files and/or directories that contain 
-# documented source files. You may enter file names like "myfile.cpp" or 
-# directories like "/usr/src/myproject". Separate the files or directories 
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
 INPUT                  = ../include/android ../../av/include/ndk ../../av/include/camera/ndk
 
-# This tag can be used to specify the character encoding of the source files 
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
-# also the default input encoding. Doxygen uses libiconv (or the iconv built 
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
 # the list of possible encodings.
 
 INPUT_ENCODING         = UTF-8
 
-# If the value of the INPUT tag contains directories, you can use the 
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
-# blank the following patterns are tested: 
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh 
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py 
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
 # *.f90 *.f *.for *.vhd *.vhdl
 
 FILE_PATTERNS          = *.c \
@@ -730,159 +730,159 @@
                          *.vhd \
                          *.vhdl
 
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
-# should be searched for input files as well. Possible values are YES and NO. 
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
 # If left blank NO is used.
 
 RECURSIVE              = YES
 
-# The EXCLUDE tag can be used to specify files and/or directories that should be 
-# excluded from the INPUT source files. This way you can easily exclude a 
-# subdirectory from a directory tree whose root is specified with the INPUT tag. 
-# Note that relative paths are relative to the directory from which doxygen is 
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
 # run.
 
-EXCLUDE                = 
+EXCLUDE                =
 
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or 
-# directories that are symbolic links (a Unix file system feature) are excluded 
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
 # from the input.
 
 EXCLUDE_SYMLINKS       = NO
 
-# If the value of the INPUT tag contains directories, you can use the 
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
-# certain files from those directories. Note that the wildcards are matched 
-# against the file with absolute path, so to exclude all test directories 
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
 # for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       = 
+EXCLUDE_PATTERNS       =
 
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
-# (namespaces, classes, functions, etc.) that should be excluded from the 
-# output. The symbol name can be a fully qualified name, a word, or if the 
-# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
 # AClass::ANamespace, ANamespace::*Test
 
-EXCLUDE_SYMBOLS        = 
+EXCLUDE_SYMBOLS        =
 
-# The EXAMPLE_PATH tag can be used to specify one or more files or 
-# directories that contain example code fragments that are included (see 
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
 # the \include command).
 
-EXAMPLE_PATH           = 
+EXAMPLE_PATH           =
 
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
 # blank all files are included.
 
 EXAMPLE_PATTERNS       = *
 
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
-# searched for input files to be used with the \include or \dontinclude 
-# commands irrespective of the value of the RECURSIVE tag. 
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
 # Possible values are YES and NO. If left blank NO is used.
 
 EXAMPLE_RECURSIVE      = NO
 
-# The IMAGE_PATH tag can be used to specify one or more files or 
-# directories that contain image that are included in the documentation (see 
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
 # the \image command).
 
-IMAGE_PATH             = 
+IMAGE_PATH             =
 
-# The INPUT_FILTER tag can be used to specify a program that doxygen should 
-# invoke to filter for each input file. Doxygen will invoke the filter program 
-# by executing (via popen()) the command <filter> <input-file>, where <filter> 
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
-# input file. Doxygen will then use the output that the filter program writes 
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be
 # ignored.
 
-INPUT_FILTER           = 
+INPUT_FILTER           =
 
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
-# basis.  Doxygen will compare the file name with each pattern and apply the 
-# filter if there is a match.  The filters are a list of the form: 
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
-# info on how filters are used. If FILTER_PATTERNS is empty or if 
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.  Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.  The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
 # non of the patterns match the file name, INPUT_FILTER is applied.
 
-FILTER_PATTERNS        = 
+FILTER_PATTERNS        =
 
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
-# INPUT_FILTER) will be used to filter the input files when producing source 
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
 # files to browse (i.e. when SOURCE_BROWSER is set to YES).
 
 FILTER_SOURCE_FILES    = NO
 
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file 
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any) 
-# and it is also possible to disable source filtering for a specific pattern 
-# using *.ext= (so without naming a filter). This option only has effect when 
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
 # FILTER_SOURCE_FILES is enabled.
 
-FILTER_SOURCE_PATTERNS = 
+FILTER_SOURCE_PATTERNS =
 
-# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that 
-# is part of the input, its contents will be placed on the main page (index.html). 
-# This can be useful if you have a project on for instance GitHub and want reuse 
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page (index.html).
+# This can be useful if you have a project on for instance GitHub and want reuse
 # the introduction page also for the doxygen output.
 
-USE_MDFILE_AS_MAINPAGE = 
+USE_MDFILE_AS_MAINPAGE =
 
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
 
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
-# be generated. Documented entities will be cross-referenced with these sources. 
-# Note: To get rid of all source code in the generated output, make sure also 
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
 # VERBATIM_HEADERS is set to NO.
 
 SOURCE_BROWSER         = NO
 
-# Setting the INLINE_SOURCES tag to YES will include the body 
+# Setting the INLINE_SOURCES tag to YES will include the body
 # of functions and classes directly in the documentation.
 
 INLINE_SOURCES         = NO
 
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
-# doxygen to hide any special comment blocks from generated source code 
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
 # fragments. Normal C, C++ and Fortran comments will always remain visible.
 
 STRIP_CODE_COMMENTS    = NO
 
-# If the REFERENCED_BY_RELATION tag is set to YES 
-# then for each documented function all documented 
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
 # functions referencing it will be listed.
 
 REFERENCED_BY_RELATION = NO
 
-# If the REFERENCES_RELATION tag is set to YES 
-# then for each documented function all documented entities 
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
 # called/used by that function will be listed.
 
 REFERENCES_RELATION    = NO
 
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
 # link to the source code.  Otherwise they will link to the documentation.
 
 REFERENCES_LINK_SOURCE = YES
 
-# If the USE_HTAGS tag is set to YES then the references to source code 
-# will point to the HTML generated by the htags(1) tool instead of doxygen 
-# built-in source browser. The htags tool is part of GNU's global source 
-# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
 # will need version 4.8.6 or higher.
 
 USE_HTAGS              = NO
 
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
-# will generate a verbatim copy of the header file for each class for 
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
 # which an include is specified. Set to NO to disable this.
 
 VERBATIM_HEADERS       = NO
@@ -891,170 +891,170 @@
 # configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
 
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
-# of all compounds will be generated. Enable this if the project 
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
 # contains a lot of classes, structs, unions or interfaces.
 
 ALPHABETICAL_INDEX     = NO
 
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
 # in which this list will be split (can be a number in the range [1..20])
 
 COLS_IN_ALPHA_INDEX    = 5
 
-# In case all classes in a project start with a common prefix, all 
-# classes will be put under the same header in the alphabetical index. 
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
 # should be ignored while generating the index headers.
 
-IGNORE_PREFIX          = 
+IGNORE_PREFIX          =
 
 #---------------------------------------------------------------------------
 # configuration options related to the HTML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
 # generate HTML output.
 
 GENERATE_HTML          = YES
 
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `html' will be used as the default path.
 
 HTML_OUTPUT            = $(HTML_OUTPUT)
 
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
 # doxygen will generate files with .html extension.
 
 HTML_FILE_EXTENSION    = .html
 
-# The HTML_HEADER tag can be used to specify a personal HTML header for 
-# each generated HTML page. If it is left blank doxygen will generate a 
-# standard header. Note that when using a custom header you are responsible  
-# for the proper inclusion of any scripts and style sheets that doxygen 
-# needs, which is dependent on the configuration options used. 
-# It is advised to generate a default header using "doxygen -w html 
-# header.html footer.html stylesheet.css YourConfigFile" and then modify 
-# that header. Note that the header is subject to change so you typically 
-# have to redo this when upgrading to a newer version of doxygen or when 
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
 # changing the value of configuration settings such as GENERATE_TREEVIEW!
 
 HTML_HEADER            = $(HTML_HEADER)
 
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
-# each generated HTML page. If it is left blank doxygen will generate a 
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
 # standard footer.
 
 HTML_FOOTER            = $(HTML_FOOTER)
 
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
-# style sheet that is used by each HTML page. It can be used to 
-# fine-tune the look of the HTML output. If left blank doxygen will 
-# generate a default style sheet. Note that it is recommended to use 
-# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this 
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
 # tag will in the future become obsolete.
 
-HTML_STYLESHEET        = 
+HTML_STYLESHEET        =
 
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional 
-# user-defined cascading style sheet that is included after the standard 
-# style sheets created by doxygen. Using this option one can overrule 
-# certain style aspects. This is preferred over using HTML_STYLESHEET 
-# since it does not replace the standard style sheet and is therefor more 
-# robust against future updates. Doxygen will copy the style sheet file to 
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
 # the output directory.
 
-HTML_EXTRA_STYLESHEET  = 
+HTML_EXTRA_STYLESHEET  =
 
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or 
-# other source files which should be copied to the HTML output directory. Note 
-# that these files will be copied to the base HTML output directory. Use the 
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these 
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that 
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
 # the files will be copied as-is; there are no commands or markers available.
 
-HTML_EXTRA_FILES       = 
+HTML_EXTRA_FILES       =
 
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. 
-# Doxygen will adjust the colors in the style sheet and background images 
-# according to this color. Hue is specified as an angle on a colorwheel, 
-# see http://en.wikipedia.org/wiki/Hue for more information. 
-# For instance the value 0 represents red, 60 is yellow, 120 is green, 
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. 
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
 # The allowed range is 0 to 359.
 
 HTML_COLORSTYLE_HUE    = 220
 
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of 
-# the colors in the HTML output. For a value of 0 the output will use 
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
 # grayscales only. A value of 255 will produce the most vivid colors.
 
 HTML_COLORSTYLE_SAT    = 0
 
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to 
-# the luminance component of the colors in the HTML output. Values below 
-# 100 gradually make the output lighter, whereas values above 100 make 
-# the output darker. The value divided by 100 is the actual gamma applied, 
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, 
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
 # and 100 does not change the gamma.
 
 HTML_COLORSTYLE_GAMMA  = 80
 
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML 
-# page will contain the date and time when the page was generated. Setting 
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
 # this to NO can help when comparing the output of multiple runs.
 
 HTML_TIMESTAMP         = YES
 
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
-# documentation will contain sections that can be hidden and shown after the 
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
 # page has loaded.
 
 HTML_DYNAMIC_SECTIONS  = NO
 
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of 
-# entries shown in the various tree structured indices initially; the user 
-# can expand and collapse entries dynamically later on. Doxygen will expand 
-# the tree to such a level that at most the specified number of entries are 
-# visible (unless a fully collapsed tree already exceeds this amount). 
-# So setting the number of entries 1 will produce a full collapsed tree by 
-# default. 0 is a special value representing an infinite number of entries 
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
 # and will result in a full expanded tree by default.
 
 HTML_INDEX_NUM_ENTRIES = 100
 
-# If the GENERATE_DOCSET tag is set to YES, additional index files 
-# will be generated that can be used as input for Apple's Xcode 3 
-# integrated development environment, introduced with OSX 10.5 (Leopard). 
-# To create a documentation set, doxygen will generate a Makefile in the 
-# HTML output directory. Running make will produce the docset in that 
-# directory and running "make install" will install the docset in 
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
-# it at startup. 
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html 
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
 # for more information.
 
 GENERATE_DOCSET        = NO
 
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
-# feed. A documentation feed provides an umbrella under which multiple 
-# documentation sets from a single provider (such as a company or product suite) 
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
 # can be grouped.
 
 DOCSET_FEEDNAME        = "Doxygen generated docs"
 
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
-# should uniquely identify the documentation set bundle. This should be a 
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
 # will append .docset to the name.
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely 
-# identify the documentation publisher. This should be a reverse domain-name 
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
 # style string, e.g. com.mycompany.MyDocSet.documentation.
 
 DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
@@ -1063,361 +1063,361 @@
 
 DOCSET_PUBLISHER_NAME  = Publisher
 
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
-# will be generated that can be used as input for tools like the 
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
 # of the generated HTML documentation.
 
 GENERATE_HTMLHELP      = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
-# be used to specify the file name of the resulting .chm file. You 
-# can add a path in front of the file if the result should not be 
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
 # written to the html output directory.
 
-CHM_FILE               = 
+CHM_FILE               =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
-# be used to specify the location (absolute path including file name) of 
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
 # the HTML help compiler on the generated index.hhp.
 
-HHC_LOCATION           = 
+HHC_LOCATION           =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
-# controls if a separate .chi index file is generated (YES) or that 
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
 # it should be included in the master .chm file (NO).
 
 GENERATE_CHI           = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING 
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file 
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
 # content.
 
-CHM_INDEX_ENCODING     = 
+CHM_INDEX_ENCODING     =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
-# controls whether a binary table of contents is generated (YES) or a 
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
 # normal table of contents (NO) in the .chm file.
 
 BINARY_TOC             = NO
 
-# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
 # to the contents of the HTML help documentation and to the tree view.
 
 TOC_EXPAND             = NO
 
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and 
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated 
-# that can be used as input for Qt's qhelpgenerator to generate a 
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
 # Qt Compressed Help (.qch) of the generated HTML documentation.
 
 GENERATE_QHP           = NO
 
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
-# be used to specify the file name of the resulting .qch file. 
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
 # The path specified is relative to the HTML output folder.
 
-QCH_FILE               = 
+QCH_FILE               =
 
-# The QHP_NAMESPACE tag specifies the namespace to use when generating 
-# Qt Help Project output. For more information please see 
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
 # http://doc.trolltech.com/qthelpproject.html#namespace
 
 QHP_NAMESPACE          = org.doxygen.Project
 
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
-# Qt Help Project output. For more information please see 
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
 # http://doc.trolltech.com/qthelpproject.html#virtual-folders
 
 QHP_VIRTUAL_FOLDER     = doc
 
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to 
-# add. For more information please see 
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
 # http://doc.trolltech.com/qthelpproject.html#custom-filters
 
-QHP_CUST_FILTER_NAME   = 
+QHP_CUST_FILTER_NAME   =
 
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the 
-# custom filter to add. For more information please see 
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> 
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
 # Qt Help Project / Custom Filters</a>.
 
-QHP_CUST_FILTER_ATTRS  = 
+QHP_CUST_FILTER_ATTRS  =
 
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this 
-# project's 
-# filter section matches. 
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> 
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
 # Qt Help Project / Filter Attributes</a>.
 
-QHP_SECT_FILTER_ATTRS  = 
+QHP_SECT_FILTER_ATTRS  =
 
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
-# be used to specify the location of Qt's qhelpgenerator. 
-# If non-empty doxygen will try to run qhelpgenerator on the generated 
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
 # .qhp file.
 
-QHG_LOCATION           = 
+QHG_LOCATION           =
 
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files  
-# will be generated, which together with the HTML files, form an Eclipse help 
-# plugin. To install this plugin and make it available under the help contents 
-# menu in Eclipse, the contents of the directory containing the HTML and XML 
-# files needs to be copied into the plugins directory of eclipse. The name of 
-# the directory within the plugins directory should be the same as 
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before 
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
 # the help appears.
 
 GENERATE_ECLIPSEHELP   = NO
 
-# A unique identifier for the eclipse help plugin. When installing the plugin 
-# the directory name containing the HTML and XML files should also have 
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
 # this name.
 
 ECLIPSE_DOC_ID         = org.doxygen.Project
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) 
-# at top of each HTML page. The value NO (the default) enables the index and 
-# the value YES disables it. Since the tabs have the same information as the 
-# navigation tree you can set this option to NO if you already set 
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
 # GENERATE_TREEVIEW to YES.
 
 DISABLE_INDEX          = YES
 
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index 
-# structure should be generated to display hierarchical information. 
-# If the tag value is set to YES, a side panel will be generated 
-# containing a tree-like index structure (just like the one that 
-# is generated for HTML Help). For this to work a browser that supports 
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). 
-# Windows users are probably better off using the HTML help feature. 
-# Since the tree basically has the same information as the tab index you 
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
 # could consider to set DISABLE_INDEX to NO when enabling this option.
 
 GENERATE_TREEVIEW      = NO
 
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values 
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML 
-# documentation. Note that a value of 0 will completely suppress the enum 
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
 # values from appearing in the overview section.
 
 ENUM_VALUES_PER_LINE   = 4
 
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
-# used to set the initial width (in pixels) of the frame in which the tree 
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
 # is shown.
 
 TREEVIEW_WIDTH         = 250
 
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open 
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
 # links to external symbols imported via tag files in a separate window.
 
 EXT_LINKS_IN_WINDOW    = NO
 
-# Use this tag to change the font size of Latex formulas included 
-# as images in the HTML documentation. The default is 10. Note that 
-# when you change the font size after a successful doxygen run you need 
-# to manually remove any form_*.png images from the HTML output directory 
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
 # to force them to be regenerated.
 
 FORMULA_FONTSIZE       = 10
 
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images 
-# generated for formulas are transparent PNGs. Transparent PNGs are 
-# not supported properly for IE 6.0, but are supported on all modern browsers. 
-# Note that when changing this option you need to delete any form_*.png files 
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
 # in the HTML output before the changes have effect.
 
 FORMULA_TRANSPARENT    = YES
 
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax 
-# (see http://www.mathjax.org) which uses client side Javascript for the 
-# rendering instead of using prerendered bitmaps. Use this if you do not 
-# have LaTeX installed or if you want to formulas look prettier in the HTML 
-# output. When enabled you may also need to install MathJax separately and 
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
 # configure the path to it using the MATHJAX_RELPATH option.
 
 USE_MATHJAX            = NO
 
-# When MathJax is enabled you can set the default output format to be used for 
-# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and 
-# SVG. The default value is HTML-CSS, which is slower, but has the best 
+# When MathJax is enabled you can set the default output format to be used for
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
 # compatibility.
 
 MATHJAX_FORMAT         = HTML-CSS
 
-# When MathJax is enabled you need to specify the location relative to the 
-# HTML output directory using the MATHJAX_RELPATH option. The destination 
-# directory should contain the MathJax.js script. For instance, if the mathjax 
-# directory is located at the same level as the HTML output directory, then 
-# MATHJAX_RELPATH should be ../mathjax. The default value points to 
-# the MathJax Content Delivery Network so you can quickly see the result without 
-# installing MathJax.  However, it is strongly recommended to install a local 
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.  However, it is strongly recommended to install a local
 # copy of MathJax from http://www.mathjax.org before deployment.
 
 MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
 
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension 
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
 # names that should be enabled during MathJax rendering.
 
-MATHJAX_EXTENSIONS     = 
+MATHJAX_EXTENSIONS     =
 
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box 
-# for the HTML output. The underlying search engine uses javascript 
-# and DHTML and should work on any modern browser. Note that when using 
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets 
-# (GENERATE_DOCSET) there is already a search function so this one should 
-# typically be disabled. For large projects the javascript based search engine 
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
 # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
 
 SEARCHENGINE           = NO
 
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be 
-# implemented using a web server instead of a web client using Javascript. 
-# There are two flavours of web server based search depending on the 
-# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for 
-# searching and an index file used by the script. When EXTERNAL_SEARCH is 
-# enabled the indexing and searching needs to be provided by external tools. 
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
 # See the manual for details.
 
 SERVER_BASED_SEARCH    = NO
 
-# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP 
-# script for searching. Instead the search results are written to an XML file 
-# which needs to be processed by an external indexer. Doxygen will invoke an 
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain 
-# the search results. Doxygen ships with an example indexer (doxyindexer) and 
-# search engine (doxysearch.cgi) which are based on the open source search engine 
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search engine
 # library Xapian. See the manual for configuration details.
 
 EXTERNAL_SEARCH        = NO
 
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server 
-# which will returned the search results when EXTERNAL_SEARCH is enabled. 
-# Doxygen ships with an example search engine (doxysearch) which is based on 
-# the open source search engine library Xapian. See the manual for configuration 
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
 # details.
 
-SEARCHENGINE_URL       = 
+SEARCHENGINE_URL       =
 
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed 
-# search data is written to a file for indexing by an external tool. With the 
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
 # SEARCHDATA_FILE tag the name of this file can be specified.
 
 SEARCHDATA_FILE        = searchdata.xml
 
-# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the 
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is 
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple 
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
 # projects and redirect the results back to the right project.
 
-EXTERNAL_SEARCH_ID     = 
+EXTERNAL_SEARCH_ID     =
 
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen 
-# projects other than the one defined by this configuration file, but that are 
-# all added to the same external search index. Each project needs to have a 
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id 
-# of to a relative location where the documentation can be found. 
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
+# of to a relative location where the documentation can be found.
 # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
 
-EXTRA_SEARCH_MAPPINGS  = 
+EXTRA_SEARCH_MAPPINGS  =
 
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
 # generate Latex output.
 
 GENERATE_LATEX         = NO
 
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `latex' will be used as the default path.
 
 LATEX_OUTPUT           = latex
 
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
-# invoked. If left blank `latex' will be used as the default command name. 
-# Note that when enabling USE_PDFLATEX this option is only used for 
-# generating bitmaps for formulas in the HTML output, but not in the 
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
 # Makefile that is written to the output directory.
 
 LATEX_CMD_NAME         = latex
 
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
-# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
 # default command name.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
-# LaTeX documents. This may be useful for small projects and may help to 
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
 # save some trees in general.
 
 COMPACT_LATEX          = NO
 
-# The PAPER_TYPE tag can be used to set the paper type that is used 
-# by the printer. Possible values are: a4, letter, legal and 
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
 # executive. If left blank a4wide will be used.
 
 PAPER_TYPE             = a4
 
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
 # packages that should be included in the LaTeX output.
 
-EXTRA_PACKAGES         = 
+EXTRA_PACKAGES         =
 
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
-# the generated latex document. The header should contain everything until 
-# the first chapter. If it is left blank doxygen will generate a 
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
 # standard header. Notice: only use this tag if you know what you are doing!
 
-LATEX_HEADER           = 
+LATEX_HEADER           =
 
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for 
-# the generated latex document. The footer should contain everything after 
-# the last chapter. If it is left blank doxygen will generate a 
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
 # standard footer. Notice: only use this tag if you know what you are doing!
 
-LATEX_FOOTER           = 
+LATEX_FOOTER           =
 
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
-# contain links (just like the HTML output) instead of page references 
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
 # This makes the output suitable for online browsing using a pdf viewer.
 
 PDF_HYPERLINKS         = YES
 
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
-# plain latex in the generated Makefile. Set this option to YES to get a 
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
 # higher quality PDF documentation.
 
 USE_PDFLATEX           = YES
 
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
-# command to the generated LaTeX files. This will instruct LaTeX to keep 
-# running if errors occur, instead of asking the user for help. 
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
 # This option is also used when generating formulas in HTML.
 
 LATEX_BATCHMODE        = NO
 
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
-# include the index chapters (such as File Index, Compound Index, etc.) 
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
 # in the output.
 
 LATEX_HIDE_INDICES     = NO
 
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include 
-# source code with syntax highlighting in the LaTeX output. 
-# Note that which sources are shown also depends on other settings 
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
 # such as SOURCE_BROWSER.
 
 LATEX_SOURCE_CODE      = NO
 
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the 
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See 
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
 # http://en.wikipedia.org/wiki/BibTeX for more info.
 
 LATEX_BIB_STYLE        = plain
@@ -1426,68 +1426,68 @@
 # configuration options related to the RTF output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
-# The RTF output is optimized for Word 97 and may not look very pretty with 
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
 # other RTF readers or editors.
 
 GENERATE_RTF           = NO
 
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `rtf' will be used as the default path.
 
 RTF_OUTPUT             = rtf
 
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
-# RTF documents. This may be useful for small projects and may help to 
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
 # save some trees in general.
 
 COMPACT_RTF            = NO
 
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
-# will contain hyperlink fields. The RTF file will 
-# contain links (just like the HTML output) instead of page references. 
-# This makes the output suitable for online browsing using WORD or other 
-# programs which support those fields. 
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
 # Note: wordpad (write) and others do not support links.
 
 RTF_HYPERLINKS         = NO
 
-# Load style sheet definitions from file. Syntax is similar to doxygen's 
-# config file, i.e. a series of assignments. You only have to provide 
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
 # replacements, missing definitions are set to their default value.
 
-RTF_STYLESHEET_FILE    = 
+RTF_STYLESHEET_FILE    =
 
-# Set optional variables used in the generation of an rtf document. 
+# Set optional variables used in the generation of an rtf document.
 # Syntax is similar to doxygen's config file.
 
-RTF_EXTENSIONS_FILE    = 
+RTF_EXTENSIONS_FILE    =
 
 #---------------------------------------------------------------------------
 # configuration options related to the man page output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
 # generate man pages
 
 GENERATE_MAN           = NO
 
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `man' will be used as the default path.
 
 MAN_OUTPUT             = man
 
-# The MAN_EXTENSION tag determines the extension that is added to 
+# The MAN_EXTENSION tag determines the extension that is added to
 # the generated man pages (default is the subroutine's section .3)
 
 MAN_EXTENSION          = .3
 
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
-# then it will generate one additional man file for each entity 
-# documented in the real man page(s). These additional files 
-# only source the real man page, but without them the man command 
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
 # would be unable to find the correct page. The default is NO.
 
 MAN_LINKS              = NO
@@ -1496,33 +1496,33 @@
 # configuration options related to the XML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_XML tag is set to YES Doxygen will 
-# generate an XML file that captures the structure of 
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
 # the code including all documentation.
 
 GENERATE_XML           = NO
 
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `xml' will be used as the default path.
 
 XML_OUTPUT             = xml
 
-# The XML_SCHEMA tag can be used to specify an XML schema, 
-# which can be used by a validating XML parser to check the 
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
 # syntax of the XML files.
 
-XML_SCHEMA             = 
+XML_SCHEMA             =
 
-# The XML_DTD tag can be used to specify an XML DTD, 
-# which can be used by a validating XML parser to check the 
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
 # syntax of the XML files.
 
-XML_DTD                = 
+XML_DTD                =
 
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
-# dump the program listings (including syntax highlighting 
-# and cross-referencing information) to the XML output. Note that 
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
 # enabling this will significantly increase the size of the XML output.
 
 XML_PROGRAMLISTING     = YES
@@ -1531,10 +1531,10 @@
 # configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
-# generate an AutoGen Definitions (see autogen.sf.net) file 
-# that captures the structure of the code including all 
-# documentation. Note that this feature is still experimental 
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
 # and incomplete at the moment.
 
 GENERATE_AUTOGEN_DEF   = NO
@@ -1543,97 +1543,97 @@
 # configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
-# generate a Perl module file that captures the structure of 
-# the code including all documentation. Note that this 
-# feature is still experimental and incomplete at the 
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
 # moment.
 
 GENERATE_PERLMOD       = NO
 
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
 # to generate PDF and DVI output from the Perl module output.
 
 PERLMOD_LATEX          = NO
 
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
-# nicely formatted so it can be parsed by a human reader.  This is useful 
-# if you want to understand what is going on.  On the other hand, if this 
-# tag is set to NO the size of the Perl module output will be much smaller 
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.  This is useful
+# if you want to understand what is going on.  On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
 # and Perl will parse it just the same.
 
 PERLMOD_PRETTY         = YES
 
-# The names of the make variables in the generated doxyrules.make file 
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
-# This is useful so different doxyrules.make files included by the same 
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
 # Makefile don't overwrite each other's variables.
 
-PERLMOD_MAKEVAR_PREFIX = 
+PERLMOD_MAKEVAR_PREFIX =
 
 #---------------------------------------------------------------------------
 # Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
-# evaluate all C-preprocessor directives found in the sources and include 
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
 # files.
 
-ENABLE_PREPROCESSING   = YES
+ENABLE_PREPROCESSING   = NO
 
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
-# names in the source code. If set to NO (the default) only conditional 
-# compilation will be performed. Macro expansion can be done in a controlled 
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
 # way by setting EXPAND_ONLY_PREDEF to YES.
 
 MACRO_EXPANSION        = NO
 
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
-# then the macro expansion is limited to the macros specified with the 
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
 # PREDEFINED and EXPAND_AS_DEFINED tags.
 
 EXPAND_ONLY_PREDEF     = NO
 
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
 # pointed to by INCLUDE_PATH will be searched when a #include is found.
 
 SEARCH_INCLUDES        = YES
 
-# The INCLUDE_PATH tag can be used to specify one or more directories that 
-# contain include files that are not input files but should be processed by 
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
 # the preprocessor.
 
-INCLUDE_PATH           = 
+INCLUDE_PATH           =
 
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
-# patterns (like *.h and *.hpp) to filter out the header-files in the 
-# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
 # be used.
 
-INCLUDE_FILE_PATTERNS  = 
+INCLUDE_FILE_PATTERNS  =
 
-# The PREDEFINED tag can be used to specify one or more macro names that 
-# are defined before the preprocessor is started (similar to the -D option of 
-# gcc). The argument of the tag is a list of macros of the form: name 
-# or name=definition (no spaces). If the definition and the = are 
-# omitted =1 is assumed. To prevent a macro definition from being 
-# undefined via #undef or recursively expanded use the := operator 
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
 # instead of the = operator.
 
-PREDEFINED             = 
+PREDEFINED             =
 
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
-# this tag can be used to specify a list of macro names that should be expanded. 
-# The macro definition that is found in the sources will be used. 
-# Use the PREDEFINED tag if you want to use a different macro definition that 
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
 # overrules the definition found in the source code.
 
-EXPAND_AS_DEFINED      = 
+EXPAND_AS_DEFINED      =
 
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
-# doxygen's preprocessor will remove all references to function-like macros 
-# that are alone on a line, have an all uppercase name, and do not end with a 
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
 # semicolon, because these will confuse the parser if not removed.
 
 SKIP_FUNCTION_MACROS   = YES
@@ -1642,37 +1642,37 @@
 # Configuration::additions related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles. For each 
-# tag file the location of the external documentation should be added. The 
-# format of a tag file without this location is as follows: 
-#   TAGFILES = file1 file2 ... 
-# Adding location for the tag files is done as follows: 
-#   TAGFILES = file1=loc1 "file2 = loc2" ... 
-# where "loc1" and "loc2" can be relative or absolute paths 
-# or URLs. Note that each tag file must have a unique name (where the name does 
-# NOT include the path). If a tag file is not located in the directory in which 
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#   TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#   TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
 # doxygen is run, you must also specify the path to the tagfile here.
 
-TAGFILES               = 
+TAGFILES               =
 
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
 # a tag file that is based on the input files it reads.
 
-GENERATE_TAGFILE       = 
+GENERATE_TAGFILE       =
 
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
-# in the class index. If set to NO only the inherited external classes 
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
 # will be listed.
 
 ALLEXTERNALS           = NO
 
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
-# in the modules index. If set to NO, only the current project's groups will 
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
 # be listed.
 
 EXTERNAL_GROUPS        = YES
 
-# The PERL_PATH should be the absolute path and name of the perl script 
+# The PERL_PATH should be the absolute path and name of the perl script
 # interpreter (i.e. the result of `which perl').
 
 PERL_PATH              = /usr/bin/perl
@@ -1681,222 +1681,222 @@
 # Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
-# or super classes. Setting the tag to NO turns the diagrams off. Note that 
-# this option also works with HAVE_DOT disabled, but it is recommended to 
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
 # install and use dot, since it yields more powerful graphs.
 
 CLASS_DIAGRAMS         = NO
 
-# You can define message sequence charts within doxygen comments using the \msc 
-# command. Doxygen will then run the mscgen tool (see 
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
-# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
 # default search path.
 
-MSCGEN_PATH            = 
+MSCGEN_PATH            =
 
-# If set to YES, the inheritance and collaboration graphs will hide 
-# inheritance and usage relations if the target is undocumented 
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
 # or is not a class.
 
 HIDE_UNDOC_RELATIONS   = YES
 
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
-# available from the path. This tool is part of Graphviz, a graph visualization 
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
 # have no effect if this option is set to NO (the default)
 
 HAVE_DOT               = NO
 
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is 
-# allowed to run in parallel. When set to 0 (the default) doxygen will 
-# base this on the number of processors available in the system. You can set it 
-# explicitly to a value larger than 0 to get control over the balance 
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
 # between CPU load and processing speed.
 
 DOT_NUM_THREADS        = 0
 
-# By default doxygen will use the Helvetica font for all dot files that 
-# doxygen generates. When you want a differently looking font you can specify 
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find 
-# the font, which can be done by putting it in a standard location or by setting 
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the 
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
 # directory containing the font.
 
 DOT_FONTNAME           = Helvetica
 
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. 
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
 # The default size is 10pt.
 
 DOT_FONTSIZE           = 10
 
-# By default doxygen will tell dot to use the Helvetica font. 
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to 
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
 # set the path where dot can find it.
 
-DOT_FONTPATH           = 
+DOT_FONTPATH           =
 
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect inheritance relations. Setting this tag to YES will force the 
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
 # CLASS_DIAGRAMS tag to NO.
 
 CLASS_GRAPH            = YES
 
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect implementation dependencies (inheritance, containment, and 
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
 # class references variables) of the class with other documented classes.
 
 COLLABORATION_GRAPH    = YES
 
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
 # will generate a graph for groups, showing the direct groups dependencies
 
 GROUP_GRAPHS           = YES
 
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
-# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
 # Language.
 
 UML_LOOK               = NO
 
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside 
-# the class node. If there are many fields or methods and many nodes the 
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS 
-# threshold limits the number of items for each type to make the size more 
-# managable. Set this to 0 for no limit. Note that the threshold may be 
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
 # exceeded by 50% before the limit is enforced.
 
 UML_LIMIT_NUM_FIELDS   = 10
 
-# If set to YES, the inheritance and collaboration graphs will show the 
+# If set to YES, the inheritance and collaboration graphs will show the
 # relations between templates and their instances.
 
 TEMPLATE_RELATIONS     = NO
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
-# tags are set to YES then doxygen will generate a graph for each documented 
-# file showing the direct and indirect include dependencies of the file with 
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
 # other documented files.
 
 INCLUDE_GRAPH          = YES
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
-# documented header file showing the documented files that directly or 
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
 # indirectly include this file.
 
 INCLUDED_BY_GRAPH      = YES
 
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
-# doxygen will generate a call dependency graph for every global function 
-# or class method. Note that enabling this option will significantly increase 
-# the time of a run. So in most cases it will be better to enable call graphs 
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
 # for selected functions only using the \callgraph command.
 
 CALL_GRAPH             = NO
 
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
-# doxygen will generate a caller dependency graph for every global function 
-# or class method. Note that enabling this option will significantly increase 
-# the time of a run. So in most cases it will be better to enable caller 
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
 # graphs for selected functions only using the \callergraph command.
 
 CALLER_GRAPH           = NO
 
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
 # will generate a graphical hierarchy of all classes instead of a textual one.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES 
-# then doxygen will show the dependencies a directory has on other directories 
-# in a graphical way. The dependency relations are determined by the #include 
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
 # relations between the files in the directories.
 
 DIRECTORY_GRAPH        = YES
 
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
-# generated by dot. Possible values are svg, png, jpg, or gif. 
-# If left blank png will be used. If you choose svg you need to set 
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files 
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
 # visible in IE 9+ (other browsers do not have this requirement).
 
 DOT_IMAGE_FORMAT       = png
 
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to 
-# enable generation of interactive SVG images that allow zooming and panning. 
-# Note that this requires a modern browser other than Internet Explorer. 
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you 
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files 
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
 # visible. Older versions of IE do not have SVG support.
 
 INTERACTIVE_SVG        = NO
 
-# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
 
-DOT_PATH               = 
+DOT_PATH               =
 
-# The DOTFILE_DIRS tag can be used to specify one or more directories that 
-# contain dot files that are included in the documentation (see the 
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
 # \dotfile command).
 
-DOTFILE_DIRS           = 
+DOTFILE_DIRS           =
 
-# The MSCFILE_DIRS tag can be used to specify one or more directories that 
-# contain msc files that are included in the documentation (see the 
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
 # \mscfile command).
 
-MSCFILE_DIRS           = 
+MSCFILE_DIRS           =
 
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
-# nodes that will be shown in the graph. If the number of nodes in a graph 
-# becomes larger than this value, doxygen will truncate the graph, which is 
-# visualized by representing a node as a red box. Note that doxygen if the 
-# number of direct children of the root node in a graph is already larger than 
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
 # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
 
 DOT_GRAPH_MAX_NODES    = 50
 
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
-# graphs generated by dot. A depth value of 3 means that only nodes reachable 
-# from the root by following a path via at most 3 edges will be shown. Nodes 
-# that lay further from the root node will be omitted. Note that setting this 
-# option to 1 or 2 may greatly reduce the computation time needed for large 
-# code bases. Also note that the size of a graph can be further restricted by 
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
 
 MAX_DOT_GRAPH_DEPTH    = 0
 
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
-# background. This is disabled by default, because dot on Windows does not 
-# seem to support this out of the box. Warning: Depending on the platform used, 
-# enabling this option may lead to badly anti-aliased labels on the edges of 
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
 # a graph (i.e. they become hard to read).
 
 DOT_TRANSPARENT        = NO
 
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
-# files in one run (i.e. multiple -o and -T options on the command line). This 
-# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
 # support this, this feature is disabled by default.
 
 DOT_MULTI_TARGETS      = NO
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
-# generate a legend page explaining the meaning of the various boxes and 
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
 # arrows in the dot generated graphs.
 
 GENERATE_LEGEND        = YES
 
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
-# remove the intermediate dot files that are used to generate 
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
 # the various graphs.
 
 DOT_CLEANUP            = YES
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 8e10f67..6287332 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -267,6 +267,36 @@
     ACONFIGURATION_SCREENROUND_NO = 0x1,
     ACONFIGURATION_SCREENROUND_YES = 0x2,
 
+    /** Wide color gamut: not specified. */
+    ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00,
+    /**
+     * Wide color gamut: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no
+     * nowidecg</a> resource qualifier specified.
+     */
+    ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1,
+    /**
+     * Wide color gamut: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">
+     * widecg</a> resource qualifier specified.
+     */
+    ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2,
+
+    /** HDR: not specified. */
+    ACONFIGURATION_HDR_ANY = 0x00,
+    /**
+     * HDR: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+     * lowdr</a> resource qualifier specified.
+     */
+    ACONFIGURATION_HDR_NO = 0x1,
+    /**
+     * HDR: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">
+     * highdr</a> resource qualifier specified.
+     */
+    ACONFIGURATION_HDR_YES = 0x2,
+
     /** UI mode: not specified. */
     ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
     /**
@@ -300,6 +330,11 @@
      * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified.
      */
     ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06,
+    /**
+     * UI mode: value that corresponds to
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified.
+     */
+    ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07,
 
     /** UI night mode: not specified.*/
     ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
@@ -426,6 +461,12 @@
     ACONFIGURATION_LAYOUTDIR = 0x4000,
     ACONFIGURATION_SCREEN_ROUND = 0x8000,
     /**
+     * Bit mask for
+     * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a>
+     * and <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations.
+     */
+    ACONFIGURATION_COLOR_MODE = 0x10000,
+    /**
      * Constant used to to represent MNC (Mobile Network Code) zero.
      * 0 cannot be used, since it is used to represent an undefined MNC.
      */
diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h
new file mode 100644
index 0000000..6020870
--- /dev/null
+++ b/include/android/hardware_buffer_jni.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file hardware_buffer_jni.h
+ */
+
+#ifndef ANDROID_HARDWARE_BUFFER_JNI_H
+#define ANDROID_HARDWARE_BUFFER_JNI_H
+
+#include <sys/cdefs.h>
+
+#include <android/hardware_buffer.h>
+
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+ * Return the AHardwareBuffer associated with a Java HardwareBuffer object,
+ * for interacting with it through native code.  This acquires a reference
+ * on the AHardwareBuffer that is returned; be sure to use
+ * AHardwareBuffer_release() when done with it so that it doesn't leak.
+ */
+AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env,
+        jobject hardwareBufferObj);
+
+/**
+ * Return a new Java HardwareBuffer object that wraps the passed native
+ * AHardwareBuffer object.
+ */
+jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env,
+        AHardwareBuffer* hardwareBuffer);
+
+__END_DECLS
+
+#endif // ANDROID_HARDWARE_BUFFER_JNI_H
diff --git a/include/android/input.h b/include/android/input.h
index f928c6e..0829989 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -835,6 +835,8 @@
     AINPUT_SOURCE_BLUETOOTH_STYLUS = 0x00008000 | AINPUT_SOURCE_STYLUS,
     /** trackball */
     AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
+    /** mouse relative */
+    AINPUT_SOURCE_MOUSE_RELATIVE = 0x00020000 | AINPUT_SOURCE_CLASS_NAVIGATION,
     /** touchpad */
     AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
     /** navigation */
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h
index be01518..97892f8 100644
--- a/include/android/multinetwork.h
+++ b/include/android/multinetwork.h
@@ -56,12 +56,10 @@
 /**
  * Set the network to be used by the given socket file descriptor.
  *
- * To clear a previous socket binding invoke with NETWORK_UNSPECIFIED.
+ * To clear a previous socket binding, invoke with NETWORK_UNSPECIFIED.
  *
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.Network#bindSocket()](https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket))
  *
- *     [ android.net.Network#bindSocket() ]
- *     https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket)
  */
 int android_setsocknetwork(net_handle_t network, int fd);
 
@@ -75,12 +73,10 @@
  * resolutions will fail.  This is by design so an application doesn't
  * accidentally use sockets it thinks are still bound to a particular network.
  *
- * To clear a previous process binding invoke with NETWORK_UNSPECIFIED.
+ * To clear a previous process binding, invoke with NETWORK_UNSPECIFIED.
  *
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network))
  *
- *     [ android.net.ConnectivityManager#setProcessDefaultNetwork() ]
- *     https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network)
  */
 int android_setprocnetwork(net_handle_t network);
 
@@ -96,10 +92,8 @@
  *     - either |node| or |service| may be NULL, but not both
  *     - |res| must not be NULL
  *
- * This is the equivalent of:
+ * This is the equivalent of: [android.net.Network#getAllByName()](https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String))
  *
- *     [ android.net.Network#getAllByName() ]
- *     https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String)
  */
 int android_getaddrinfofornetwork(net_handle_t network,
         const char *node, const char *service,
diff --git a/include/android/native_window.h b/include/android/native_window.h
deleted file mode 100644
index b60b9f1..0000000
--- a/include/android/native_window.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @addtogroup NativeActivity Native Activity
- * @{
- */
-
-/**
- * @file native_window.h
- */
-
-#ifndef ANDROID_NATIVE_WINDOW_H
-#define ANDROID_NATIVE_WINDOW_H
-
-#include <sys/cdefs.h>
-
-#include <android/rect.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Pixel formats that a window can use.
- */
-enum {
-    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
-    WINDOW_FORMAT_RGBA_8888          = 1,
-    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
-    WINDOW_FORMAT_RGBX_8888          = 2,
-    /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
-    WINDOW_FORMAT_RGB_565            = 4,
-};
-
-struct ANativeWindow;
-/**
- * {@link ANativeWindow} is opaque type that provides access to a native window.
- *
- * A pointer can be obtained using ANativeWindow_fromSurface().
- */
-typedef struct ANativeWindow ANativeWindow;
-
-/**
- * {@link ANativeWindow} is a struct that represents a windows buffer.
- *
- * A pointer can be obtained using ANativeWindow_lock().
- */
-typedef struct ANativeWindow_Buffer {
-    // The number of pixels that are show horizontally.
-    int32_t width;
-
-    // The number of pixels that are shown vertically.
-    int32_t height;
-
-    // The number of *pixels* that a line in the buffer takes in
-    // memory.  This may be >= width.
-    int32_t stride;
-
-    // The format of the buffer.  One of WINDOW_FORMAT_*
-    int32_t format;
-
-    // The actual bits.
-    void* bits;
-
-    // Do not touch.
-    uint32_t reserved[6];
-} ANativeWindow_Buffer;
-
-/**
- * Acquire a reference on the given ANativeWindow object.  This prevents the object
- * from being deleted until the reference is removed.
- */
-void ANativeWindow_acquire(ANativeWindow* window);
-
-/**
- * Remove a reference that was previously acquired with ANativeWindow_acquire().
- */
-void ANativeWindow_release(ANativeWindow* window);
-
-/**
- * Return the current width in pixels of the window surface.  Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getWidth(ANativeWindow* window);
-
-/**
- * Return the current height in pixels of the window surface.  Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getHeight(ANativeWindow* window);
-
-/**
- * Return the current pixel format of the window surface.  Returns a
- * negative value on error.
- */
-int32_t ANativeWindow_getFormat(ANativeWindow* window);
-
-/**
- * Change the format and size of the window buffers.
- *
- * The width and height control the number of pixels in the buffers, not the
- * dimensions of the window on screen.  If these are different than the
- * window's physical size, then it buffer will be scaled to match that size
- * when compositing it to the screen.
- *
- * For all of these parameters, if 0 is supplied then the window's base
- * value will come back in force.
- *
- * width and height must be either both zero or both non-zero.
- *
- */
-int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
-        int32_t width, int32_t height, int32_t format);
-
-/**
- * Lock the window's next drawing surface for writing.
- * inOutDirtyBounds is used as an in/out parameter, upon entering the
- * function, it contains the dirty region, that is, the region the caller
- * intends to redraw. When the function returns, inOutDirtyBounds is updated
- * with the actual area the caller needs to redraw -- this region is often
- * extended by ANativeWindow_lock.
- */
-int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
-        ARect* inOutDirtyBounds);
-
-/**
- * Unlock the window's drawing surface after previously locking it,
- * posting the new buffer to the display.
- */
-int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
-
-#ifdef __cplusplus
-};
-#endif
-
-#endif // ANDROID_NATIVE_WINDOW_H
-
-/** @} */
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 4a00818..186f62c 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -48,14 +48,21 @@
  *
  */
 
-#include <sys/types.h>
-
 #include <android/looper.h>
 
+#include <sys/types.h>
+#include <math.h>
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+#define ASENSOR_RESOLUTION_INVALID     (nanf(""))
+#define ASENSOR_FIFO_COUNT_INVALID     (-1)
+#define ASENSOR_DELAY_INVALID          INT32_MIN
 
 /**
  * Sensor types.
@@ -63,6 +70,10 @@
  */
 enum {
     /**
+     * Invalid sensor type. Returned by {@link ASensor_getType} as error value.
+     */
+    ASENSOR_TYPE_INVALID = -1,
+    /**
      * {@link ASENSOR_TYPE_ACCELEROMETER}
      * reporting-mode: continuous
      *
@@ -134,6 +145,8 @@
  * Sensor Reporting Modes.
  */
 enum {
+    /** invalid reporting mode */
+    AREPORTING_MODE_INVALID = -1,
     /** continuous reporting */
     AREPORTING_MODE_CONTINUOUS = 0,
     /** reporting on change */
@@ -144,6 +157,30 @@
     AREPORTING_MODE_SPECIAL_TRIGGER = 3
 };
 
+/**
+ * Sensor Direct Report Rates.
+ */
+enum {
+    /** stopped */
+    ASENSOR_DIRECT_RATE_STOP = 0,
+    /** nominal 50Hz */
+    ASENSOR_DIRECT_RATE_NORMAL = 1,
+    /** nominal 200Hz */
+    ASENSOR_DIRECT_RATE_FAST = 2,
+    /** nominal 800Hz */
+    ASENSOR_DIRECT_RATE_VERY_FAST = 3
+};
+
+/**
+ * Sensor Direct Channel Type.
+ */
+enum {
+    /** shared memory created by ASharedMemory_create */
+    ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY = 1,
+    /** AHardwareBuffer */
+    ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER = 2
+};
+
 /*
  * A few useful constants
  */
@@ -372,8 +409,7 @@
  * Returns the default sensor with the given type and wakeUp properties or NULL if no sensor
  * of this type and wakeUp properties exists.
  */
-ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type,
-        bool wakeUp);
+ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp);
 #endif
 
 /**
@@ -391,6 +427,97 @@
  */
 int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* queue);
 
+#if __ANDROID_API__ >= __ANDROID_API_O__
+/**
+ * Create direct channel based on shared memory
+ *
+ * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} to be used
+ * for configuring sensor direct report.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ *                {@link ASensorManager_getInstanceForPackage}.
+ * \param fd      file descriptor representing a shared memory created by
+ *                {@link ASharedMemory_create}
+ * \param size    size to be used, must be less or equal to size of shared memory.
+ *
+ * \return a positive integer as a channel id to be used in
+ *         {@link ASensorManager_destroyDirectChannel} and
+ *         {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures.
+ */
+int ASensorManager_createSharedMemoryDirectChannel(ASensorManager* manager, int fd, size_t size);
+
+/**
+ * Create direct channel based on AHardwareBuffer
+ *
+ * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER} type to be used
+ * for configuring sensor direct report.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ *                {@link ASensorManager_getInstanceForPackage}.
+ * \param buffer  {@link AHardwareBuffer} instance created by {@link AHardwareBuffer_allocate}.
+ * \param size    the intended size to be used, must be less or equal to size of buffer.
+ *
+ * \return a positive integer as a channel id to be used in
+ *         {@link ASensorManager_destroyDirectChannel} and
+ *         {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures.
+ */
+int ASensorManager_createHardwareBufferDirectChannel(
+        ASensorManager* manager, AHardwareBuffer const * buffer, size_t size);
+
+/**
+ * Destroy a direct channel
+ *
+ * Destroy a direct channel previously created using {@link ASensorManager_createDirectChannel}.
+ * The buffer used for creating direct channel does not get destroyed with
+ * {@link ASensorManager_destroy} and has to be close or released separately.
+ *
+ * \param manager the {@link ASensorManager} instance obtained from
+ *                {@link ASensorManager_getInstanceForPackage}.
+ * \param channelId channel id (a positive integer) returned from
+ *                  {@link ASensorManager_createSharedMemoryDirectChannel} or
+ *                  {@link ASensorManager_createHardwareBufferDirectChannel}.
+ */
+void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId);
+
+/**
+ * Configure direct report on channel
+ *
+ * Configure sensor direct report on a direct channel: set rate to value other than
+ * {@link ASENSOR_DIRECT_RATE_STOP} so that sensor event can be directly
+ * written into the shared memory region used for creating the buffer; set rate to
+ * {@link ASENSOR_DIRECT_RATE_STOP} will stop the sensor direct report.
+ *
+ * To stop all active sensor direct report configured to a channel, set sensor to NULL and rate to
+ * {@link ASENSOR_DIRECT_RATE_STOP}.
+ *
+ * In order to successfully configure a direct report, the sensor has to support the specified rate
+ * and the channel type, which can be checked by {@link ASensor_getHighestDirectReportRateLevel} and
+ * {@link ASensor_isDirectChannelTypeSupported}, respectively.
+ *
+ * Example:
+ * \code{.cpp}
+ *      ASensorManager *manager = ...;
+ *      ASensor *sensor = ...;
+ *      int channelId = ...;
+ *
+ *      ASensorManager_configureDirectReport(
+ *              manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
+ * \endcode
+ *
+ * \param manager   the {@link ASensorManager} instance obtained from
+ *                  {@link ASensorManager_getInstanceForPackage}.
+ * \param sensor    a {@link ASensor} to denote which sensor to be operate. It can be NULL if rate
+ *                  is {@link ASENSOR_DIRECT_RATE_STOP}, denoting stopping of all active sensor
+ *                  direct report.
+ * \param channelId channel id (a positive integer) returned from
+ *                  {@link ASensorManager_createSharedMemoryDirectChannel} or
+ *                  {@link ASensorManager_createHardwareBufferDirectChannel}.
+ *
+ * \return 0 for success or negative integer for failure.
+ */
+int ASensorManager_configureDirectReport(
+        ASensorManager* manager, ASensor const* sensor, int channelId, int rate);
+#endif
 
 /*****************************************************************************/
 
@@ -400,7 +527,7 @@
  * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before.
  */
 int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
-        int32_t samplingPeriodUs, int maxBatchReportLatencyUs);
+        int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs);
 
 /**
  * Enable the selected sensor. Returns a negative error code on failure.
@@ -441,8 +568,7 @@
  *   ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
  *
  */
-ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue,
-                ASensorEvent* events, size_t count);
+ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
 
 
 /*****************************************************************************/
@@ -502,6 +628,29 @@
 bool ASensor_isWakeUpSensor(ASensor const* sensor);
 #endif /* __ANDROID_API__ >= 21 */
 
+#if __ANDROID_API__ >= __ANDROID_API_O__
+/**
+ * Test if sensor supports a certain type of direct channel.
+ *
+ * \param sensor  a {@link ASensor} to denote the sensor to be checked.
+ * \param channelType  Channel type constant, either
+ *                     {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY}
+ *                     or {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER}.
+ * \returns true if sensor supports the specified direct channel type.
+ */
+bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType);
+/**
+ * Get the highest direct rate level that a sensor support.
+ *
+ * \param sensor  a {@link ASensor} to denote the sensor to be checked.
+ *
+ * \return a ASENSOR_DIRECT_RATE_... enum denoting the highest rate level supported by the sensor.
+ *         If return value is {@link ASENSOR_DIRECT_RATE_STOP}, it means the sensor
+ *         does not support direct report.
+ */
+int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor);
+#endif
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
new file mode 100644
index 0000000..8f8a931
--- /dev/null
+++ b/include/android/sharedmem.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup Memory
+ * @{
+ */
+
+/**
+ * @file sharedmem.h
+ */
+
+#ifndef ANDROID_SHARED_MEMORY_H
+#define ANDROID_SHARED_MEMORY_H
+
+#include <stddef.h>
+
+/******************************************************************
+ *
+ * IMPORTANT NOTICE:
+ *
+ *   This file is part of Android's set of stable system headers
+ *   exposed by the Android NDK (Native Development Kit).
+ *
+ *   Third-party source AND binary code relies on the definitions
+ *   here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
+ *
+ *   - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
+ *   - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
+ *   - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
+ *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
+ */
+
+/**
+ * Structures and functions for a shared memory buffer that can be shared across process.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+
+/**
+ * Create a shared memory region.
+ *
+ * Create shared memory region and returns an file descriptor.  The resulting file descriptor can be
+ * mmap'ed to process memory space with PROT_READ | PROT_WRITE | PROT_EXEC. Access to shared memory
+ * region can be restricted with {@link ASharedMemory_setProt}.
+ *
+ * Use close() to release the shared memory region.
+ *
+ * \param name an optional name.
+ * \param size size of the shared memory region
+ * \return file descriptor that denotes the shared memory; error code on failure.
+ */
+int ASharedMemory_create(const char *name, size_t size);
+
+/**
+ * Get the size of the shared memory region.
+ *
+ * \param fd file descriptor of the shared memory region
+ * \return size in bytes; 0 if fd is not a valid shared memory file descriptor.
+ */
+size_t ASharedMemory_getSize(int fd);
+
+/**
+ * Restrict access of shared memory region.
+ *
+ * This function restricts access of a shared memory region. Access can only be removed. The effect
+ * applies globally to all file descriptors in all processes across the system that refer to this
+ * shared memory region. Existing memory mapped regions are not affected.
+ *
+ * It is a common use case to create a shared memory region, map it read/write locally to intialize
+ * content, and then send the shared memory to another process with read only access. Code example
+ * as below (error handling ommited).
+ *
+ * \code{.c}
+ *   int fd = ASharedMemory_create("memory", 128);
+ *
+ *   // By default it has PROT_READ | PROT_WRITE | PROT_EXEC.
+ *   char *buffer = (char *) mmap(NULL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ *
+ *   strcpy(buffer, "This is an example."); // trivially initialize content
+ *
+ *   // limit access to read only
+ *   ASharedMemory_setProt(fd, PROT_READ);
+ *
+ *   // share fd with another process here and the other process can only map with PROT_READ.
+ * \endcode
+ *
+ * \param fd   file descriptor of the shared memory region.
+ * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting
+ *             updated access. Note access can only be removed, but not added back.
+ * \return 0 for success, error code on failure.
+ */
+int ASharedMemory_setProt(int fd, int prot);
+
+#endif
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_SHARED_MEMORY_H
+
+/** @} */
diff --git a/include/android/trace.h b/include/android/trace.h
index 6cdcfeb..d3b1fb6 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -14,6 +14,13 @@
  * limitations under the License.
  */
 
+/**
+ * @file trace.h
+ * @brief Writes trace events to the system trace buffer.
+ *
+ * These trace events can be collected and visualized using the Systrace tool.
+ * For information about using the Systrace tool, read <a href="https://developer.android.com/studio/profile/systrace.html">Analyzing UI Performance with Systrace</a>.
+ */
 
 #ifndef ANDROID_NATIVE_TRACE_H
 #define ANDROID_NATIVE_TRACE_H
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
new file mode 100644
index 0000000..834dcbd
--- /dev/null
+++ b/include/audiomanager/AudioManager.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIOMANAGER_H
+#define ANDROID_AUDIOMANAGER_H
+
+namespace android {
+
+// must be kept in sync with definitions in AudioPlaybackConfiguration.java
+
+#define PLAYER_PIID_INVALID -1
+
+typedef enum {
+    PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11,
+    PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12,
+} player_type_t;
+
+typedef enum {
+    PLAYER_STATE_UNKNOWN  = -1,
+    PLAYER_STATE_RELEASED = 0,
+    PLAYER_STATE_IDLE     = 1,
+    PLAYER_STATE_STARTED  = 2,
+    PLAYER_STATE_PAUSED   = 3,
+    PLAYER_STATE_STOPPED  = 4,
+} player_state_t;
+
+}; // namespace android
+
+#endif // ANDROID_AUDIOMANAGER_H
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
new file mode 100644
index 0000000..ce7804b
--- /dev/null
+++ b/include/audiomanager/IAudioManager.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IAUDIOMANAGER_H
+#define ANDROID_IAUDIOMANAGER_H
+
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+#include <hardware/power.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioManager : public IInterface
+{
+public:
+    // These transaction IDs must be kept in sync with the method order from
+    // IAudioService.aidl.
+    enum {
+        // transaction IDs for the unsupported methods are commented out
+        /*
+        ADJUSTSUGGESTEDSTREAMVOLUME           = IBinder::FIRST_CALL_TRANSACTION,
+        ADJUSTSTREAMVOLUME                    = IBinder::FIRST_CALL_TRANSACTION + 1,
+        SETSTREAMVOLUME                       = IBinder::FIRST_CALL_TRANSACTION + 2,
+        ISSTREAMMUTE                          = IBinder::FIRST_CALL_TRANSACTION + 3,
+        FORCEREMOTESUBMIXFULLVOLUME           = IBinder::FIRST_CALL_TRANSACTION + 4,
+        ISMASTERMUTE                          = IBinder::FIRST_CALL_TRANSACTION + 5,
+        SETMASTERMUTE                         = IBinder::FIRST_CALL_TRANSACTION + 6,
+        GETSTREAMVOLUME                       = IBinder::FIRST_CALL_TRANSACTION + 7,
+        GETSTREAMMINVOLUME                    = IBinder::FIRST_CALL_TRANSACTION + 8,
+        GETSTREAMMAXVOLUME                    = IBinder::FIRST_CALL_TRANSACTION + 9,
+        GETLASTAUDIBLESTREAMVOLUME            = IBinder::FIRST_CALL_TRANSACTION + 10,
+        SETMICROPHONEMUTE                     = IBinder::FIRST_CALL_TRANSACTION + 11,
+        SETRINGERMODEEXTERNAL                 = IBinder::FIRST_CALL_TRANSACTION + 12,
+        SETRINGERMODEINTERNAL                 = IBinder::FIRST_CALL_TRANSACTION + 13,
+        GETRINGERMODEEXTERNAL                 = IBinder::FIRST_CALL_TRANSACTION + 14,
+        GETRINGERMODEINTERNAL                 = IBinder::FIRST_CALL_TRANSACTION + 15,
+        ISVALIDRINGERMODE                     = IBinder::FIRST_CALL_TRANSACTION + 16,
+        SETVIBRATESETTING                     = IBinder::FIRST_CALL_TRANSACTION + 17,
+        GETVIBRATESETTING                     = IBinder::FIRST_CALL_TRANSACTION + 18,
+        SHOULDVIBRATE                         = IBinder::FIRST_CALL_TRANSACTION + 19,
+        SETMODE                               = IBinder::FIRST_CALL_TRANSACTION + 20,
+        GETMODE                               = IBinder::FIRST_CALL_TRANSACTION + 21,
+        PLAYSOUNDEFFECT                       = IBinder::FIRST_CALL_TRANSACTION + 22,
+        PLAYSOUNDEFFECTVOLUME                 = IBinder::FIRST_CALL_TRANSACTION + 23,
+        LOADSOUNDEFFECTS                      = IBinder::FIRST_CALL_TRANSACTION + 24,
+        UNLOADSOUNDEFFECTS                    = IBinder::FIRST_CALL_TRANSACTION + 25,
+        RELOADAUDIOSETTINGS                   = IBinder::FIRST_CALL_TRANSACTION + 26,
+        AVRCPSUPPORTSABSOLUTEVOLUME           = IBinder::FIRST_CALL_TRANSACTION + 27,
+        SETSPEAKERPHONEON                     = IBinder::FIRST_CALL_TRANSACTION + 28,
+        ISSPEAKERPHONEON                      = IBinder::FIRST_CALL_TRANSACTION + 29,
+        SETBLUETOOTHSCOON                     = IBinder::FIRST_CALL_TRANSACTION + 30,
+        ISBLUETOOTHSCOON                      = IBinder::FIRST_CALL_TRANSACTION + 31,
+        SETBLUETOOTHA2DPON                    = IBinder::FIRST_CALL_TRANSACTION + 32,
+        ISBLUETOOTHA2DPON                     = IBinder::FIRST_CALL_TRANSACTION + 33,
+        REQUESTAUDIOFOCUS                     = IBinder::FIRST_CALL_TRANSACTION + 34,
+        ABANDONAUDIOFOCUS                     = IBinder::FIRST_CALL_TRANSACTION + 35,
+        UNREGISTERAUDIOFOCUSCLIENT            = IBinder::FIRST_CALL_TRANSACTION + 36,
+        GETCURRENTAUDIOFOCUS                  = IBinder::FIRST_CALL_TRANSACTION + 37,
+        STARTBLUETOOTHSCO                     = IBinder::FIRST_CALL_TRANSACTION + 38,
+        STARTBLUETOOTHSCOVIRTUALCALL          = IBinder::FIRST_CALL_TRANSACTION + 39,
+        STOPBLUETOOTHSCO                      = IBinder::FIRST_CALL_TRANSACTION + 40,
+        FORCEVOLUMECONTROLSTREAM              = IBinder::FIRST_CALL_TRANSACTION + 41,
+        SETRINGTONEPLAYER                     = IBinder::FIRST_CALL_TRANSACTION + 42,
+        GETRINGTONEPLAYER                     = IBinder::FIRST_CALL_TRANSACTION + 43,
+        GETUISOUNDSSTREAMTYPE                 = IBinder::FIRST_CALL_TRANSACTION + 44,
+        SETWIREDDEVICECONNECTIONSTATE         = IBinder::FIRST_CALL_TRANSACTION + 45,
+        SETBLUETOOTHA2DPDEVICECONNECTIONSTATE = IBinder::FIRST_CALL_TRANSACTION + 46,
+        HANDLEBLUETOOTHA2DPDEVICECONFIGCHANGE = IBinder::FIRST_CALL_TRANSACTION + 47,
+        STARTWATCHINGROUTES                   = IBinder::FIRST_CALL_TRANSACTION + 48,
+        ISCAMERASOUNDFORCED                   = IBinder::FIRST_CALL_TRANSACTION + 49,
+        SETVOLUMECONTROLLER                   = IBinder::FIRST_CALL_TRANSACTION + 50,
+        NOTIFYVOLUMECONTROLLERVISIBLE         = IBinder::FIRST_CALL_TRANSACTION + 51,
+        ISSTREAMAFFECTEDBYRINGERMODE          = IBinder::FIRST_CALL_TRANSACTION + 52,
+        ISSTREAMAFFECTEDBYMUTE                = IBinder::FIRST_CALL_TRANSACTION + 53,
+        DISABLESAFEMEDIAVOLUME                = IBinder::FIRST_CALL_TRANSACTION + 54,
+        SETHDMISYSTEMAUDIOSUPPORTED           = IBinder::FIRST_CALL_TRANSACTION + 55,
+        ISHDMISYSTEMAUDIOSUPPORTED            = IBinder::FIRST_CALL_TRANSACTION + 56,
+        REGISTERAUDIOPOLICY                   = IBinder::FIRST_CALL_TRANSACTION + 57,
+        UNREGISTERAUDIOPOLICYASYNC            = IBinder::FIRST_CALL_TRANSACTION + 58,
+        SETFOCUSPROPERTIESFORPOLICY           = IBinder::FIRST_CALL_TRANSACTION + 59,
+        SETVOLUMEPOLICY                       = IBinder::FIRST_CALL_TRANSACTION + 60,
+        REGISTERRECORDINGCALLBACK             = IBinder::FIRST_CALL_TRANSACTION + 61,
+        UNREGISTERRECORDINGCALLBACK           = IBinder::FIRST_CALL_TRANSACTION + 62,
+        GETACTIVERECORDINGCONFIGURATIONS      = IBinder::FIRST_CALL_TRANSACTION + 63,
+        REGISTERPLAYBACKCALLBACK              = IBinder::FIRST_CALL_TRANSACTION + 64,
+        UNREGISTERPLAYBACKCALLBACK            = IBinder::FIRST_CALL_TRANSACTION + 65,
+        GETACTIVEPLAYBACKCONFIGURATIONS       = IBinder::FIRST_CALL_TRANSACTION + 66,
+        */
+
+        TRACK_PLAYER                          = IBinder::FIRST_CALL_TRANSACTION + 67,
+        PLAYER_ATTRIBUTES                     = IBinder::FIRST_CALL_TRANSACTION + 68,
+        PLAYER_EVENT                          = IBinder::FIRST_CALL_TRANSACTION + 69,
+        RELEASE_PLAYER                        = IBinder::FIRST_CALL_TRANSACTION + 70,
+
+        /*
+        DISABLE_RINGTONE_SYNC                 = IBinder::FIRST_CALL_TRANSACTION + 71,
+        */
+    };
+
+    DECLARE_META_INTERFACE(AudioManager)
+
+    // The parcels created by these methods must be kept in sync with the
+    // corresponding methods from IAudioService.aidl and objects it imports.
+    virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
+                audio_content_type_t content, const sp<IBinder>& player) = 0;
+    /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
+                audio_content_type_t content)= 0;
+    /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
+    /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOMANAGER_H
diff --git a/include/audiomanager/IPlayer.h b/include/audiomanager/IPlayer.h
new file mode 100644
index 0000000..de5c1c7
--- /dev/null
+++ b/include/audiomanager/IPlayer.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IPLAYER_H
+#define ANDROID_IPLAYER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <media/VolumeShaper.h>
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IPlayer : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(Player);
+
+    virtual void start() = 0;
+
+    virtual void pause() = 0;
+
+    virtual void stop() = 0;
+
+    virtual void setVolume(float vol) = 0;
+
+    virtual void setPan(float pan) = 0;
+
+    virtual void setStartDelayMs(int delayMs) = 0;
+
+    virtual void applyVolumeShaper(
+            const sp<VolumeShaper::Configuration>& configuration,
+            const sp<VolumeShaper::Operation>& operation) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnPlayer : public BnInterface<IPlayer>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IPLAYER_H
diff --git a/include/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h
index b399905..80ab7f3 100644
--- a/include/batteryservice/BatteryService.h
+++ b/include/batteryservice/BatteryService.h
@@ -24,33 +24,17 @@
 
 namespace android {
 
-// must be kept in sync with definitions in BatteryManager.java
-enum {
-    BATTERY_STATUS_UNKNOWN = 1, // equals BatteryManager.BATTERY_STATUS_UNKNOWN constant
-    BATTERY_STATUS_CHARGING = 2, // equals BatteryManager.BATTERY_STATUS_CHARGING constant
-    BATTERY_STATUS_DISCHARGING = 3, // equals BatteryManager.BATTERY_STATUS_DISCHARGING constant
-    BATTERY_STATUS_NOT_CHARGING = 4, // equals BatteryManager.BATTERY_STATUS_NOT_CHARGING constant
-    BATTERY_STATUS_FULL = 5, // equals BatteryManager.BATTERY_STATUS_FULL constant
-};
+#include "BatteryServiceConstants.h"
 
-// must be kept in sync with definitions in BatteryManager.java
+// must be kept in sync with definitions in
+// frameworks/base/core/java/android/os/BatteryManager.java
 enum {
-    BATTERY_HEALTH_UNKNOWN = 1, // equals BatteryManager.BATTERY_HEALTH_UNKNOWN constant
-    BATTERY_HEALTH_GOOD = 2, // equals BatteryManager.BATTERY_HEALTH_GOOD constant
-    BATTERY_HEALTH_OVERHEAT = 3, // equals BatteryManager.BATTERY_HEALTH_OVERHEAT constant
-    BATTERY_HEALTH_DEAD = 4, // equals BatteryManager.BATTERY_HEALTH_DEAD constant
-    BATTERY_HEALTH_OVER_VOLTAGE = 5, // equals BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE constant
-    BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6, // equals BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE constant
-    BATTERY_HEALTH_COLD = 7, // equals BatteryManager.BATTERY_HEALTH_COLD constant
-};
-
-// must be kept in sync with definitions in BatteryProperty.java
-enum {
-    BATTERY_PROP_CHARGE_COUNTER = 1, // equals BatteryProperty.CHARGE_COUNTER constant
-    BATTERY_PROP_CURRENT_NOW = 2, // equals BatteryProperty.CURRENT_NOW constant
-    BATTERY_PROP_CURRENT_AVG = 3, // equals BatteryProperty.CURRENT_AVG constant
-    BATTERY_PROP_CAPACITY = 4, // equals BatteryProperty.CAPACITY constant
-    BATTERY_PROP_ENERGY_COUNTER = 5, // equals BatteryProperty.ENERGY_COUNTER constant
+    BATTERY_PROP_CHARGE_COUNTER = 1, // equals BATTERY_PROPERTY_CHARGE_COUNTER
+    BATTERY_PROP_CURRENT_NOW = 2, // equals BATTERY_PROPERTY_CURRENT_NOW
+    BATTERY_PROP_CURRENT_AVG = 3, // equals BATTERY_PROPERTY_CURRENT_AVERAGE
+    BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY
+    BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER
+    BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS
 };
 
 struct BatteryProperties {
diff --git a/include/batteryservice/BatteryServiceConstants.h b/include/batteryservice/BatteryServiceConstants.h
new file mode 100644
index 0000000..8a90a12
--- /dev/null
+++ b/include/batteryservice/BatteryServiceConstants.h
@@ -0,0 +1,32 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+    BATTERY_STATUS_UNKNOWN = 1,
+    BATTERY_STATUS_CHARGING = 2,
+    BATTERY_STATUS_DISCHARGING = 3,
+    BATTERY_STATUS_NOT_CHARGING = 4,
+    BATTERY_STATUS_FULL = 5,
+};
+
+enum {
+    BATTERY_HEALTH_UNKNOWN = 1,
+    BATTERY_HEALTH_GOOD = 2,
+    BATTERY_HEALTH_OVERHEAT = 3,
+    BATTERY_HEALTH_DEAD = 4,
+    BATTERY_HEALTH_OVER_VOLTAGE = 5,
+    BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6,
+    BATTERY_HEALTH_COLD = 7,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/include/batteryservice/IBatteryPropertiesListener.h b/include/batteryservice/IBatteryPropertiesListener.h
index 9154076..b226dd6 100644
--- a/include/batteryservice/IBatteryPropertiesListener.h
+++ b/include/batteryservice/IBatteryPropertiesListener.h
@@ -40,6 +40,12 @@
 
 // ----------------------------------------------------------------------------
 
+class BnBatteryPropertiesListener: public BnInterface<IBatteryPropertiesListener> {
+public:
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+                                Parcel* reply, uint32_t flags = 0);
+};
+
 }; // namespace android
 
 #endif // ANDROID_IBATTERYPROPERTIESLISTENER_H
diff --git a/include/binder/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/HalToken.h b/include/binder/HalToken.h
new file mode 100644
index 0000000..ce97c78
--- /dev/null
+++ b/include/binder/HalToken.h
@@ -0,0 +1,237 @@
+/*
+ * 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_HALTOKEN_H
+#define ANDROID_HALTOKEN_H
+
+#include <binder/Parcel.h>
+#include <hidl/HidlSupport.h>
+
+/**
+ * Hybrid Interfaces
+ * =================
+ *
+ * A hybrid interface is a binder interface that
+ * 1. is implemented both traditionally and as a wrapper around a hidl
+ *    interface, and allows querying whether the underlying instance comes from
+ *    a hidl interface or not; and
+ * 2. allows efficient calls to a hidl interface (if the underlying instance
+ *    comes from a hidl interface) by automatically creating the wrapper in the
+ *    process that calls it.
+ *
+ * Terminology:
+ * - `HalToken`: The type for a "token" of a hidl interface. This is defined to
+ *   be compatible with `ITokenManager.hal`.
+ * - `HInterface`: The base type for a hidl interface. Currently, it is defined
+ *   as `::android::hidl::base::V1_0::IBase`.
+ * - `HALINTERFACE`: The hidl interface that will be sent through binders.
+ * - `INTERFACE`: The binder interface that will be the wrapper of
+ *   `HALINTERFACE`. `INTERFACE` is supposed to be somewhat similar to
+ *   `HALINTERFACE`.
+ *
+ * To demonstrate how this is done, here is an example. Suppose `INTERFACE` is
+ * `IFoo` and `HALINTERFACE` is `HFoo`. The required steps are:
+ * 1. Use DECLARE_HYBRID_META_INTERFACE instead of DECLARE_META_INTERFACE in the
+ *    definition of `IFoo`. The usage is
+ *        DECLARE_HYBRID_META_INTERFACE(IFoo, HFoo)
+ *    inside the body of `IFoo`.
+ * 2. Create a converter class that derives from
+ *    `H2BConverter<HFoo, IFoo, BnFoo>`. Let us call this `H2BFoo`.
+ * 3. Add the following constructor in `H2BFoo` that call the corresponding
+ *    constructors in `H2BConverter`:
+ *        H2BFoo(const sp<HalInterface>& base) : CBase(base) {}
+ *    Note: `CBase = H2BConverter<HFoo, IFoo, BnFoo>` and `HalInterface = HFoo`
+ *    are member typedefs of `H2BConverter<HFoo, IFoo, BnFoo>`, so the above
+ *    line can be copied into `H2BFoo`.
+ * 4. Implement `IFoo` in `H2BFoo` on top of `HFoo`. `H2BConverter` provides a
+ *    protected `mBase` of type `sp<HFoo>` that can be used to access the `HFoo`
+ *    instance. (There is also a public function named `getHalInterface()` that
+ *    returns `mBase`.)
+ * 5. Create a hardware proxy class that derives from
+ *    `HpInterface<BpFoo, H2BFoo>`. Name this class `HpFoo`. (This name cannot
+ *    deviate. See step 8 below.)
+ * 6. Add the following constructor to `HpFoo`:
+ *        HpFoo(const sp<IBinder>& base): PBase(base) {}
+ *    Note: `PBase` a member typedef of `HpInterface<BpFoo, H2BFoo>` that is
+ *    equal to `HpInterface<BpFoo, H2BFoo>` itself, so the above line can be
+ *    copied verbatim into `HpFoo`.
+ * 7. Delegate all functions in `HpFoo` that come from `IFoo` except
+ *    `getHalInterface` to the protected member `mBase`,
+ *    which is defined in `HpInterface<BpFoo, H2BFoo>` (hence in `HpFoo`) with
+ *    type `IFoo`. (There is also a public function named `getBaseInterface()`
+ *    that returns `mBase`.)
+ * 8. Replace the existing `IMPLEMENT_META_INTERFACE` for INTERFACE by
+ *    `IMPLEMENT_HYBRID_META_INTERFACE`. Note that this macro relies on the
+ *    exact naming of `HpFoo`, where `Foo` comes from the interface name `IFoo`.
+ *    An example usage is
+ *        IMPLEMENT_HYBRID_META_INTERFACE(IFoo, HFoo, "example.interface.foo");
+ *
+ * `GETTOKEN` Template Argument
+ * ============================
+ *
+ * Following the instructions above, `H2BConverter` and `HpInterface` would use
+ * `transact()` to send over tokens, with `code` (the first argument of
+ * `transact()`) equal to a 4-byte value of '_GTK'. If this value clashes with
+ * other values already in use in the `Bp` class, it can be changed by supplying
+ * the last optional template argument to `H2BConverter` and `HpInterface`.
+ *
+ */
+
+namespace android {
+
+typedef uint64_t HalToken;
+typedef ::android::hidl::base::V1_0::IBase HInterface;
+
+sp<HInterface> retrieveHalInterface(const HalToken& token);
+bool createHalToken(const sp<HInterface>& interface, HalToken* token);
+bool deleteHalToken(const HalToken& token);
+
+template <
+        typename HINTERFACE,
+        typename INTERFACE,
+        typename BNINTERFACE,
+        uint32_t GETTOKEN = '_GTK'>
+class H2BConverter : public BNINTERFACE {
+public:
+    typedef H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN> CBase; // Converter Base
+    typedef INTERFACE BaseInterface;
+    typedef HINTERFACE HalInterface;
+    static constexpr uint32_t GET_HAL_TOKEN = GETTOKEN;
+
+    H2BConverter(const sp<HalInterface>& base) : mBase(base) {}
+    virtual status_t onTransact(uint32_t code,
+            const Parcel& data, Parcel* reply, uint32_t flags = 0);
+    sp<HalInterface> getHalInterface() override { return mBase; }
+    HalInterface* getBaseInterface() { return mBase.get(); }
+
+protected:
+    sp<HalInterface> mBase;
+};
+
+template <
+        typename BPINTERFACE,
+        typename CONVERTER,
+        uint32_t GETTOKEN = '_GTK'>
+class HpInterface : public CONVERTER::BaseInterface {
+public:
+    typedef HpInterface<BPINTERFACE, CONVERTER, GETTOKEN> PBase; // Proxy Base
+    typedef typename CONVERTER::BaseInterface BaseInterface;
+    typedef typename CONVERTER::HalInterface HalInterface;
+    static constexpr uint32_t GET_HAL_TOKEN = GETTOKEN;
+
+    explicit HpInterface(const sp<IBinder>& impl);
+    sp<HalInterface> getHalInterface() override { return mHal; }
+    BaseInterface* getBaseInterface() { return mBase.get(); }
+
+protected:
+    sp<IBinder> mImpl;
+    sp<BaseInterface> mBase;
+    sp<HalInterface> mHal;
+    IBinder* onAsBinder() override { return mImpl.get(); }
+};
+
+// ----------------------------------------------------------------------
+
+#define DECLARE_HYBRID_META_INTERFACE(INTERFACE, HAL)                   \
+    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();                                            \
+    virtual sp<HAL> getHalInterface();                                  \
+
+
+#define IMPLEMENT_HYBRID_META_INTERFACE(INTERFACE, HAL, NAME)           \
+    const ::android::String16 I##INTERFACE::descriptor(NAME);           \
+    const ::android::String16&                                          \
+            I##INTERFACE::getInterfaceDescriptor() const {              \
+        return I##INTERFACE::descriptor;                                \
+    }                                                                   \
+    ::android::sp<I##INTERFACE> I##INTERFACE::asInterface(              \
+            const ::android::sp<::android::IBinder>& obj)               \
+    {                                                                   \
+        ::android::sp<I##INTERFACE> intr;                               \
+        if (obj != NULL) {                                              \
+            intr = static_cast<I##INTERFACE*>(                          \
+                obj->queryLocalInterface(                               \
+                        I##INTERFACE::descriptor).get());               \
+            if (intr == NULL) {                                         \
+                intr = new Hp##INTERFACE(obj);                          \
+            }                                                           \
+        }                                                               \
+        return intr;                                                    \
+    }                                                                   \
+    I##INTERFACE::I##INTERFACE() { }                                    \
+    I##INTERFACE::~I##INTERFACE() { }                                   \
+    sp<HAL> I##INTERFACE::getHalInterface() { return nullptr; }         \
+
+// ----------------------------------------------------------------------
+
+template <
+        typename HINTERFACE,
+        typename INTERFACE,
+        typename BNINTERFACE,
+        uint32_t GETTOKEN>
+status_t H2BConverter<HINTERFACE, INTERFACE, BNINTERFACE, GETTOKEN>::
+        onTransact(
+        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+    if (code == GET_HAL_TOKEN) {
+        HalToken token;
+        bool result;
+        result = createHalToken(mBase, &token);
+        if (!result) {
+            ALOGE("H2BConverter: Failed to create HAL token.");
+        }
+        reply->writeBool(result);
+        reply->writeUint64(token);
+        return NO_ERROR;
+    }
+    return BNINTERFACE::onTransact(code, data, reply, flags);
+}
+
+template <typename BPINTERFACE, typename CONVERTER, uint32_t GETTOKEN>
+HpInterface<BPINTERFACE, CONVERTER, GETTOKEN>::HpInterface(
+        const sp<IBinder>& impl) : mImpl(impl) {
+    Parcel data, reply;
+    data.writeInterfaceToken(BaseInterface::getInterfaceDescriptor());
+    if (impl->transact(GET_HAL_TOKEN, data, &reply) == NO_ERROR) {
+        bool tokenCreated = reply.readBool();
+        HalToken token = reply.readUint64();
+        if (!tokenCreated) {
+            ALOGE("HpInterface: Sender failed to create HAL token.");
+            mBase = new BPINTERFACE(impl);
+        } else {
+            sp<HInterface> hInterface = retrieveHalInterface(token);
+            deleteHalToken(token);
+            if (hInterface != nullptr) {
+                mHal = static_cast<HalInterface*>(hInterface.get());
+                mBase = new CONVERTER(mHal);
+            } else {
+                ALOGE("HpInterface: Cannot retrieve HAL interface from token.");
+                mBase = new BPINTERFACE(impl);
+            }
+        }
+    } else {
+        mBase = new BPINTERFACE(impl);
+    }
+}
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_HALTOKEN_H
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/ProcessInfoService.h b/include/binder/ProcessInfoService.h
index c5ead20..0da61ee 100644
--- a/include/binder/ProcessInfoService.h
+++ b/include/binder/ProcessInfoService.h
@@ -35,6 +35,8 @@
     ProcessInfoService();
 
     status_t getProcessStatesImpl(size_t length, /*in*/ int32_t* pids, /*out*/ int32_t* states);
+    status_t getProcessStatesScoresImpl(size_t length, /*in*/ int32_t* pids,
+            /*out*/ int32_t* states, /*out*/ int32_t *scores);
     void updateBinderLocked();
 
     static const int BINDER_ATTEMPT_LIMIT = 5;
@@ -55,6 +57,21 @@
                 /*out*/ states);
     }
 
+    /**
+     * For each PID in the given "pids" input array, write the current process state
+     * for that process into the "states" output array, or
+     * ActivityManager.PROCESS_STATE_NONEXISTENT * to indicate that no process with the given PID
+     * exists. OoM scores will also be written in the "scores" output array.
+     * Please also note that clients calling this method need to have
+     * "GET_PROCESS_STATE_AND_OOM_SCORE" permission.
+     *
+     * Returns NO_ERROR if this operation was successful, or a negative error code otherwise.
+     */
+    static status_t getProcessStatesScoresFromPids(size_t length, /*in*/ int32_t* pids,
+            /*out*/ int32_t* states, /*out*/ int32_t *scores) {
+        return ProcessInfoService::getInstance().getProcessStatesScoresImpl(
+                length, /*in*/ pids, /*out*/ states, /*out*/ scores);
+    }
 };
 
 // ----------------------------------------------------------------------
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/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..55637a9 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -17,9 +17,7 @@
 #ifndef ANDROID_GUI_BUFFERITEM_H
 #define ANDROID_GUI_BUFFERITEM_H
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
+#include <ui/FenceTime.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -59,6 +57,9 @@
     // mFence is a fence that will signal when the buffer is idle.
     sp<Fence> mFence;
 
+    // The std::shared_ptr<FenceTime> wrapper around mFence.
+    std::shared_ptr<FenceTime> mFenceTime{FenceTime::NO_FENCE};
+
     // mCrop is the current crop rectangle for this buffer slot.
     Rect mCrop;
 
diff --git a/include/gui/BufferItemConsumer.h b/include/gui/BufferItemConsumer.h
index 56c7a3f..ec77ec7 100644
--- a/include/gui/BufferItemConsumer.h
+++ b/include/gui/BufferItemConsumer.h
@@ -18,18 +18,13 @@
 #define ANDROID_GUI_BUFFERITEMCONSUMER_H
 
 #include <gui/ConsumerBase.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <utils/String8.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
+#include <gui/BufferQueue.h>
 
 #define ANDROID_GRAPHICS_BUFFERITEMCONSUMER_JNI_ID "mBufferItemConsumer"
 
 namespace android {
 
-class BufferQueue;
+class String8;
 
 /**
  * BufferItemConsumer is a BufferQueue consumer endpoint that allows clients
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 266f0aa..c95c535 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -62,12 +62,14 @@
     public:
         explicit ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
         virtual ~ProxyConsumerListener();
-        virtual void onFrameAvailable(const BufferItem& item) override;
-        virtual void onFrameReplaced(const BufferItem& item) override;
-        virtual void onBuffersReleased() override;
-        virtual void onSidebandStreamChanged() override;
-        virtual bool getFrameTimestamps(uint64_t frameNumber,
-                FrameTimestamps* outTimestamps) const override;
+        void onDisconnect() override;
+        void onFrameAvailable(const BufferItem& item) override;
+        void onFrameReplaced(const BufferItem& item) override;
+        void onBuffersReleased() override;
+        void onSidebandStreamChanged() override;
+        void addAndGetFrameTimestamps(
+                const NewFrameEventsEntry* newTimestamps,
+                FrameEventHistoryDelta* outDelta) override;
     private:
         // mConsumerListener is a weak reference to the IConsumerListener.  This is
         // the raison d'etre of ProxyConsumerListener.
@@ -79,7 +81,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..7912528 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -17,19 +17,23 @@
 #ifndef ANDROID_GUI_CONSUMERBASE_H
 #define ANDROID_GUI_CONSUMERBASE_H
 
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/OccupancyTracker.h>
 
-#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
 
 #include <utils/String8.h>
 #include <utils/Vector.h>
 #include <utils/threads.h>
-#include <gui/IConsumerListener.h>
+
 
 namespace android {
 // ----------------------------------------------------------------------------
 
 class String8;
+class GraphicBuffer;
 
 // ConsumerBase is a base class for BufferQueue consumer end-points. It
 // handles common tasks like management of the connection to the BufferQueue
@@ -180,7 +184,7 @@
     // Derived classes should override this method to perform any cleanup that
     // must take place when a buffer is released back to the BufferQueue.  If
     // it is overridden the derived class's implementation must call
-    // ConsumerBase::releaseBufferLocked.e
+    // ConsumerBase::releaseBufferLocked.
     virtual status_t releaseBufferLocked(int slot,
             const sp<GraphicBuffer> graphicBuffer,
             EGLDisplay display, EGLSyncKHR eglFence);
@@ -222,7 +226,7 @@
     // slot that has not yet been used. The buffer allocated to a slot will also
     // be replaced if the requested buffer usage or geometry differs from that
     // of the buffer allocated to a slot.
-    Slot mSlots[BufferQueue::NUM_BUFFER_SLOTS];
+    Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // mAbandoned indicates that the BufferQueue will no longer be used to
     // consume images buffers pushed to it using the IGraphicBufferProducer
@@ -244,6 +248,10 @@
     // if none is supplied
     sp<IGraphicBufferConsumer> mConsumer;
 
+    // The final release fence of the most recent buffer released by
+    // releaseBufferLocked.
+    sp<Fence> mPrevFinalReleaseFence;
+
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of ConsumerBase objects. It must be locked whenever the
     // member variables are accessed or when any of the *Locked methods are
diff --git a/include/gui/CpuConsumer.h b/include/gui/CpuConsumer.h
index b7aa463..58602bf 100644
--- a/include/gui/CpuConsumer.h
+++ b/include/gui/CpuConsumer.h
@@ -17,18 +17,19 @@
 #ifndef ANDROID_GUI_CPUCONSUMER_H
 #define ANDROID_GUI_CPUCONSUMER_H
 
+#include <system/window.h>
+
 #include <gui/ConsumerBase.h>
+#include <gui/BufferQueue.h>
 
-#include <ui/GraphicBuffer.h>
-
-#include <utils/String8.h>
 #include <utils/Vector.h>
-#include <utils/threads.h>
 
 
 namespace android {
 
 class BufferQueue;
+class GraphicBuffer;
+class String8;
 
 /**
  * CpuConsumer is a BufferQueue consumer endpoint that allows direct CPU
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
index 4dc7467..bda3c5c 100644
--- a/include/gui/FrameTimestamps.h
+++ b/include/gui/FrameTimestamps.h
@@ -17,29 +17,332 @@
 #ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
 #define ANDROID_GUI_FRAMETIMESTAMPS_H
 
-#include <utils/Timers.h>
+#include <ui/FenceTime.h>
 #include <utils/Flattenable.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <array>
+#include <bitset>
+#include <vector>
 
 namespace android {
 
-struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
-    FrameTimestamps() :
-        frameNumber(0),
-        postedTime(0),
-        acquireTime(0),
-        refreshStartTime(0),
-        glCompositionDoneTime(0),
-        displayRetireTime(0),
-        releaseTime(0) {}
+struct FrameEvents;
+class FrameEventHistoryDelta;
+class String8;
 
-    uint64_t frameNumber;
-    nsecs_t postedTime;
-    nsecs_t acquireTime;
-    nsecs_t refreshStartTime;
-    nsecs_t glCompositionDoneTime;
-    nsecs_t displayRetireTime;
-    nsecs_t releaseTime;
+
+// Identifiers for all the events that may be recorded or reported.
+enum class FrameEvent {
+    POSTED,
+    REQUESTED_PRESENT,
+    LATCH,
+    ACQUIRE,
+    FIRST_REFRESH_START,
+    LAST_REFRESH_START,
+    GPU_COMPOSITION_DONE,
+    DISPLAY_PRESENT,
+    DISPLAY_RETIRE,
+    DEQUEUE_READY,
+    RELEASE,
+    EVENT_COUNT, // Not an actual event.
 };
 
+
+// A collection of timestamps corresponding to a single frame.
+struct FrameEvents {
+    static constexpr auto EVENT_COUNT =
+            static_cast<size_t>(FrameEvent::EVENT_COUNT);
+    static_assert(EVENT_COUNT <= 32, "Event count sanity check failed.");
+    static constexpr nsecs_t TIMESTAMP_PENDING =
+            std::numeric_limits<nsecs_t>::max();
+
+    static inline bool isValidTimestamp(nsecs_t time) {
+        return time != TIMESTAMP_PENDING;
+    }
+
+    bool hasPostedInfo() const;
+    bool hasRequestedPresentInfo() const;
+    bool hasLatchInfo() const;
+    bool hasFirstRefreshStartInfo() const;
+    bool hasLastRefreshStartInfo() const;
+    bool hasAcquireInfo() const;
+    bool hasGpuCompositionDoneInfo() const;
+    bool hasDisplayPresentInfo() const;
+    bool hasDisplayRetireInfo() const;
+    bool hasReleaseInfo() const;
+    bool hasDequeueReadyInfo() const;
+
+    void checkFencesForCompletion();
+    void dump(String8& outString) const;
+
+    bool valid{false};
+    int connectId{0};
+    uint64_t frameNumber{0};
+
+    // Whether or not certain points in the frame's life cycle have been
+    // encountered help us determine if timestamps aren't available because
+    // a) we'll just never get them or b) they're not ready yet.
+    bool addPostCompositeCalled{false};
+    bool addRetireCalled{false};
+    bool addReleaseCalled{false};
+
+    nsecs_t postedTime{TIMESTAMP_PENDING};
+    nsecs_t requestedPresentTime{TIMESTAMP_PENDING};
+    nsecs_t latchTime{TIMESTAMP_PENDING};
+    nsecs_t firstRefreshStartTime{TIMESTAMP_PENDING};
+    nsecs_t lastRefreshStartTime{TIMESTAMP_PENDING};
+    nsecs_t dequeueReadyTime{TIMESTAMP_PENDING};
+
+    std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> displayPresentFence{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> displayRetireFence{FenceTime::NO_FENCE};
+    std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE};
+};
+
+struct CompositorTiming {
+    nsecs_t deadline{0};
+    nsecs_t interval{16666667};
+    nsecs_t presentLatency{0};
+};
+
+// A short history of frames that are synchronized between the consumer and
+// producer via deltas.
+class FrameEventHistory {
+public:
+    virtual ~FrameEventHistory();
+
+    FrameEvents* getFrame(uint64_t frameNumber);
+    FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint);
+    void checkFencesForCompletion();
+    void dump(String8& outString) const;
+
+    static constexpr size_t MAX_FRAME_HISTORY = 8;
+
+protected:
+    std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames;
+
+    CompositorTiming mCompositorTiming;
+};
+
+
+// The producer's interface to FrameEventHistory
+class ProducerFrameEventHistory : public FrameEventHistory {
+public:
+    ~ProducerFrameEventHistory() override;
+
+    // Public for testing.
+    static nsecs_t snapToNextTick(
+            nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval);
+
+    nsecs_t getNextCompositeDeadline(const nsecs_t now) const;
+    nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; }
+    nsecs_t getCompositeToPresentLatency() const {
+        return mCompositorTiming.presentLatency;
+    }
+
+    // virtual for testing.
+    virtual void updateAcquireFence(
+            uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire);
+    void applyDelta(const FrameEventHistoryDelta& delta);
+
+    void updateSignalTimes();
+
+protected:
+    void applyFenceDelta(FenceTimeline* timeline,
+            std::shared_ptr<FenceTime>* dst,
+            const FenceTime::Snapshot& src) const;
+
+    // virtual for testing.
+    virtual std::shared_ptr<FenceTime> createFenceTime(
+            const sp<Fence>& fence) const;
+
+    size_t mAcquireOffset{0};
+
+    // The consumer updates it's timelines in Layer and SurfaceFlinger since
+    // they can coordinate shared timelines better. The producer doesn't have
+    // shared timelines though, so just let it own and update all of them.
+    FenceTimeline mAcquireTimeline;
+    FenceTimeline mGpuCompositionDoneTimeline;
+    FenceTimeline mPresentTimeline;
+    FenceTimeline 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 onDisconnect();
+
+    void initializeCompositorTiming(const CompositorTiming& compositorTiming);
+
+    void addQueue(const NewFrameEventsEntry& newEntry);
+    void addLatch(uint64_t frameNumber, nsecs_t latchTime);
+    void addPreComposition(uint64_t frameNumber, nsecs_t refreshStartTime);
+    void addPostComposition(uint64_t frameNumber,
+            const std::shared_ptr<FenceTime>& gpuCompositionDone,
+            const std::shared_ptr<FenceTime>& displayPresent,
+            const CompositorTiming& compositorTiming);
+    void 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};
+
+    int mCurrentConnectId{0};
+    bool mProducerWantsEvents{false};
+};
+
+
+// A single frame update from the consumer to producer that can be sent
+// through Binder.
+// Although this may be sent multiple times for the same frame as new
+// timestamps are set, Fences only need to be sent once.
+class FrameEventsDelta : public Flattenable<FrameEventsDelta> {
+friend class ProducerFrameEventHistory;
+public:
+    FrameEventsDelta() = default;
+    FrameEventsDelta(size_t index,
+            const FrameEvents& frameTimestamps,
+            const FrameEventDirtyFields& dirtyFields);
+
+    // Movable.
+    FrameEventsDelta(FrameEventsDelta&& src) = default;
+    FrameEventsDelta& operator=(FrameEventsDelta&& src) = default;
+    // Not copyable.
+    FrameEventsDelta(const FrameEventsDelta& src) = delete;
+    FrameEventsDelta& operator=(const FrameEventsDelta& src) = delete;
+
+    // Flattenable implementation
+    size_t getFlattenedSize() const;
+    size_t getFdCount() const;
+    status_t flatten(void*& buffer, size_t& size, int*& fds,
+            size_t& count) const;
+    status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+            size_t& count);
+
+private:
+    static constexpr size_t minFlattenedSize();
+
+    size_t mIndex{0};
+    uint64_t mFrameNumber{0};
+
+    bool mAddPostCompositeCalled{0};
+    bool mAddRetireCalled{0};
+    bool mAddReleaseCalled{0};
+
+    nsecs_t mPostedTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mRequestedPresentTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mLatchTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mFirstRefreshStartTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mLastRefreshStartTime{FrameEvents::TIMESTAMP_PENDING};
+    nsecs_t mDequeueReadyTime{FrameEvents::TIMESTAMP_PENDING};
+
+    FenceTime::Snapshot mGpuCompositionDoneFence;
+    FenceTime::Snapshot mDisplayPresentFence;
+    FenceTime::Snapshot 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 constexpr size_t minFlattenedSize();
+
+    std::vector<FrameEventsDelta> mDeltas;
+    CompositorTiming mCompositorTiming;
+};
+
+
 } // namespace android
 #endif
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 6267625..51d7666 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -20,8 +20,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
 #include <gui/ConsumerBase.h>
 
 #include <ui/GraphicBuffer.h>
@@ -146,6 +145,10 @@
     // documented by the source.
     int64_t getTimestamp();
 
+    // getDataSpace retrieves the DataSpace associated with the texture image
+    // set by the most recent call to updateTexImage.
+    android_dataspace getCurrentDataSpace();
+
     // getFrameNumber retrieves the frame number associated with the texture
     // image set by the most recent call to updateTexImage.
     //
@@ -168,7 +171,9 @@
     void setFilteringEnabled(bool enabled);
 
     // getCurrentBuffer returns the buffer associated with the current image.
-    sp<GraphicBuffer> getCurrentBuffer() const;
+    // When outSlot is not nullptr, the current buffer slot index is also
+    // returned.
+    sp<GraphicBuffer> getCurrentBuffer(int* outSlot = nullptr) const;
 
     // getCurrentTextureTarget returns the texture target of the current
     // texture as returned by updateTexImage().
@@ -187,6 +192,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 +259,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 +407,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 +419,10 @@
     // gets set each time updateTexImage is called.
     int64_t mCurrentTimestamp;
 
+    // mCurrentDataSpace is the dataspace for the current texture. It
+    // gets set each time updateTexImage is called.
+    android_dataspace mCurrentDataSpace;
+
     // mCurrentFrameNumber is the frame counter for the current texture.
     // It gets set each time updateTexImage is called.
     uint64_t mCurrentFrameNumber;
@@ -472,7 +488,7 @@
     // slot that has not yet been used. The buffer allocated to a slot will also
     // be replaced if the requested buffer usage or geometry differs from that
     // of the buffer allocated to a slot.
-    EglSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
+    EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // mCurrentTexture is the buffer slot index of the buffer that is currently
     // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
diff --git a/include/gui/GraphicBufferAlloc.h b/include/gui/GraphicBufferAlloc.h
index 62e3877..54c9829 100644
--- a/include/gui/GraphicBufferAlloc.h
+++ b/include/gui/GraphicBufferAlloc.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
-#define ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
+#ifndef ANDROID_GUI_GRAPHIC_BUFFER_ALLOC_H
+#define ANDROID_GUI_GRAPHIC_BUFFER_ALLOC_H
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -25,21 +25,27 @@
 #include <utils/Errors.h>
 
 namespace android {
-// ---------------------------------------------------------------------------
 
 class GraphicBuffer;
 
+/*
+ * Concrete implementation of the IGraphicBufferAlloc interface.
+ *
+ * This can create GraphicBuffer instance across processes. This is mainly used
+ * by surfaceflinger.
+ */
+
 class GraphicBufferAlloc : public BnGraphicBufferAlloc {
 public:
     GraphicBufferAlloc();
     virtual ~GraphicBufferAlloc();
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t usage,
+            uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t producerUsage, uint64_t consumerUsage,
             std::string requestorName, status_t* error) override;
 };
 
 
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
 
-#endif // ANDROID_SF_GRAPHIC_BUFFER_ALLOC_H
+#endif // ANDROID_GUI_GRAPHIC_BUFFER_ALLOC_H
diff --git a/include/gui/GraphicsEnv.h b/include/gui/GraphicsEnv.h
deleted file mode 100644
index 4c7366f..0000000
--- a/include/gui/GraphicsEnv.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_GRAPHICS_ENV_H
-#define ANDROID_GUI_GRAPHICS_ENV_H 1
-
-#include <string>
-
-struct android_namespace_t;
-
-namespace android {
-
-class GraphicsEnv {
-public:
-    static GraphicsEnv& getInstance();
-
-    // Set a search path for loading graphics drivers. The path is a list of
-    // directories separated by ':'. A directory can be contained in a zip file
-    // (drivers must be stored uncompressed and page aligned); such elements
-    // in the search path must have a '!' after the zip filename, e.g.
-    //     /data/app/com.example.driver/base.apk!/lib/arm64-v8a
-    void setDriverPath(const std::string path);
-    android_namespace_t* getDriverNamespace();
-
-private:
-    GraphicsEnv() = default;
-    std::string mDriverPath;
-    android_namespace_t* mDriverNamespace = nullptr;
-};
-
-} // namespace android
-
-/* FIXME
- * Export an un-mangled function that just does
- *     return android::GraphicsEnv::getInstance().getDriverNamespace();
- * This allows libEGL to get the function pointer via dlsym, since it can't
- * directly link against libgui. In a future release, we'll fix this so that
- * libgui does not depend on graphics API libraries, and libEGL can link
- * against it. The current dependencies from libgui -> libEGL are:
- *  - the GLConsumer class, which should be moved to its own library
- *  - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and
- *    will be removed soon.
- */
-extern "C" android_namespace_t* android_getDriverNamespace();
-
-#endif // ANDROID_GUI_GRAPHICS_ENV_H
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index 460a03d..a3c7d64 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -43,13 +43,17 @@
     ConsumerListener() { }
     virtual ~ConsumerListener();
 
+    // onDisconnect is called when a producer disconnects from the BufferQueue.
+    virtual void onDisconnect() {} /* Asynchronous */
+
     // onFrameAvailable is called from queueBuffer each time an additional
     // frame becomes available for consumption. This means that frames that
     // are queued while in asynchronous mode only trigger the callback if no
     // previous frames are pending. Frames queued while in synchronous mode
     // always trigger the callback. The item passed to the callback will contain
     // all of the information about the queued frame except for its
-    // GraphicBuffer pointer, which will always be null.
+    // 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 +85,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..1e578cc 100644
--- a/include/gui/IGraphicBufferAlloc.h
+++ b/include/gui/IGraphicBufferAlloc.h
@@ -38,12 +38,29 @@
     /* 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,
+            PixelFormat format, uint32_t layerCount, uint64_t producerUsage,
+            uint64_t consumerUsage, 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,
+                usage, "<Unknown>", error);
+    }
+
+    sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
+            PixelFormat format, uint32_t layerCount, uint32_t usage,
+            std::string requestorName, status_t* error) {
+        return createGraphicBuffer(w, h, format, layerCount, usage,
+                usage, requestorName, error);
+    }
+
+    sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
+            PixelFormat format, uint32_t layerCount, uint64_t producerUsage,
+            uint64_t consumerUsage, status_t* error) {
+        return createGraphicBuffer(w, h, format, layerCount, producerUsage,
+                consumerUsage, "<Unknown>", error);
     }
 };
 
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index dcddca4..60b7d24 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -25,8 +25,9 @@
 #include <utils/Timers.h>
 
 #include <binder/IInterface.h>
+
 #include <ui/PixelFormat.h>
-#include <ui/Rect.h>
+
 #include <gui/OccupancyTracker.h>
 
 #include <EGL/egl.h>
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
deleted file mode 100644
index 857444b..0000000
--- a/include/gui/ISensorEventConnection.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
-#define ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <binder/IInterface.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class BitTube;
-
-class ISensorEventConnection : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(SensorEventConnection)
-
-    virtual sp<BitTube> getSensorChannel() const = 0;
-    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
-                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0;
-    virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
-    virtual status_t flush() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSensorEventConnection : public BnInterface<ISensorEventConnection>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h
deleted file mode 100644
index 737c430..0000000
--- a/include/gui/ISensorServer.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_ISENSORSERVER_H
-#define ANDROID_GUI_ISENSORSERVER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-
-#include <binder/IInterface.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Sensor;
-class ISensorEventConnection;
-class String8;
-
-class ISensorServer : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(SensorServer)
-
-    virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
-    virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
-
-    virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
-             int mode, const String16& opPackageName) = 0;
-    virtual int32_t isDataInjectionEnabled() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnSensorServer : public BnInterface<ISensorServer>
-{
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_ISENSORSERVER_H
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 555a0cc..9870ba0 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -28,9 +28,9 @@
 #include <binder/IInterface.h>
 
 #include <ui/FrameStats.h>
+#include <ui/PixelFormat.h>
 
-#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceComposerClient.h>
+#include <vector>
 
 namespace android {
 // ----------------------------------------------------------------------------
@@ -41,8 +41,11 @@
 struct DisplayStatInfo;
 class HdrCapabilities;
 class IDisplayEventConnection;
-class IMemoryHeap;
+class IGraphicBufferAlloc;
+class IGraphicBufferProducer;
+class ISurfaceComposerClient;
 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
deleted file mode 100644
index 7506835..0000000
--- a/include/gui/Sensor.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_SENSOR_H
-#define ANDROID_GUI_SENSOR_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/Flattenable.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
-#include <hardware/sensors.h>
-
-#include <android/sensor.h>
-
-// ----------------------------------------------------------------------------
-// Concrete types for the NDK
-struct ASensor { };
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Parcel;
-
-// ----------------------------------------------------------------------------
-
-class Sensor : public ASensor, public LightFlattenable<Sensor>
-{
-public:
-    enum {
-        TYPE_ACCELEROMETER  = ASENSOR_TYPE_ACCELEROMETER,
-        TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
-        TYPE_GYROSCOPE      = ASENSOR_TYPE_GYROSCOPE,
-        TYPE_LIGHT          = ASENSOR_TYPE_LIGHT,
-        TYPE_PROXIMITY      = ASENSOR_TYPE_PROXIMITY
-    };
-
-    struct uuid_t{
-        union {
-            uint8_t b[16];
-            int64_t i64[2];
-        };
-        uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));}
-        uuid_t() : b{0} {}
-    };
-
-    Sensor(const Sensor&) = default;
-    Sensor& operator=(const Sensor&) = default;
-
-    Sensor(const char * name = "");
-    Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
-    Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0);
-    ~Sensor();
-
-    const String8& getName() const;
-    const String8& getVendor() const;
-    int32_t getHandle() const;
-    int32_t getType() const;
-    float getMinValue() const;
-    float getMaxValue() const;
-    float getResolution() const;
-    float getPowerUsage() const;
-    int32_t getMinDelay() const;
-    nsecs_t getMinDelayNs() const;
-    int32_t getVersion() const;
-    uint32_t getFifoReservedEventCount() const;
-    uint32_t getFifoMaxEventCount() const;
-    const String8& getStringType() const;
-    const String8& getRequiredPermission() const;
-    bool isRequiredPermissionRuntime() const;
-    int32_t getRequiredAppOp() const;
-    int32_t getMaxDelay() const;
-    uint32_t getFlags() const;
-    bool isWakeUpSensor() const;
-    bool isDynamicSensor() const;
-    bool hasAdditionalInfo() const;
-    int32_t getReportingMode() const;
-
-    // Note that after setId() has been called, getUuid() no longer
-    // returns the UUID.
-    // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
-    //     make sure setId() doesn't change the UuidIndex.
-    const uuid_t& getUuid() const;
-    int32_t getId() const;
-    void setId(int32_t id);
-
-    // LightFlattenable protocol
-    inline bool isFixedSize() const { return false; }
-    size_t getFlattenedSize() const;
-    status_t flatten(void* buffer, size_t size) const;
-    status_t unflatten(void const* buffer, size_t size);
-
-private:
-    String8 mName;
-    String8 mVendor;
-    int32_t mHandle;
-    int32_t mType;
-    float   mMinValue;
-    float   mMaxValue;
-    float   mResolution;
-    float   mPower;
-    int32_t mMinDelay;
-    int32_t mVersion;
-    uint32_t mFifoReservedEventCount;
-    uint32_t mFifoMaxEventCount;
-    String8 mStringType;
-    String8 mRequiredPermission;
-    bool mRequiredPermissionRuntime = false;
-    int32_t mRequiredAppOp;
-    int32_t mMaxDelay;
-    uint32_t mFlags;
-    // TODO(b/29547335): Get rid of this field and replace with an index.
-    //     The index will be into a separate global vector of UUIDs.
-    //     Also add an mId field (and change flatten/unflatten appropriately).
-    uuid_t  mUuid;
-    static void flattenString8(void*& buffer, size_t& size, const String8& string8);
-    static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SENSOR_H
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
deleted file mode 100644
index 4ee7c02..0000000
--- a/include/gui/SensorEventQueue.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SENSOR_EVENT_QUEUE_H
-#define ANDROID_SENSOR_EVENT_QUEUE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-#include <utils/String16.h>
-
-#include <gui/BitTube.h>
-
-// ----------------------------------------------------------------------------
-#define WAKE_UP_SENSOR_EVENT_NEEDS_ACK (1U << 31)
-struct ALooper;
-struct ASensorEvent;
-
-// Concrete types for the NDK
-struct ASensorEventQueue {
-    ALooper* looper;
-};
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class ISensorEventConnection;
-class Sensor;
-class Looper;
-
-// ----------------------------------------------------------------------------
-
-class SensorEventQueue : public ASensorEventQueue, public RefBase
-{
-public:
-
-    enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 };
-
-    /**
-     * Typical sensor delay (sample period) in microseconds.
-     */
-    // Fastest sampling, system will bound it to minDelay
-    static constexpr int32_t SENSOR_DELAY_FASTEST = 0;
-    // Typical sample period for game, 50Hz;
-    static constexpr int32_t SENSOR_DELAY_GAME = 20000;
-    // Typical sample period for UI, 15Hz
-    static constexpr int32_t SENSOR_DELAY_UI = 66667;
-    // Default sensor sample period
-    static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
-
-    SensorEventQueue(const sp<ISensorEventConnection>& connection);
-    virtual ~SensorEventQueue();
-    virtual void onFirstRef();
-
-    int getFd() const;
-
-    static ssize_t write(const sp<BitTube>& tube,
-            ASensorEvent const* events, size_t numEvents);
-
-    ssize_t read(ASensorEvent* events, size_t numEvents);
-
-    status_t waitForEvent() const;
-    status_t wake() const;
-
-    status_t enableSensor(Sensor const* sensor) const;
-    status_t enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const;
-    status_t disableSensor(Sensor const* sensor) const;
-    status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
-
-    // these are here only to support SensorManager.java
-    status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int maxBatchReportLatencyUs,
-                          int reservedFlags) const;
-    status_t disableSensor(int32_t handle) const;
-    status_t flush() const;
-    // Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK.
-    void sendAck(const ASensorEvent* events, int count);
-
-    status_t injectSensorEvent(const ASensorEvent& event);
-private:
-    sp<Looper> getLooper() const;
-    sp<ISensorEventConnection> mSensorEventConnection;
-    sp<BitTube> mSensorChannel;
-    mutable Mutex mLock;
-    mutable sp<Looper> mLooper;
-    ASensorEvent* mRecBuffer;
-    size_t mAvailable;
-    size_t mConsumed;
-    uint32_t mNumAcksToSend;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SENSOR_EVENT_QUEUE_H
diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h
deleted file mode 100644
index 6c6230f..0000000
--- a/include/gui/SensorManager.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_SENSOR_MANAGER_H
-#define ANDROID_GUI_SENSOR_MANAGER_H
-
-#include <map>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Singleton.h>
-#include <utils/Vector.h>
-#include <utils/String8.h>
-
-#include <gui/SensorEventQueue.h>
-
-// ----------------------------------------------------------------------------
-// Concrete types for the NDK
-struct ASensorManager { };
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-class ISensorServer;
-class Sensor;
-class SensorEventQueue;
-// ----------------------------------------------------------------------------
-
-class SensorManager :
-    public ASensorManager
-{
-public:
-    static SensorManager& getInstanceForPackage(const String16& packageName);
-    ~SensorManager();
-
-    ssize_t getSensorList(Sensor const* const** list);
-    ssize_t getDynamicSensorList(Vector<Sensor>& list);
-    Sensor const* getDefaultSensor(int type);
-    sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
-    bool isDataInjectionEnabled();
-
-private:
-    // DeathRecipient interface
-    void sensorManagerDied();
-
-    SensorManager(const String16& opPackageName);
-    status_t assertStateLocked();
-
-private:
-    static Mutex sLock;
-    static std::map<String16, SensorManager*> sPackageInstances;
-
-    Mutex mLock;
-    sp<ISensorServer> mSensorServer;
-    Sensor const** mSensorList;
-    Vector<Sensor> mSensors;
-    sp<IBinder::DeathRecipient> mDeathObserver;
-    const String16 mOpPackageName;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GUI_SENSOR_MANAGER_H
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 489d5ea..cfc68c6 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -18,21 +18,21 @@
 #define ANDROID_GUI_SURFACE_H
 
 #include <gui/IGraphicBufferProducer.h>
-#include <gui/BufferQueue.h>
+#include <gui/BufferQueueDefs.h>
 
 #include <ui/ANativeObjectBase.h>
 #include <ui/Region.h>
 
-#include <binder/Parcelable.h>
-
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
 #include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <utils/KeyedVector.h>
 
 struct ANativeWindow_Buffer;
 
 namespace android {
 
+class ISurfaceComposer;
+
 /*
  * An implementation of ANativeWindow that feeds graphics buffers into a
  * BufferQueue.
@@ -66,7 +66,8 @@
      * the controlledByApp flag indicates that this Surface (producer) is
      * controlled by the application. This flag is used at connect time.
      */
-    explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false);
+    explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer,
+            bool controlledByApp = false);
 
     /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
      * Surface was created with. Usually it's an error to use the
@@ -134,17 +135,38 @@
     status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]);
 
+    status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration);
+
+    /* Enables or disables frame timestamp tracking. It is disabled by default
+     * to avoid overhead during queue and dequeue for applications that don't
+     * need the feature. If disabled, calls to getFrameTimestamps will fail.
+     */
+    void enableFrameTimestamps(bool enable);
+
+    status_t getCompositorTiming(
+            nsecs_t* compositeDeadline, nsecs_t* compositeInterval,
+            nsecs_t* compositeToPresentLatency);
+
     // See IGraphicBufferProducer::getFrameTimestamps
-    bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
-            nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
-            nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
-            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 getWideColorSupport(bool* supported);
+    status_t getHdrSupport(bool* supported);
 
     status_t getUniqueId(uint64_t* outId) const;
 
 protected:
     virtual ~Surface();
 
+    // Virtual for testing.
+    virtual sp<ISurfaceComposer> composerService() const;
+    virtual nsecs_t now() const;
+
 private:
     // can't be copied
     Surface& operator = (const Surface& rhs);
@@ -191,7 +213,13 @@
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSharedBufferMode(va_list args);
     int dispatchSetAutoRefresh(va_list args);
+    int dispatchGetDisplayRefreshCycleDuration(va_list args);
+    int dispatchGetNextFrameId(va_list args);
+    int dispatchEnableFrameTimestamps(va_list args);
+    int dispatchGetCompositorTiming(va_list args);
     int dispatchGetFrameTimestamps(va_list args);
+    int dispatchGetWideColorSupport(va_list args);
+    int dispatchGetHdrSupport(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -204,7 +232,6 @@
 
     virtual int connect(int api);
     virtual int setBufferCount(int bufferCount);
-    virtual int setBuffersDimensions(uint32_t width, uint32_t height);
     virtual int setBuffersUserDimensions(uint32_t width, uint32_t height);
     virtual int setBuffersFormat(PixelFormat format);
     virtual int setBuffersTransform(uint32_t transform);
@@ -224,6 +251,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;
@@ -234,10 +262,11 @@
     virtual int attachBuffer(ANativeWindowBuffer*);
 
 protected:
-    enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS };
+    enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
     enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
 
-private:
+    void querySupportedTimestampsLocked() const;
+
     void freeAllBuffers();
     int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
 
@@ -379,46 +408,19 @@
 
     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 {
-
-/**
- * A simple holder for an IGraphicBufferProducer, to match the managed-side
- * android.view.Surface parcelable behavior.
- *
- * This implements android/view/Surface.aidl
- *
- * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly
- * used in managed Binder calls.
- */
-class Surface : public Parcelable {
-  public:
-
-    String16 name;
-    sp<IGraphicBufferProducer> graphicBufferProducer;
-
-    virtual status_t writeToParcel(Parcel* parcel) const override;
-    virtual status_t readFromParcel(const Parcel* parcel) override;
-
-    // nameAlreadyWritten set to true by Surface.java, because it splits
-    // Parceling itself between managed and native code, so it only wants a part
-    // of the full parceling to happen on its native side.
-    status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const;
-
-    // nameAlreadyRead set to true by Surface.java, because it splits
-    // Parceling itself between managed and native code, so it only wants a part
-    // of the full parceling to happen on its native side.
-    status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
-
-  private:
-
-    static String16 readMaybeEmptyString16(const Parcel* parcel);
-};
-
-} // namespace view
-
-}; // namespace android
+} // namespace android
 
 #endif  // ANDROID_GUI_SURFACE_H
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index f2932f2..1e8cf76 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -21,7 +21,6 @@
 #include <sys/types.h>
 
 #include <binder/IBinder.h>
-#include <binder/IMemory.h>
 
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
@@ -52,6 +51,7 @@
     friend class Composer;
 public:
                 SurfaceComposerClient();
+                SurfaceComposerClient(const sp<IGraphicBufferProducer>& parent);
     virtual     ~SurfaceComposerClient();
 
     // Always make sure we could initialize
@@ -105,7 +105,10 @@
             uint32_t w,         // width in pixel
             uint32_t h,         // height in pixel
             PixelFormat format, // pixel-format desired
-            uint32_t flags = 0  // usage flags
+            uint32_t flags = 0, // usage flags
+            SurfaceControl* parent = nullptr, // parent
+            uint32_t windowType = 0, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
+            uint32_t ownerUid = 0 // UID of the task
     );
 
     //! Create a virtual display
@@ -131,6 +134,10 @@
     //! Close a composer transaction on all active SurfaceComposerClients.
     static void closeGlobalTransaction(bool synchronous = false);
 
+    static status_t enableVSyncInjections(bool enable);
+
+    static status_t injectVSync(nsecs_t when);
+
     //! Flag the currently open transaction as an animation transaction.
     static void setAnimationTransaction();
 
@@ -138,9 +145,9 @@
     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    setMatrix(const sp<IBinder>& id, float dsdx, float dtdx, float dtdy, float dsdy);
     status_t    setPosition(const sp<IBinder>& id, float x, float y);
     status_t    setSize(const sp<IBinder>& id, uint32_t w, uint32_t h);
     status_t    setCrop(const sp<IBinder>& id, const Rect& crop);
@@ -148,6 +155,11 @@
     status_t    setLayerStack(const sp<IBinder>& id, uint32_t layerStack);
     status_t    deferTransactionUntil(const sp<IBinder>& id,
             const sp<IBinder>& handle, uint64_t frameNumber);
+    status_t    deferTransactionUntil(const sp<IBinder>& id,
+            const sp<Surface>& handle, uint64_t frameNumber);
+    status_t    reparentChildren(const sp<IBinder>& id,
+            const sp<IBinder>& newParentHandle);
+    status_t    detachChildren(const sp<IBinder>& id);
     status_t    setOverrideScalingMode(const sp<IBinder>& id,
             int32_t overrideScalingMode);
     status_t    setGeometryAppliesWithResize(const sp<IBinder>& id);
@@ -195,6 +207,7 @@
                 status_t                    mStatus;
                 sp<ISurfaceComposerClient>  mClient;
                 Composer&                   mComposer;
+                wp<IGraphicBufferProducer>  mParent;
 };
 
 // ---------------------------------------------------------------------------
@@ -208,9 +221,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 +250,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..8ee35bc 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();
@@ -69,7 +69,7 @@
     status_t    setFlags(uint32_t flags, uint32_t mask);
     status_t    setTransparentRegionHint(const Region& transparent);
     status_t    setAlpha(float alpha=1.0f);
-    status_t    setMatrix(float dsdx, float dtdx, float dsdy, float dtdy);
+    status_t    setMatrix(float dsdx, float dtdx, float dtdy, float dsdy);
     status_t    setCrop(const Rect& crop);
     status_t    setFinalCrop(const Rect& crop);
 
@@ -80,8 +80,30 @@
     status_t    setGeometryAppliesWithResize();
 
     // Defers applying any changes made in this transaction until the Layer
-    // identified by handle reaches the given frameNumber
-    status_t deferTransactionUntil(sp<IBinder> handle, uint64_t frameNumber);
+    // identified by handle reaches the given frameNumber. If the Layer identified
+    // by handle is removed, then we will apply this transaction regardless of
+    // what frame number has been reached.
+    status_t deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
+
+    // A variant of deferTransactionUntil which identifies the Layer we wait for by
+    // Surface instead of Handle. Useful for clients which may not have the
+    // SurfaceControl for some of their Surfaces. Otherwise behaves identically.
+    status_t deferTransactionUntil(const sp<Surface>& barrier, uint64_t frameNumber);
+
+    // Reparents all children of this layer to the new parent handle.
+    status_t reparentChildren(const sp<IBinder>& newParentHandle);
+
+    // Detaches all child surfaces (and their children recursively)
+    // from their SurfaceControl.
+    // The child SurfaceControl's will not throw exceptions or return errors,
+    // but transactions will have no effect.
+    // The child surfaces will continue to follow their parent surfaces,
+    // and remain eligible for rendering, but their relative state will be
+    // frozen. We use this in the WindowManager, in app shutdown/relaunch
+    // scenarios, where the app would otherwise clean up its child Surfaces.
+    // Sometimes the WindowManager needs to extend their lifetime slightly
+    // in order to perform an exit animation or prevent flicker.
+    status_t detachChildren();
 
     // Set an override scaling mode as documented in <system/window.h>
     // the override scaling mode will take precedence over any client
diff --git a/include/gui/view/Surface.h b/include/gui/view/Surface.h
new file mode 100644
index 0000000..cc64fd4
--- /dev/null
+++ b/include/gui/view/Surface.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_VIEW_SURFACE_H
+#define ANDROID_GUI_VIEW_SURFACE_H
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/String16.h>
+
+#include <binder/Parcelable.h>
+
+namespace android {
+
+class IGraphicBufferProducer;
+
+namespace view {
+
+/**
+ * A simple holder for an IGraphicBufferProducer, to match the managed-side
+ * android.view.Surface parcelable behavior.
+ *
+ * This implements android/view/Surface.aidl
+ *
+ * TODO: Convert IGraphicBufferProducer into AIDL so that it can be directly
+ * used in managed Binder calls.
+ */
+class Surface : public Parcelable {
+  public:
+
+    String16 name;
+    sp<IGraphicBufferProducer> graphicBufferProducer;
+
+    virtual status_t writeToParcel(Parcel* parcel) const override;
+    virtual status_t readFromParcel(const Parcel* parcel) override;
+
+    // nameAlreadyWritten set to true by Surface.java, because it splits
+    // Parceling itself between managed and native code, so it only wants a part
+    // of the full parceling to happen on its native side.
+    status_t writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const;
+
+    // nameAlreadyRead set to true by Surface.java, because it splits
+    // Parceling itself between managed and native code, so it only wants a part
+    // of the full parceling to happen on its native side.
+    status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
+
+  private:
+
+    static String16 readMaybeEmptyString16(const Parcel* parcel);
+};
+
+} // namespace view
+} // namespace android
+
+#endif  // ANDROID_GUI_VIEW_SURFACE_H
diff --git a/include/media/cas/CasAPI.h b/include/media/cas/CasAPI.h
new file mode 100644
index 0000000..0e88019
--- /dev/null
+++ b/include/media/cas/CasAPI.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CAS_API_H_
+#define CAS_API_H_
+
+#include <vector>
+#include <utils/String8.h>
+
+//  Loadable CasPlugin shared libraries should define the entry points
+//  as shown below:
+//
+//  extern "C" {
+//      extern android::CasFactory *createCasFactory();
+//      extern android::DescramblerFactory *createDescramblerFactory();
+//  }
+
+namespace android {
+
+struct CasPlugin;
+
+struct CasPluginDescriptor {
+    int32_t CA_system_id;
+    String8 name;
+};
+
+typedef std::vector<uint8_t> CasData;
+typedef std::vector<uint8_t> CasSessionId;
+typedef std::vector<uint8_t> CasEmm;
+typedef std::vector<uint8_t> CasEcm;
+typedef void (*CasPluginCallback)(
+        void *appData,
+        int32_t event,
+        int32_t arg,
+        uint8_t *data,
+        size_t size);
+
+struct CasFactory {
+    CasFactory() {}
+    virtual ~CasFactory() {}
+
+    // Determine if the plugin can handle the CA scheme identified by CA_system_id.
+    virtual bool isSystemIdSupported(
+            int32_t CA_system_id) const = 0;
+
+    // Get a list of the CA schemes supported by the plugin.
+    virtual status_t queryPlugins(
+            std::vector<CasPluginDescriptor> *descriptors) const = 0;
+
+    // Construct a new instance of a CasPlugin given a CA_system_id
+    virtual status_t createPlugin(
+            int32_t CA_system_id,
+            uint64_t appData,
+            CasPluginCallback callback,
+            CasPlugin **plugin) = 0;
+
+private:
+    CasFactory(const CasFactory &);
+    CasFactory &operator=(const CasFactory &); /* NOLINT */
+};
+
+struct CasPlugin {
+    CasPlugin() {}
+    virtual ~CasPlugin() {}
+
+    // Provide the CA private data from a CA_descriptor in the conditional
+    // access table to a CasPlugin.
+    virtual status_t setPrivateData(
+            const CasData &privateData) = 0;
+
+    // Open a session for descrambling a program. The session will receive the
+    // ECM stream corresponding to the CA_PID for the program.
+    virtual status_t openSession(
+            uint16_t program_number,
+            CasSessionId *sessionId) = 0;
+
+    // Open a session for descrambling an elementary stream inside a program.
+    // The session will receive the ECM stream corresponding to the CA_PID for
+    // the stream.
+    virtual status_t openSession(
+            uint16_t program_number,
+            uint16_t elementary_PID,
+            CasSessionId *sessionId) = 0;
+
+    // Close a previously opened session.
+    virtual status_t closeSession(
+            const CasSessionId &sessionId) = 0;
+
+    // Provide the CA private data from a CA_descriptor in the program map
+    // table to a CasPlugin.
+    virtual status_t setSessionPrivateData(
+            const CasSessionId &sessionId,
+            const CasData &privateData) = 0;
+
+    // Process an ECM from the ECM stream for this session’s elementary stream.
+    virtual status_t processEcm(
+            const CasSessionId &sessionId,
+            const CasEcm &ecm) = 0;
+
+    // Process an in-band EMM from the EMM stream.
+    virtual status_t processEmm(
+            const CasEmm &emm) = 0;
+
+    // Deliver an event to the CasPlugin. The format of the event is specific
+    // to the CA scheme and is opaque to the framework.
+    virtual status_t sendEvent(
+            int32_t event,
+            int32_t arg,
+            const CasData &eventData) = 0;
+
+    // Native implementation of the MediaCas Java API provision method.
+    virtual status_t provision(
+            const String8 &provisionString) = 0;
+
+    // Native implementation of the MediaCas Java API refreshEntitlements method
+    virtual status_t refreshEntitlements(
+            int32_t refreshType,
+            const CasData &refreshData) = 0;
+
+private:
+    CasPlugin(const CasPlugin &);
+    CasPlugin &operator=(const CasPlugin &); /* NOLINT */
+};
+
+extern "C" {
+    extern android::CasFactory *createCasFactory();
+}
+
+} // namespace android
+
+#endif // CAS_API_H_
diff --git a/include/media/cas/DescramblerAPI.h b/include/media/cas/DescramblerAPI.h
new file mode 100644
index 0000000..0a51952
--- /dev/null
+++ b/include/media/cas/DescramblerAPI.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DESCRAMBLER_API_H_
+#define DESCRAMBLER_API_H_
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/cas/CasAPI.h>
+
+namespace android {
+
+struct AString;
+struct DescramblerPlugin;
+
+struct DescramblerFactory {
+    DescramblerFactory() {}
+    virtual ~DescramblerFactory() {}
+
+    // Determine if the plugin can handle the CA scheme identified by CA_system_id.
+    virtual bool isSystemIdSupported(
+            int32_t CA_system_id) const = 0;
+
+    // Construct a new instance of a DescramblerPlugin given a CA_system_id
+    virtual status_t createPlugin(
+            int32_t CA_system_id, DescramblerPlugin **plugin) = 0;
+
+private:
+    DescramblerFactory(const DescramblerFactory &);
+    DescramblerFactory &operator=(const DescramblerFactory &);
+};
+
+struct DescramblerPlugin {
+    enum ScramblingControl {
+        kScrambling_Unscrambled = 0,
+        kScrambling_Reserved    = 1,
+        kScrambling_EvenKey     = 2,
+        kScrambling_OddKey      = 3,
+    };
+
+    struct SubSample {
+        uint32_t mNumBytesOfClearData;
+        uint32_t mNumBytesOfEncryptedData;
+    };
+
+    DescramblerPlugin() {}
+    virtual ~DescramblerPlugin() {}
+
+    // If this method returns false, a non-secure decoder will be used to
+    // decode the data after decryption. The decrypt API below will have
+    // to support insecure decryption of the data (secure = false) for
+    // media data of the given mime type.
+    virtual bool requiresSecureDecoderComponent(const char *mime) const = 0;
+
+    // A MediaCas session may be associated with a MediaCrypto session.  The
+    // associated MediaCas session is used to load decryption keys
+    // into the crypto/cas plugin.  The keys are then referenced by key-id
+    // in the 'key' parameter to the decrypt() method.
+    // Should return NO_ERROR on success, ERROR_DRM_SESSION_NOT_OPENED if
+    // the session is not opened and a code from MediaErrors.h otherwise.
+    virtual status_t setMediaCasSession(const CasSessionId& sessionId) = 0;
+
+    // If the error returned falls into the range
+    // ERROR_DRM_VENDOR_MIN..ERROR_DRM_VENDOR_MAX, errorDetailMsg should be
+    // filled in with an appropriate string.
+    // At the java level these special errors will then trigger a
+    // MediaCodec.CryptoException that gives clients access to both
+    // the error code and the errorDetailMsg.
+    // Returns a non-negative result to indicate the number of bytes written
+    // to the dstPtr, or a negative result to indicate an error.
+    virtual ssize_t descramble(
+            bool secure,
+            ScramblingControl scramblingControl,
+            size_t numSubSamples,
+            const SubSample *subSamples,
+            const void *srcPtr,
+            int32_t srcOffset,
+            void *dstPtr,
+            int32_t dstOffset,
+            AString *errorDetailMsg) = 0;
+
+private:
+    DescramblerPlugin(const DescramblerPlugin &);
+    DescramblerPlugin &operator=(const DescramblerPlugin &);
+};
+
+}  // namespace android
+
+extern "C" {
+    extern android::DescramblerFactory *createDescramblerFactory();
+}
+
+#endif  // DESCRAMBLER_API_H_
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index 7ae07ad..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/binder/Static.h b/include/private/binder/Static.h
index d104646..3d10456 100644
--- a/include/private/binder/Static.h
+++ b/include/private/binder/Static.h
@@ -20,7 +20,6 @@
 #include <utils/threads.h>
 
 #include <binder/IBinder.h>
-#include <binder/IMemory.h>
 #include <binder/ProcessState.h>
 #include <binder/IPermissionController.h>
 #include <binder/IServiceManager.h>
diff --git a/include/gui/BitTube.h b/include/private/gui/BitTube.h
similarity index 100%
rename from include/gui/BitTube.h
rename to include/private/gui/BitTube.h
diff --git a/include/private/gui/ComposerService.h b/include/private/gui/ComposerService.h
index ff2f9bf..50bd742 100644
--- a/include/private/gui/ComposerService.h
+++ b/include/private/gui/ComposerService.h
@@ -28,7 +28,6 @@
 
 // ---------------------------------------------------------------------------
 
-class IMemoryHeap;
 class ISurfaceComposer;
 
 // ---------------------------------------------------------------------------
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 4b3fcc6..20f51a5 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -24,6 +24,7 @@
 
 #include <ui/Region.h>
 #include <ui/Rect.h>
+#include <gui/IGraphicBufferProducer.h>
 
 namespace android {
 
@@ -56,6 +57,8 @@
         eFinalCropChanged           = 0x00000400,
         eOverrideScalingModeChanged = 0x00000800,
         eGeometryAppliesWithResize  = 0x00001000,
+        eReparentChildren           = 0x00002000,
+        eDetachChildren             = 0x00004000
     };
 
     layer_state_t()
@@ -74,16 +77,16 @@
     status_t    read(const Parcel& input);
 
             struct matrix22_t {
-                float   dsdx;
-                float   dtdx;
-                float   dsdy;
-                float   dtdy;
+                float   dsdx{0};
+                float   dtdx{0};
+                float   dtdy{0};
+                float   dsdy{0};
             };
             sp<IBinder>     surface;
             uint32_t        what;
             float           x;
             float           y;
-            uint32_t        z;
+            int32_t         z;
             uint32_t        w;
             uint32_t        h;
             uint32_t        layerStack;
@@ -94,9 +97,13 @@
             matrix22_t      matrix;
             Rect            crop;
             Rect            finalCrop;
-            sp<IBinder>     handle;
+            sp<IBinder>     barrierHandle;
+            sp<IBinder>     reparentHandle;
             uint64_t        frameNumber;
             int32_t         overrideScalingMode;
+
+            sp<IGraphicBufferProducer> barrierGbp;
+
             // non POD must be last. see write/read
             Region          transparentRegion;
 };
diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h
index c7c3160..a22b2cb 100644
--- a/include/private/ui/RegionHelper.h
+++ b/include/private/ui/RegionHelper.h
@@ -27,10 +27,10 @@
 class region_operator
 {
 public:
-    typedef typename RECT::value_type TYPE;    
-    static const TYPE max_value = 0x7FFFFFF;
+    typedef typename RECT::value_type TYPE;
+    static const TYPE max_value = std::numeric_limits<TYPE>::max();
 
-    /* 
+    /*
      * Common boolean operations:
      * value is computed as 0b101 op 0b110
      *    other boolean operation are possible, simply compute
diff --git a/include/ui/ANativeObjectBase.h b/include/ui/ANativeObjectBase.h
index 76e850f..640e34b 100644
--- a/include/ui/ANativeObjectBase.h
+++ b/include/ui/ANativeObjectBase.h
@@ -18,9 +18,7 @@
 #define ANDROID_ANDROID_NATIVES_H
 
 #include <sys/types.h>
-#include <string.h>
 
-#include <hardware/gralloc.h>
 #include <system/window.h>
 
 // ---------------------------------------------------------------------------
diff --git a/include/ui/ColorSpace.h b/include/ui/ColorSpace.h
new file mode 100644
index 0000000..8c4acb7
--- /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 <math/mat3.h>
+#include <math/scalar.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
+
+namespace android {
+
+class ColorSpace {
+public:
+    typedef std::function<float(float)> transfer_function;
+    typedef std::function<float(float)> clamping_function;
+
+    /**
+     * 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;
+    }
+
+    std::string mName;
+
+    mat3 mRGBtoXYZ;
+    mat3 mXYZtoRGB;
+
+    transfer_function mOETF;
+    transfer_function mEOTF;
+    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..94caf6b 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -19,23 +19,22 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <utils/Timers.h>
 
-#include <ui/PixelFormat.h>
+#include <utils/Timers.h>
 
 namespace android {
 
 struct DisplayInfo {
-    uint32_t w;
-    uint32_t h;
-    float xdpi;
-    float ydpi;
-    float fps;
-    float density;
-    uint8_t orientation;
-    bool secure;
-    nsecs_t appVsyncOffset;
-    nsecs_t presentationDeadline;
+    uint32_t w{0};
+    uint32_t h{0};
+    float xdpi{0};
+    float ydpi{0};
+    float fps{0};
+    float density{0};
+    uint8_t orientation{0};
+    bool secure{false};
+    nsecs_t appVsyncOffset{0};
+    nsecs_t presentationDeadline{0};
 };
 
 /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
diff --git a/include/ui/DisplayStatInfo.h b/include/ui/DisplayStatInfo.h
index 0549a83..09543ec 100644
--- a/include/ui/DisplayStatInfo.h
+++ b/include/ui/DisplayStatInfo.h
@@ -22,8 +22,8 @@
 namespace android {
 
 struct DisplayStatInfo {
-    nsecs_t vsyncTime;
-    nsecs_t vsyncPeriod;
+    nsecs_t vsyncTime{0};
+    nsecs_t vsyncPeriod{0};
 };
 
 }; // namespace android
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index 1df15f8..37811bc 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -18,21 +18,15 @@
 #define ANDROID_FENCE_H
 
 #include <stdint.h>
-#include <sys/types.h>
 
-#include <ui/ANativeObjectBase.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
 #include <utils/Flattenable.h>
-#include <utils/String8.h>
+#include <utils/RefBase.h>
 #include <utils/Timers.h>
 
-#include <experimental/optional>
-
-struct ANativeWindowBuffer;
-
 namespace android {
 
+class String8;
+
 // ===========================================================================
 // Fence
 // ===========================================================================
@@ -42,6 +36,11 @@
 {
 public:
     static const sp<Fence> NO_FENCE;
+    static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX;
+    static constexpr nsecs_t SIGNAL_TIME_INVALID = -1;
+    static inline bool isValidTimestamp(nsecs_t time) {
+        return time >= 0 && time < INT64_MAX;
+    }
 
     // TIMEOUT_NEVER may be passed to the wait method to indicate that it
     // should wait indefinitely for the fence to signal.
@@ -57,6 +56,12 @@
     // closed.
     explicit Fence(int fenceFd);
 
+    // Not copyable or movable.
+    Fence(const Fence& rhs) = delete;
+    Fence& operator=(const Fence& rhs) = delete;
+    Fence(Fence&& rhs) = delete;
+    Fence& operator=(Fence&& rhs) = delete;
+
     // Check whether the Fence has an open fence file descriptor. Most Fence
     // methods treat an invalid file descriptor just like a valid fence that
     // is already signalled, so using this is usually not necessary.
@@ -94,30 +99,33 @@
 
     // getSignalTime returns the system monotonic clock time at which the
     // fence transitioned to the signaled state.  If the fence is not signaled
-    // then INT64_MAX is returned.  If the fence is invalid or if an error
-    // occurs then -1 is returned.
+    // then SIGNAL_TIME_PENDING is returned.  If the fence is invalid or if an
+    // error occurs then SIGNAL_TIME_INVALID is returned.
     nsecs_t getSignalTime() const;
 
-#if __cplusplus > 201103L
-    // hasSignaled returns whether the fence has signaled yet. Prefer this to
+    enum class Status {
+        Invalid,     // Fence is invalid
+        Unsignaled,  // Fence is valid but has not yet signaled
+        Signaled,    // Fence is valid and has signaled
+    };
+
+    // getStatus() returns whether the fence has signaled yet. Prefer this to
     // getSignalTime() or wait() if all you care about is whether the fence has
-    // signaled. Returns an optional bool, which will have a value if there was
-    // no error.
-    inline std::experimental::optional<bool> hasSignaled() {
+    // signaled.
+    inline Status getStatus() {
         // The sync_wait call underlying wait() has been measured to be
         // significantly faster than the sync_fence_info call underlying
         // getSignalTime(), which might otherwise appear to be the more obvious
         // way to check whether a fence has signaled.
         switch (wait(0)) {
             case NO_ERROR:
-                return true;
+                return Status::Signaled;
             case -ETIME:
-                return false;
+                return Status::Unsignaled;
             default:
-                return {};
+                return Status::Invalid;
         }
     }
-#endif
 
     // Flattenable interface
     size_t getFlattenedSize() const;
@@ -130,11 +138,6 @@
     friend class LightRefBase<Fence>;
     ~Fence();
 
-    // Disallow copying
-    Fence(const Fence& rhs);
-    Fence& operator = (const Fence& rhs);
-    const Fence& operator = (const Fence& rhs) const;
-
     int mFenceFd;
 };
 
diff --git a/include/ui/FenceTime.h b/include/ui/FenceTime.h
new file mode 100644
index 0000000..871fcf2
--- /dev/null
+++ b/include/ui/FenceTime.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FENCE_TIME_H
+#define ANDROID_FENCE_TIME_H
+
+#include <ui/Fence.h>
+#include <utils/Flattenable.h>
+#include <utils/Timers.h>
+
+#include <atomic>
+#include <mutex>
+#include <queue>
+#include <unordered_map>
+
+namespace android {
+
+class FenceToFenceTimeMap;
+
+// A wrapper around fence that only implements isValid and getSignalTime.
+// It automatically closes the fence in a thread-safe manner once the signal
+// time is known.
+class FenceTime {
+friend class FenceToFenceTimeMap;
+public:
+    // An atomic snapshot of the FenceTime that is flattenable.
+    //
+    // This class is needed because the FenceTime class may not stay
+    // consistent for all steps of the flattening process.
+    //
+    // Not thread safe.
+    struct Snapshot : public Flattenable<Snapshot> {
+        enum class State {
+            EMPTY,
+            FENCE,
+            SIGNAL_TIME,
+        };
+
+        Snapshot() = default;  // Creates an empty snapshot.
+        explicit Snapshot(const sp<Fence>& fence);
+        explicit Snapshot(nsecs_t signalTime);
+
+        // Movable.
+        Snapshot(Snapshot&& src) = default;
+        Snapshot& operator=(Snapshot&& src) = default;
+        // Not copyable.
+        Snapshot(const Snapshot& src) = delete;
+        Snapshot& operator=(const Snapshot&& src) = delete;
+
+        // Flattenable implementation.
+        size_t getFlattenedSize() const;
+        size_t getFdCount() const;
+        status_t flatten(void*& buffer, size_t& size, int*& fds,
+                size_t& count) const;
+        status_t unflatten(void const*& buffer, size_t& size, int const*& fds,
+                size_t& count);
+
+        State state{State::EMPTY};
+        sp<Fence> fence{Fence::NO_FENCE};
+        nsecs_t signalTime{Fence::SIGNAL_TIME_INVALID};
+    };
+
+    static const std::shared_ptr<FenceTime> NO_FENCE;
+
+    explicit FenceTime(const sp<Fence>& fence);
+    explicit FenceTime(sp<Fence>&& fence);
+
+    // Passing in Fence::SIGNAL_TIME_PENDING is not allowed.
+    // Doing so will convert the signalTime to Fence::SIGNAL_TIME_INVALID.
+    explicit FenceTime(nsecs_t signalTime);
+
+    // Do not allow default construction. Share NO_FENCE or explicitly construct
+    // with Fence::SIGNAL_TIME_INVALID instead.
+    FenceTime() = delete;
+
+    // Do not allow copy, assign, or move. Use a shared_ptr to share the
+    // signalTime result. Or use getSnapshot() if a thread-safe copy is really
+    // needed.
+    FenceTime(const FenceTime&) = delete;
+    FenceTime(FenceTime&&) = delete;
+    FenceTime& operator=(const FenceTime&) = delete;
+    FenceTime& operator=(FenceTime&&) = delete;
+
+    // This method should only be called when replacing the fence with
+    // a signalTime. Since this is an indirect way of setting the signal time
+    // of a fence, the snapshot should come from a trusted source.
+    void applyTrustedSnapshot(const Snapshot& src);
+
+    bool isValid() const;
+
+    // Attempts to get the timestamp from the Fence if the timestamp isn't
+    // already cached. Otherwise, it returns the cached value.
+    nsecs_t getSignalTime();
+
+    // Gets the cached timestamp without attempting to query the Fence.
+    nsecs_t getCachedSignalTime() const;
+
+    // Returns a snapshot of the FenceTime in its current state.
+    Snapshot getSnapshot() const;
+
+    void signalForTest(nsecs_t signalTime);
+
+    // Override new and delete since this needs 8-byte alignment, which
+    // is not guaranteed on x86.
+    static void* operator new(size_t nbytes) noexcept;
+    static void operator delete(void *p);
+
+private:
+    // For tests only. If forceValidForTest is true, then getSignalTime will
+    // never return SIGNAL_TIME_INVALID and isValid will always return true.
+    FenceTime(const sp<Fence>& fence, bool forceValidForTest);
+
+    enum class State {
+        VALID,
+        INVALID,
+        FORCED_VALID_FOR_TEST,
+    };
+
+    const State mState{State::INVALID};
+
+    // mMutex guards mFence and mSignalTime.
+    // mSignalTime is also atomic since it is sometimes read outside the lock
+    // for quick checks.
+    mutable std::mutex mMutex;
+    sp<Fence> mFence{Fence::NO_FENCE};
+    std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
+};
+
+// A queue of FenceTimes that are expected to signal in FIFO order.
+// Only maintains a queue of weak pointers so it doesn't keep references
+// to Fences on its own.
+//
+// Can be used to get the signal time of a fence and close its file descriptor
+// without making a syscall for every fence later in the timeline.
+// Additionally, since the FenceTime caches the timestamp internally,
+// other timelines that reference the same FenceTime can avoid the syscall.
+//
+// FenceTimeline only keeps track of a limited number of entries to avoid
+// growing unbounded. Users of FenceTime must make sure they can work even
+// if FenceTimeline did nothing. i.e. they should eventually call
+// Fence::getSignalTime(), not only Fence::getCachedSignalTime().
+//
+// push() and updateSignalTimes() are safe to call simultaneously from
+// different threads.
+class FenceTimeline {
+public:
+    static constexpr size_t MAX_ENTRIES = 64;
+
+    void push(const std::shared_ptr<FenceTime>& fence);
+    void updateSignalTimes();
+
+private:
+    mutable std::mutex mMutex;
+    std::queue<std::weak_ptr<FenceTime>> mQueue;
+};
+
+// Used by test code to create or get FenceTimes for a given Fence.
+//
+// By design, Fences cannot be signaled from user space. However, this class
+// allows test code to set the apparent signalTime of a Fence and
+// have it be visible to all FenceTimes. Release code should not use
+// FenceToFenceTimeMap.
+//
+// FenceToFenceTimeMap keeps a weak reference to the FenceTime and automatically
+// garbage collects entries every time a new FenceTime is created to avoid
+// leaks. This prevents us from having to make the Fence destructor
+// automatically notify that the underlying fence has been destroyed, which
+// would affect release code paths. Garbage collecting so often is inefficient,
+// but acceptable for testing.
+//
+// Since FenceTimes maintain a strong reference to underlying Fences, there
+// should not be any aliasing issues where a new Fence happens to have the same
+// address as a previous Fence; the previous entry will be garbage collected
+// before the new one is added.
+class FenceToFenceTimeMap {
+public:
+    // Create a new FenceTime with that wraps the provided Fence.
+    std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence);
+
+    // Signals all FenceTimes created through this class that are wrappers
+    // around |fence|.
+    void signalAllForTest(const sp<Fence>& fence, nsecs_t signalTime);
+
+private:
+    // Cleans up the entries that no longer have a strong reference.
+    void garbageCollectLocked();
+
+    mutable std::mutex mMutex;
+    std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap;
+};
+
+
+}; // namespace android
+
+#endif // ANDROID_FENCE_TIME_H
diff --git a/include/ui/FloatRect.h b/include/ui/FloatRect.h
new file mode 100644
index 0000000..270675c
--- /dev/null
+++ b/include/ui/FloatRect.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+
+class FloatRect {
+public:
+    FloatRect() = default;
+    constexpr FloatRect(float _left, float _top, float _right, float _bottom)
+      : left(_left), top(_top), right(_right), bottom(_bottom) {}
+
+    float getWidth() const { return right - left; }
+    float getHeight() const { return bottom - top; }
+
+    float left = 0.0f;
+    float top = 0.0f;
+    float right = 0.0f;
+    float bottom = 0.0f;
+};
+
+inline bool operator==(const FloatRect& a, const FloatRect& b) {
+    return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom;
+}
+
+}  // namespace android
diff --git a/include/ui/Gralloc1.h b/include/ui/Gralloc1.h
index cf8c173..90713b3 100644
--- a/include/ui/Gralloc1.h
+++ b/include/ui/Gralloc1.h
@@ -19,10 +19,17 @@
 
 #define GRALLOC1_LOG_TAG "Gralloc1"
 
-#include <ui/Gralloc1On0Adapter.h>
-
+#include <functional>
+#include <memory>
 #include <unordered_set>
 
+#include <log/log.h>
+
+#include <ui/Fence.h>
+
+#include <hardware/gralloc1.h>
+
+
 namespace std {
     template <>
     struct hash<gralloc1_capability_t> {
@@ -33,10 +40,42 @@
 }
 
 namespace android {
-
+class GraphicBuffer;
 class Fence;
 class GraphicBuffer;
+class Gralloc1On0Adapter;
+} // namespace android
 
+
+// This is not an "official" capability (i.e., it is not found in gralloc1.h),
+// but we will use it to detect that we are running through the adapter, which
+// is capable of collaborating with GraphicBuffer such that queries on a
+// buffer_handle_t succeed
+static const auto GRALLOC1_CAPABILITY_ON_ADAPTER =
+        static_cast<gralloc1_capability_t>(GRALLOC1_LAST_CAPABILITY + 1);
+
+static const auto GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER =
+        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 1);
+static const auto GRALLOC1_FUNCTION_ALLOCATE_WITH_ID =
+        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 2);
+static const auto GRALLOC1_FUNCTION_LOCK_YCBCR =
+        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 3);
+static const auto GRALLOC1_LAST_ADAPTER_FUNCTION = GRALLOC1_FUNCTION_LOCK_YCBCR;
+
+typedef gralloc1_error_t (*GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER)(
+        gralloc1_device_t* device, const android::GraphicBuffer* buffer);
+typedef gralloc1_error_t (*GRALLOC1_PFN_ALLOCATE_WITH_ID)(
+        gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptor,
+        gralloc1_backing_store_t id, buffer_handle_t* outBuffer);
+typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_LOCK_YCBCR)(
+        gralloc1_device_t* device, buffer_handle_t buffer,
+        uint64_t /*gralloc1_producer_usage_t*/ producerUsage,
+        uint64_t /*gralloc1_consumer_usage_t*/ consumerUsage,
+        const gralloc1_rect_t* accessRegion, struct android_ycbcr* outYCbCr,
+        int32_t acquireFence);
+
+
+namespace android {
 namespace Gralloc1 {
 
 class Device;
@@ -49,6 +88,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 +98,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 +109,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;
 
@@ -85,8 +127,6 @@
 
     std::shared_ptr<Descriptor> createDescriptor();
 
-    gralloc1_error_t getStride(buffer_handle_t buffer, uint32_t* outStride);
-
     gralloc1_error_t allocate(
             const std::vector<std::shared_ptr<const Descriptor>>& descriptors,
             std::vector<buffer_handle_t>* outBuffers);
@@ -99,6 +139,19 @@
 
     gralloc1_error_t release(buffer_handle_t buffer);
 
+    gralloc1_error_t getDimensions(buffer_handle_t buffer,
+            uint32_t* outWidth, uint32_t* outHeight);
+    gralloc1_error_t getFormat(buffer_handle_t buffer,
+            int32_t* outFormat);
+    gralloc1_error_t getLayerCount(buffer_handle_t buffer,
+            uint32_t* outLayerCount);
+    gralloc1_error_t getProducerUsage(buffer_handle_t buffer,
+            uint64_t* outProducerUsage);
+    gralloc1_error_t getConsumerUsage(buffer_handle_t buffer,
+            uint64_t* outConsumerUsage);
+    gralloc1_error_t getBackingStore(buffer_handle_t buffer,
+            uint64_t* outBackingStore);
+    gralloc1_error_t getStride(buffer_handle_t buffer, uint32_t* outStride);
     gralloc1_error_t getNumFlexPlanes(buffer_handle_t buffer,
             uint32_t* outNumPlanes);
 
@@ -178,6 +231,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 +243,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..6379a08 100644
--- a/include/ui/Gralloc1On0Adapter.h
+++ b/include/ui/Gralloc1On0Adapter.h
@@ -17,9 +17,11 @@
 #ifndef ANDROID_UI_GRALLOC_1_ON_0_ADAPTER_H
 #define ANDROID_UI_GRALLOC_1_ON_0_ADAPTER_H
 
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
+#include <log/log.h>
 
+#include <ui/Fence.h>
+
+#include <hardware/gralloc.h>
 #include <hardware/gralloc1.h>
 
 #include <mutex>
@@ -27,35 +29,12 @@
 #include <unordered_map>
 #include <vector>
 
+namespace android {
+class GraphicBuffer;
+} // namespace android
+
 struct gralloc_module_t;
 
-// This is not an "official" capability (i.e., it is not found in gralloc1.h),
-// but we will use it to detect that we are running through the adapter, which
-// is capable of collaborating with GraphicBuffer such that queries on a
-// buffer_handle_t succeed
-static const auto GRALLOC1_CAPABILITY_ON_ADAPTER =
-        static_cast<gralloc1_capability_t>(GRALLOC1_LAST_CAPABILITY + 1);
-
-static const auto GRALLOC1_FUNCTION_RETAIN_GRAPHIC_BUFFER =
-        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 1);
-static const auto GRALLOC1_FUNCTION_ALLOCATE_WITH_ID =
-        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 2);
-static const auto GRALLOC1_FUNCTION_LOCK_YCBCR =
-        static_cast<gralloc1_function_descriptor_t>(GRALLOC1_LAST_FUNCTION + 3);
-static const auto GRALLOC1_LAST_ADAPTER_FUNCTION = GRALLOC1_FUNCTION_LOCK_YCBCR;
-
-typedef gralloc1_error_t (*GRALLOC1_PFN_RETAIN_GRAPHIC_BUFFER)(
-        gralloc1_device_t* device, const android::GraphicBuffer* buffer);
-typedef gralloc1_error_t (*GRALLOC1_PFN_ALLOCATE_WITH_ID)(
-        gralloc1_device_t* device, gralloc1_buffer_descriptor_t descriptor,
-        gralloc1_backing_store_t id, buffer_handle_t* outBuffer);
-typedef int32_t /*gralloc1_error_t*/ (*GRALLOC1_PFN_LOCK_YCBCR)(
-        gralloc1_device_t* device, buffer_handle_t buffer,
-        uint64_t /*gralloc1_producer_usage_t*/ producerUsage,
-        uint64_t /*gralloc1_consumer_usage_t*/ consumerUsage,
-        const gralloc1_rect_t* accessRegion, struct android_ycbcr* outYCbCr,
-        int32_t acquireFence);
-
 namespace android {
 
 class Gralloc1On0Adapter : public gralloc1_device_t
@@ -131,6 +110,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 +125,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 +146,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 +183,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 +238,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
@@ -307,7 +304,7 @@
         auto usage = GRALLOC1_CONSUMER_USAGE_NONE;
         auto error = callBufferFunction(device, bufferHandle,
                 &Buffer::getConsumerUsage, &usage);
-        if (error != GRALLOC1_ERROR_NONE) {
+        if (error == GRALLOC1_ERROR_NONE) {
             *outUsage = static_cast<uint64_t>(usage);
         }
         return error;
@@ -318,7 +315,7 @@
         auto usage = GRALLOC1_PRODUCER_USAGE_NONE;
         auto error = callBufferFunction(device, bufferHandle,
                 &Buffer::getProducerUsage, &usage);
-        if (error != GRALLOC1_ERROR_NONE) {
+        if (error == GRALLOC1_ERROR_NONE) {
             *outUsage = static_cast<uint64_t>(usage);
         }
         return error;
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..5a0d64b
--- /dev/null
+++ b/include/ui/GrallocMapper.h
@@ -0,0 +1,74 @@
+/*
+ * 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 <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 getDimensions(buffer_handle_t handle,
+            uint32_t* outWidth, uint32_t* outHeight) const;
+    Error getFormat(buffer_handle_t handle, int32_t* outFormat) const;
+    Error getLayerCount(buffer_handle_t handle, uint32_t* outLayerCount) const;
+    Error getProducerUsage(buffer_handle_t handle,
+            uint64_t* outProducerUsage) const;
+    Error getConsumerUsage(buffer_handle_t handle,
+            uint64_t* outConsumerUsage) const;
+    Error getBackingStore(buffer_handle_t handle,
+            uint64_t* outBackingStore) const;
+    Error getStride(buffer_handle_t handle, uint32_t* outStride) const;
+
+    Error lock(buffer_handle_t handle, uint64_t producerUsage,
+            uint64_t consumerUsage, const IMapper::Rect& accessRegion,
+            int acquireFence, void** outData) const;
+    Error lock(buffer_handle_t handle, uint64_t producerUsage,
+            uint64_t consumerUsage, 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..040d1e7 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -20,13 +20,15 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <string>
+
 #include <ui/ANativeObjectBase.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
 #include <utils/RefBase.h>
 
-#include <string>
+#include <hardware/gralloc.h>
 
 struct ANativeWindowBuffer;
 
@@ -76,10 +78,21 @@
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
             uint32_t inUsage, std::string requestorName = "<Unknown>");
 
+    // creates w * h buffer with a layer count using gralloc1
+    GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+            uint32_t inLayerCount, uint64_t inProducerUsage,
+            uint64_t inConsumerUsage, 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 handle using gralloc1
+    GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+            uint32_t inLayerCount, uint32_t inProducerUsage,
+            uint32_t inConsumerUsage, uint32_t inStride,
+            native_handle_t* inHandle, bool keepOwnership);
 
     // create a buffer from an existing ANativeWindowBuffer
     GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership);
@@ -92,6 +105,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 +115,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);
@@ -116,6 +130,8 @@
     status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd);
     status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr,
             int fenceFd);
+    status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage,
+            const Rect& rect, void** vaddr, int fenceFd);
     status_t lockAsyncYCbCr(uint32_t inUsage, android_ycbcr *ycbcr,
             int fenceFd);
     status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect,
@@ -160,7 +176,8 @@
     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, uint64_t inProducerUsage,
+            uint64_t inConsumerUsage, std::string requestorName);
 
     void free_handle();
 
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 28d0238..e97122b 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -20,11 +20,14 @@
 
 #include <stdint.h>
 
+#include <memory>
+#include <string>
+
 #include <cutils/native_handle.h>
 
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
-#include <utils/threads.h>
+#include <utils/Mutex.h>
 #include <utils/Singleton.h>
 
 #include <ui/Gralloc1.h>
@@ -32,7 +35,11 @@
 
 namespace android {
 
-class Gralloc1Loader;
+namespace Gralloc2 {
+class Allocator;
+}
+
+class GraphicBufferMapper;
 class String8;
 
 class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
@@ -60,8 +67,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, uint64_t producerUsage, uint64_t consumerUsage,
+            buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
+            std::string requestorName);
 
     status_t free(buffer_handle_t handle);
 
@@ -74,7 +82,9 @@
         uint32_t height;
         uint32_t stride;
         PixelFormat format;
-        uint32_t usage;
+        uint32_t layerCount;
+        uint64_t producerUsage;
+        uint64_t consumerUsage;
         size_t size;
         std::string requestorName;
     };
@@ -86,6 +96,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..b6d4021 100644
--- a/include/ui/GraphicBufferMapper.h
+++ b/include/ui/GraphicBufferMapper.h
@@ -20,14 +20,26 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <memory>
+
 #include <ui/Gralloc1.h>
 
 #include <utils/Singleton.h>
 
+
+// Needed by code that still uses the GRALLOC_USAGE_* constants.
+// when/if we get rid of gralloc, we should provide aliases or fix call sites.
+#include <hardware/gralloc.h>
+
+
 namespace android {
 
 // ---------------------------------------------------------------------------
 
+namespace Gralloc2 {
+class Mapper;
+}
+
 class Rect;
 
 class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -35,11 +47,32 @@
 public:
     static inline GraphicBufferMapper& get() { return getInstance(); }
 
+    // This may NOT work on devices without a valid Gralloc2::Mapper.
     status_t registerBuffer(buffer_handle_t handle);
+
     status_t registerBuffer(const GraphicBuffer* buffer);
 
     status_t unregisterBuffer(buffer_handle_t handle);
 
+    status_t getDimensions(buffer_handle_t handle,
+            uint32_t* outWidth, uint32_t* outHeight) const;
+
+    status_t getFormat(buffer_handle_t handle, int32_t* outFormat) const;
+
+    status_t getLayerCount(buffer_handle_t handle,
+            uint32_t* outLayerCount) const;
+
+    status_t getProducerUsage(buffer_handle_t handle,
+            uint64_t* outProducerUsage) const;
+
+    status_t getConsumerUsage(buffer_handle_t handle,
+            uint64_t* outConsumerUsage) const;
+
+    status_t getBackingStore(buffer_handle_t handle,
+            uint64_t* outBackingStore) const;
+
+    status_t getStride(buffer_handle_t handle, uint32_t* outStride) const;
+
     status_t lock(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, void** vaddr);
 
@@ -51,17 +84,28 @@
     status_t lockAsync(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd);
 
+    status_t lockAsync(buffer_handle_t handle,
+            uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
+            void** vaddr, int fenceFd);
+
     status_t lockAsyncYCbCr(buffer_handle_t handle,
             uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr,
             int fenceFd);
 
     status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
 
+    const Gralloc2::Mapper& getGrallocMapper() const
+    {
+        return *mMapper;
+    }
+
 private:
     friend class Singleton<GraphicBufferMapper>;
 
     GraphicBufferMapper();
 
+    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/GraphicsEnv.h b/include/ui/GraphicsEnv.h
new file mode 100644
index 0000000..7817076
--- /dev/null
+++ b/include/ui/GraphicsEnv.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_GRAPHICS_ENV_H
+#define ANDROID_UI_GRAPHICS_ENV_H 1
+
+#include <string>
+
+struct android_namespace_t;
+
+namespace android {
+
+class GraphicsEnv {
+public:
+    static GraphicsEnv& getInstance();
+
+    // Set a search path for loading graphics drivers. The path is a list of
+    // directories separated by ':'. A directory can be contained in a zip file
+    // (drivers must be stored uncompressed and page aligned); such elements
+    // in the search path must have a '!' after the zip filename, e.g.
+    //     /data/app/com.example.driver/base.apk!/lib/arm64-v8a
+    void setDriverPath(const std::string path);
+    android_namespace_t* getDriverNamespace();
+
+private:
+    GraphicsEnv() = default;
+    std::string mDriverPath;
+    android_namespace_t* mDriverNamespace = nullptr;
+};
+
+} // namespace android
+
+/* FIXME
+ * Export an un-mangled function that just does
+ *     return android::GraphicsEnv::getInstance().getDriverNamespace();
+ * This allows libEGL to get the function pointer via dlsym, since it can't
+ * directly link against libgui. In a future release, we'll fix this so that
+ * libgui does not depend on graphics API libraries, and libEGL can link
+ * against it. The current dependencies from libgui -> libEGL are:
+ *  - the GLConsumer class, which should be moved to its own library
+ *  - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and
+ *    will be removed soon.
+ */
+extern "C" android_namespace_t* android_getDriverNamespace();
+
+#endif // ANDROID_UI_GRAPHICS_ENV_H
diff --git a/include/ui/HdrCapabilities.h b/include/ui/HdrCapabilities.h
index a7cd5fb..925aa1b 100644
--- a/include/ui/HdrCapabilities.h
+++ b/include/ui/HdrCapabilities.h
@@ -17,11 +17,15 @@
 #ifndef ANDROID_UI_HDR_CAPABILTIES_H
 #define ANDROID_UI_HDR_CAPABILTIES_H
 
-#include <binder/Parcelable.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include <utils/Flattenable.h>
 
 namespace android {
 
-class HdrCapabilities : public Parcelable
+class HdrCapabilities : public LightFlattenable<HdrCapabilities>
 {
 public:
     HdrCapabilities(const std::vector<int32_t /*android_hdr_t*/>& types,
@@ -32,8 +36,8 @@
         mMinLuminance(minLuminance) {}
 
     // Make this move-constructable and move-assignable
-    HdrCapabilities(HdrCapabilities&& other) = default;
-    HdrCapabilities& operator=(HdrCapabilities&& other) = default;
+    HdrCapabilities(HdrCapabilities&& other);
+    HdrCapabilities& operator=(HdrCapabilities&& other);
 
     HdrCapabilities()
       : mSupportedHdrTypes(),
@@ -41,7 +45,7 @@
         mMaxAverageLuminance(-1.0f),
         mMinLuminance(-1.0f) {}
 
-    virtual ~HdrCapabilities() = default;
+    ~HdrCapabilities();
 
     const std::vector<int32_t /*android_hdr_t*/>& getSupportedHdrTypes() const {
         return mSupportedHdrTypes;
@@ -50,9 +54,11 @@
     float getDesiredMaxAverageLuminance() const { return mMaxAverageLuminance; }
     float getDesiredMinLuminance() const { return mMinLuminance; }
 
-    // Parcelable interface
-    virtual status_t writeToParcel(Parcel* parcel) const override;
-    virtual status_t readFromParcel(const Parcel* parcel) override;
+    // Flattenable protocol
+    bool isFixedSize() const { return false; }
+    size_t getFlattenedSize() const;
+    status_t flatten(void* buffer, size_t size) const;
+    status_t unflatten(void const* buffer, size_t size);
 
 private:
     std::vector<int32_t /*android_hdr_t*/> mSupportedHdrTypes;
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index f26fecb..02773d9 100644
--- a/include/ui/PixelFormat.h
+++ b/include/ui/PixelFormat.h
@@ -53,13 +53,15 @@
 
     // real pixel formats supported for rendering -----------------------------
 
-    PIXEL_FORMAT_RGBA_8888   = HAL_PIXEL_FORMAT_RGBA_8888,   // 4x8-bit RGBA
-    PIXEL_FORMAT_RGBX_8888   = HAL_PIXEL_FORMAT_RGBX_8888,   // 4x8-bit RGB0
-    PIXEL_FORMAT_RGB_888     = HAL_PIXEL_FORMAT_RGB_888,     // 3x8-bit RGB
-    PIXEL_FORMAT_RGB_565     = HAL_PIXEL_FORMAT_RGB_565,     // 16-bit RGB
-    PIXEL_FORMAT_BGRA_8888   = HAL_PIXEL_FORMAT_BGRA_8888,   // 4x8-bit BGRA
-    PIXEL_FORMAT_RGBA_5551   = 6,                            // 16-bit ARGB
-    PIXEL_FORMAT_RGBA_4444   = 7,                            // 16-bit ARGB
+    PIXEL_FORMAT_RGBA_8888    = HAL_PIXEL_FORMAT_RGBA_8888,    // 4x8-bit RGBA
+    PIXEL_FORMAT_RGBX_8888    = HAL_PIXEL_FORMAT_RGBX_8888,    // 4x8-bit RGB0
+    PIXEL_FORMAT_RGB_888      = HAL_PIXEL_FORMAT_RGB_888,      // 3x8-bit RGB
+    PIXEL_FORMAT_RGB_565      = HAL_PIXEL_FORMAT_RGB_565,      // 16-bit RGB
+    PIXEL_FORMAT_BGRA_8888    = HAL_PIXEL_FORMAT_BGRA_8888,    // 4x8-bit BGRA
+    PIXEL_FORMAT_RGBA_5551    = 6,                             // 16-bit ARGB
+    PIXEL_FORMAT_RGBA_4444    = 7,                             // 16-bit ARGB
+    PIXEL_FORMAT_RGBA_FP16    = HAL_PIXEL_FORMAT_RGBA_FP16,    // 64-bit RGBA
+    PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
 };
 
 typedef int32_t PixelFormat;
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index e9859fe..b50e4ec 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -22,6 +22,7 @@
 #include <utils/TypeHelpers.h>
 #include <log/log.h>
 
+#include <ui/FloatRect.h>
 #include <ui/Point.h>
 
 #include <android/rect.h>
@@ -44,13 +45,9 @@
     template <typename T>
     inline Rect(T w, T h) {
         if (w > INT32_MAX) {
-            ALOG(LOG_WARN, "Rect",
-                    "Width %u too large for Rect class, clamping", w);
             w = INT32_MAX;
         }
         if (h > INT32_MAX) {
-            ALOG(LOG_WARN, "Rect",
-                    "Height %u too large for Rect class, clamping", h);
             h = INT32_MAX;
         }
         left = top = 0;
@@ -179,11 +176,15 @@
     // this calculates (Region(*this) - exclude).bounds() efficiently
     Rect reduce(const Rect& exclude) const;
 
-
     // for backward compatibility
     inline int32_t width() const { return getWidth(); }
     inline int32_t height() const { return getHeight(); }
     inline void set(const Rect& rhs) { operator = (rhs); }
+
+    FloatRect toFloatRect() const {
+        return {static_cast<float>(left), static_cast<float>(top),
+                static_cast<float>(right), static_cast<float>(bottom)};
+    }
 };
 
 ANDROID_BASIC_TYPES_TRAITS(Rect)
diff --git a/include/ui/TMatHelpers.h b/include/ui/TMatHelpers.h
deleted file mode 100644
index a6aadca..0000000
--- a/include/ui/TMatHelpers.h
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TMAT_IMPLEMENTATION
-#error "Don't include TMatHelpers.h directly. use ui/mat*.h instead"
-#else
-#undef TMAT_IMPLEMENTATION
-#endif
-
-
-#ifndef UI_TMAT_HELPERS_H
-#define UI_TMAT_HELPERS_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <math.h>
-#include <utils/Debug.h>
-#include <utils/String8.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-/*
- * No user serviceable parts here.
- *
- * Don't use this file directly, instead include ui/mat*.h
- */
-
-
-/*
- * Matrix utilities
- */
-
-namespace matrix {
-
-inline int     PURE transpose(int v)    { return v; }
-inline float   PURE transpose(float v)  { return v; }
-inline double  PURE transpose(double v) { return v; }
-
-inline int     PURE trace(int v)    { return v; }
-inline float   PURE trace(float v)  { return v; }
-inline double  PURE trace(double v) { return v; }
-
-template<typename MATRIX>
-MATRIX PURE inverse(const MATRIX& src) {
-
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::COL_SIZE == MATRIX::ROW_SIZE );
-
-    typename MATRIX::value_type t;
-    const size_t N = MATRIX::col_size();
-    size_t swap;
-    MATRIX tmp(src);
-    MATRIX inverse(1);
-
-    for (size_t i=0 ; i<N ; i++) {
-        // look for largest element in column
-        swap = i;
-        for (size_t j=i+1 ; j<N ; j++) {
-            if (fabs(tmp[j][i]) > fabs(tmp[i][i])) {
-                swap = j;
-            }
-        }
-
-        if (swap != i) {
-            /* swap rows. */
-            for (size_t k=0 ; k<N ; k++) {
-                t = tmp[i][k];
-                tmp[i][k] = tmp[swap][k];
-                tmp[swap][k] = t;
-
-                t = inverse[i][k];
-                inverse[i][k] = inverse[swap][k];
-                inverse[swap][k] = t;
-            }
-        }
-
-        t = 1 / tmp[i][i];
-        for (size_t k=0 ; k<N ; k++) {
-            tmp[i][k] *= t;
-            inverse[i][k] *= t;
-        }
-        for (size_t j=0 ; j<N ; j++) {
-            if (j != i) {
-                t = tmp[j][i];
-                for (size_t k=0 ; k<N ; k++) {
-                    tmp[j][k] -= tmp[i][k] * t;
-                    inverse[j][k] -= inverse[i][k] * t;
-                }
-            }
-        }
-    }
-    return inverse;
-}
-
-template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
-MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
-    // pre-requisite:
-    //  lhs : D columns, R rows
-    //  rhs : C columns, D rows
-    //  res : C columns, R rows
-
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_A::ROW_SIZE == MATRIX_B::COL_SIZE );
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::ROW_SIZE == MATRIX_B::ROW_SIZE );
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX_R::COL_SIZE == MATRIX_A::COL_SIZE );
-
-    MATRIX_R res(MATRIX_R::NO_INIT);
-    for (size_t r=0 ; r<MATRIX_R::row_size() ; r++) {
-        res[r] = lhs * rhs[r];
-    }
-    return res;
-}
-
-// transpose. this handles matrices of matrices
-template <typename MATRIX>
-MATRIX PURE transpose(const MATRIX& m) {
-    // for now we only handle square matrix transpose
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
-    MATRIX result(MATRIX::NO_INIT);
-    for (size_t r=0 ; r<MATRIX::row_size() ; r++)
-        for (size_t c=0 ; c<MATRIX::col_size() ; c++)
-            result[c][r] = transpose(m[r][c]);
-    return result;
-}
-
-// trace. this handles matrices of matrices
-template <typename MATRIX>
-typename MATRIX::value_type PURE trace(const MATRIX& m) {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
-    typename MATRIX::value_type result(0);
-    for (size_t r=0 ; r<MATRIX::row_size() ; r++)
-        result += trace(m[r][r]);
-    return result;
-}
-
-// trace. this handles matrices of matrices
-template <typename MATRIX>
-typename MATRIX::col_type PURE diag(const MATRIX& m) {
-    COMPILE_TIME_ASSERT_FUNCTION_SCOPE( MATRIX::ROW_SIZE == MATRIX::COL_SIZE );
-    typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
-    for (size_t r=0 ; r<MATRIX::row_size() ; r++)
-        result[r] = m[r][r];
-    return result;
-}
-
-template <typename MATRIX>
-String8 asString(const MATRIX& m) {
-    String8 s;
-    for (size_t c=0 ; c<MATRIX::col_size() ; c++) {
-        s.append("|  ");
-        for (size_t r=0 ; r<MATRIX::row_size() ; r++) {
-            s.appendFormat("%7.2f  ", m[r][c]);
-        }
-        s.append("|\n");
-    }
-    return s;
-}
-
-}; // namespace matrix
-
-// -------------------------------------------------------------------------------------
-
-/*
- * TMatProductOperators implements basic arithmetic and basic compound assignments
- * operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template <template<typename T> class BASE, typename T>
-class TMatProductOperators {
-public:
-    // multiply by a scalar
-    BASE<T>& operator *= (T v) {
-        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
-        for (size_t r=0 ; r<lhs.row_size() ; r++) {
-            lhs[r] *= v;
-        }
-        return lhs;
-    }
-
-    // divide by a scalar
-    BASE<T>& operator /= (T v) {
-        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
-        for (size_t r=0 ; r<lhs.row_size() ; r++) {
-            lhs[r] /= v;
-        }
-        return lhs;
-    }
-
-    // matrix * matrix, result is a matrix of the same type than the lhs matrix
-    template<typename U>
-    friend BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
-        return matrix::multiply<BASE<T> >(lhs, rhs);
-    }
-};
-
-
-/*
- * TMatSquareFunctions implements functions on a matrix of type BASE<T>.
- *
- * BASE only needs to implement:
- *  - operator[]
- *  - col_type
- *  - row_type
- *  - COL_SIZE
- *  - ROW_SIZE
- *
- * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template<template<typename U> class BASE, typename T>
-class TMatSquareFunctions {
-public:
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-    friend BASE<T> PURE inverse(const BASE<T>& m)   { return matrix::inverse(m); }
-    friend BASE<T> PURE transpose(const BASE<T>& m) { return matrix::transpose(m); }
-    friend T       PURE trace(const BASE<T>& m)     { return matrix::trace(m); }
-};
-
-template <template<typename T> class BASE, typename T>
-class TMatDebug {
-public:
-    String8 asString() const {
-        return matrix::asString( static_cast< const BASE<T>& >(*this) );
-    }
-};
-
-// -------------------------------------------------------------------------------------
-}; // namespace android
-
-#undef PURE
-
-#endif /* UI_TMAT_HELPERS_H */
diff --git a/include/ui/TVecHelpers.h b/include/ui/TVecHelpers.h
deleted file mode 100644
index bb7dbfc..0000000
--- a/include/ui/TVecHelpers.h
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TVEC_IMPLEMENTATION
-#error "Don't include TVecHelpers.h directly. use ui/vec*.h instead"
-#else
-#undef TVEC_IMPLEMENTATION
-#endif
-
-
-#ifndef UI_TVEC_HELPERS_H
-#define UI_TVEC_HELPERS_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-/*
- * No user serviceable parts here.
- *
- * Don't use this file directly, instead include ui/vec{2|3|4}.h
- */
-
-/*
- * This class casts itself into anything and assign itself from anything!
- * Use with caution!
- */
-template <typename TYPE>
-struct Impersonator {
-    Impersonator& operator = (const TYPE& rhs) {
-        reinterpret_cast<TYPE&>(*this) = rhs;
-        return *this;
-    }
-    operator TYPE& () {
-        return reinterpret_cast<TYPE&>(*this);
-    }
-    operator TYPE const& () const {
-        return reinterpret_cast<TYPE const&>(*this);
-    }
-};
-
-/*
- * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
- * operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-
-template <template<typename T> class BASE, typename T>
-class TVecAddOperators {
-public:
-    /* compound assignment from a another vector of the same size but different
-     * element type.
-     */
-    template <typename OTHER>
-    BASE<T>& operator += (const BASE<OTHER>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] += v[i];
-        }
-        return rhs;
-    }
-    template <typename OTHER>
-    BASE<T>& operator -= (const BASE<OTHER>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] -= v[i];
-        }
-        return rhs;
-    }
-
-    /* compound assignment from a another vector of the same type.
-     * These operators can be used for implicit conversion and  handle operations
-     * like "vector *= scalar" by letting the compiler implicitly convert a scalar
-     * to a vector (assuming the BASE<T> allows it).
-     */
-    BASE<T>& operator += (const BASE<T>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] += v[i];
-        }
-        return rhs;
-    }
-    BASE<T>& operator -= (const BASE<T>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] -= v[i];
-        }
-        return rhs;
-    }
-
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-
-    /* The operators below handle operation between vectors of the same side
-     * but of a different element type.
-     */
-    template<typename RT>
-    friend inline
-    BASE<T> PURE operator +(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) += rv;
-    }
-    template<typename RT>
-    friend inline
-    BASE<T> PURE operator -(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) -= rv;
-    }
-
-    /* The operators below (which are not templates once this class is instanced,
-     * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
-     * These handle operations like "vector * scalar" and "scalar * vector" by
-     * letting the compiler implicitly convert a scalar to a vector (assuming
-     * the BASE<T> allows it).
-     */
-    friend inline
-    BASE<T> PURE operator +(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(lv) += rv;
-    }
-    friend inline
-    BASE<T> PURE operator -(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(lv) -= rv;
-    }
-};
-
-template <template<typename T> class BASE, typename T>
-class TVecProductOperators {
-public:
-    /* compound assignment from a another vector of the same size but different
-     * element type.
-     */
-    template <typename OTHER>
-    BASE<T>& operator *= (const BASE<OTHER>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] *= v[i];
-        }
-        return rhs;
-    }
-    template <typename OTHER>
-    BASE<T>& operator /= (const BASE<OTHER>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] /= v[i];
-        }
-        return rhs;
-    }
-
-    /* compound assignment from a another vector of the same type.
-     * These operators can be used for implicit conversion and  handle operations
-     * like "vector *= scalar" by letting the compiler implicitly convert a scalar
-     * to a vector (assuming the BASE<T> allows it).
-     */
-    BASE<T>& operator *= (const BASE<T>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] *= v[i];
-        }
-        return rhs;
-    }
-    BASE<T>& operator /= (const BASE<T>& v) {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            rhs[i] /= v[i];
-        }
-        return rhs;
-    }
-
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-
-    /* The operators below handle operation between vectors of the same side
-     * but of a different element type.
-     */
-    template<typename RT>
-    friend inline
-    BASE<T> PURE operator *(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) *= rv;
-    }
-    template<typename RT>
-    friend inline
-    BASE<T> PURE operator /(const BASE<T>& lv, const BASE<RT>& rv) {
-        return BASE<T>(lv) /= rv;
-    }
-
-    /* The operators below (which are not templates once this class is instanced,
-     * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
-     * These handle operations like "vector * scalar" and "scalar * vector" by
-     * letting the compiler implicitly convert a scalar to a vector (assuming
-     * the BASE<T> allows it).
-     */
-    friend inline
-    BASE<T> PURE operator *(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(lv) *= rv;
-    }
-    friend inline
-    BASE<T> PURE operator /(const BASE<T>& lv, const BASE<T>& rv) {
-        return BASE<T>(lv) /= rv;
-    }
-};
-
-/*
- * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- *
- * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
- */
-template <template<typename T> class BASE, typename T>
-class TVecUnaryOperators {
-public:
-    BASE<T>& operator ++ () {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            ++rhs[i];
-        }
-        return rhs;
-    }
-    BASE<T>& operator -- () {
-        BASE<T>& rhs = static_cast<BASE<T>&>(*this);
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            --rhs[i];
-        }
-        return rhs;
-    }
-    BASE<T> operator - () const {
-        BASE<T> r(BASE<T>::NO_INIT);
-        BASE<T> const& rv(static_cast<BASE<T> const&>(*this));
-        for (size_t i=0 ; i<BASE<T>::size() ; i++) {
-            r[i] = -rv[i];
-        }
-        return r;
-    }
-};
-
-
-/*
- * TVecComparisonOperators implements relational/comparison operators
- * on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-template <template<typename T> class BASE, typename T>
-class TVecComparisonOperators {
-public:
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-    template<typename RT>
-    friend inline
-    bool PURE operator ==(const BASE<T>& lv, const BASE<RT>& rv) {
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            if (lv[i] != rv[i])
-                return false;
-        return true;
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator !=(const BASE<T>& lv, const BASE<RT>& rv) {
-        return !operator ==(lv, rv);
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator >(const BASE<T>& lv, const BASE<RT>& rv) {
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            if (lv[i] <= rv[i])
-                return false;
-        return true;
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator <=(const BASE<T>& lv, const BASE<RT>& rv) {
-        return !(lv > rv);
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator <(const BASE<T>& lv, const BASE<RT>& rv) {
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            if (lv[i] >= rv[i])
-                return false;
-        return true;
-    }
-
-    template<typename RT>
-    friend inline
-    bool PURE operator >=(const BASE<T>& lv, const BASE<RT>& rv) {
-        return !(lv < rv);
-    }
-};
-
-
-/*
- * TVecFunctions implements functions on a vector of type BASE<T>.
- *
- * BASE only needs to implement operator[] and size().
- * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
- * get all the functionality here.
- */
-template <template<typename T> class BASE, typename T>
-class TVecFunctions {
-public:
-    /*
-     * NOTE: the functions below ARE NOT member methods. They are friend functions
-     * with they definition inlined with their declaration. This makes these
-     * template functions available to the compiler when (and only when) this class
-     * is instantiated, at which point they're only templated on the 2nd parameter
-     * (the first one, BASE<T> being known).
-     */
-    template<typename RT>
-    friend inline
-    T PURE dot(const BASE<T>& lv, const BASE<RT>& rv) {
-        T r(0);
-        for (size_t i = 0; i < BASE<T>::size(); i++)
-            r += lv[i]*rv[i];
-        return r;
-    }
-
-    friend inline
-    T PURE length(const BASE<T>& lv) {
-        return sqrt( dot(lv, lv) );
-    }
-
-    template<typename RT>
-    friend inline
-    T PURE distance(const BASE<T>& lv, const BASE<RT>& rv) {
-        return length(rv - lv);
-    }
-
-    friend inline
-    BASE<T> PURE normalize(const BASE<T>& lv) {
-        return lv * (1 / length(lv));
-    }
-};
-
-#undef PURE
-
-// -------------------------------------------------------------------------------------
-}; // namespace android
-
-
-#endif /* UI_TVEC_HELPERS_H */
diff --git a/include/ui/mat4.h b/include/ui/mat4.h
deleted file mode 100644
index 4fd1eff..0000000
--- a/include/ui/mat4.h
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UI_MAT4_H
-#define UI_MAT4_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec4.h>
-#include <utils/String8.h>
-
-#define TMAT_IMPLEMENTATION
-#include <ui/TMatHelpers.h>
-
-#define PURE __attribute__((pure))
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tmat44 :  public TVecUnaryOperators<tmat44, T>,
-                public TVecComparisonOperators<tmat44, T>,
-                public TVecAddOperators<tmat44, T>,
-                public TMatProductOperators<tmat44, T>,
-                public TMatSquareFunctions<tmat44, T>,
-                public TMatDebug<tmat44, T>
-{
-public:
-    enum no_init { NO_INIT };
-    typedef T value_type;
-    typedef T& reference;
-    typedef T const& const_reference;
-    typedef size_t size_type;
-    typedef tvec4<T> col_type;
-    typedef tvec4<T> row_type;
-
-    // size of a column (i.e.: number of rows)
-    enum { COL_SIZE = col_type::SIZE };
-    static inline size_t col_size() { return COL_SIZE; }
-
-    // size of a row (i.e.: number of columns)
-    enum { ROW_SIZE = row_type::SIZE };
-    static inline size_t row_size() { return ROW_SIZE; }
-    static inline size_t size()     { return row_size(); }  // for TVec*<>
-
-private:
-
-    /*
-     *  <--  N columns  -->
-     *
-     *  a00 a10 a20 ... aN0    ^
-     *  a01 a11 a21 ... aN1    |
-     *  a02 a12 a22 ... aN2  M rows
-     *  ...                    |
-     *  a0M a1M a2M ... aNM    v
-     *
-     *  COL_SIZE = M
-     *  ROW_SIZE = N
-     *  m[0] = [a00 a01 a02 ... a01M]
-     */
-
-    col_type mValue[ROW_SIZE];
-
-public:
-    // array access
-    inline col_type const& operator [] (size_t i) const { return mValue[i]; }
-    inline col_type&       operator [] (size_t i)       { return mValue[i]; }
-
-    T const* asArray() const { return &mValue[0][0]; }
-
-    // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
-
-    /*
-     *  constructors
-     */
-
-    // leaves object uninitialized. use with caution.
-    explicit tmat44(no_init) { }
-
-    // initialize to identity
-    tmat44();
-
-    // initialize to Identity*scalar.
-    template<typename U>
-    explicit tmat44(U v);
-
-    // sets the diagonal to the passed vector
-    template <typename U>
-    explicit tmat44(const tvec4<U>& rhs);
-
-    // construct from another matrix of the same size
-    template <typename U>
-    explicit tmat44(const tmat44<U>& rhs);
-
-    // construct from 4 column vectors
-    template <typename A, typename B, typename C, typename D>
-    tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3);
-
-    // construct from 16 scalars
-    template <
-        typename A, typename B, typename C, typename D,
-        typename E, typename F, typename G, typename H,
-        typename I, typename J, typename K, typename L,
-        typename M, typename N, typename O, typename P>
-    tmat44( A m00, B m01, C m02, D m03,
-            E m10, F m11, G m12, H m13,
-            I m20, J m21, K m22, L m23,
-            M m30, N m31, O m32, P m33);
-
-    // construct from a C array
-    template <typename U>
-    explicit tmat44(U const* rawArray);
-
-    /*
-     *  helpers
-     */
-
-    static tmat44 ortho(T left, T right, T bottom, T top, T near, T far);
-
-    static tmat44 frustum(T left, T right, T bottom, T top, T near, T far);
-
-    template <typename A, typename B, typename C>
-    static tmat44 lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up);
-
-    template <typename A>
-    static tmat44 translate(const tvec4<A>& t);
-
-    template <typename A>
-    static tmat44 scale(const tvec4<A>& s);
-
-    template <typename A, typename B>
-    static tmat44 rotate(A radian, const tvec3<B>& about);
-};
-
-// ----------------------------------------------------------------------------------------
-// Constructors
-// ----------------------------------------------------------------------------------------
-
-/*
- * Since the matrix code could become pretty big quickly, we don't inline most
- * operations.
- */
-
-template <typename T>
-tmat44<T>::tmat44() {
-    mValue[0] = col_type(1,0,0,0);
-    mValue[1] = col_type(0,1,0,0);
-    mValue[2] = col_type(0,0,1,0);
-    mValue[3] = col_type(0,0,0,1);
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(U v) {
-    mValue[0] = col_type(v,0,0,0);
-    mValue[1] = col_type(0,v,0,0);
-    mValue[2] = col_type(0,0,v,0);
-    mValue[3] = col_type(0,0,0,v);
-}
-
-template<typename T>
-template<typename U>
-tmat44<T>::tmat44(const tvec4<U>& v) {
-    mValue[0] = col_type(v.x,0,0,0);
-    mValue[1] = col_type(0,v.y,0,0);
-    mValue[2] = col_type(0,0,v.z,0);
-    mValue[3] = col_type(0,0,0,v.w);
-}
-
-// construct from 16 scalars
-template<typename T>
-template <
-    typename A, typename B, typename C, typename D,
-    typename E, typename F, typename G, typename H,
-    typename I, typename J, typename K, typename L,
-    typename M, typename N, typename O, typename P>
-tmat44<T>::tmat44(  A m00, B m01, C m02, D m03,
-                    E m10, F m11, G m12, H m13,
-                    I m20, J m21, K m22, L m23,
-                    M m30, N m31, O m32, P m33) {
-    mValue[0] = col_type(m00, m01, m02, m03);
-    mValue[1] = col_type(m10, m11, m12, m13);
-    mValue[2] = col_type(m20, m21, m22, m23);
-    mValue[3] = col_type(m30, m31, m32, m33);
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(const tmat44<U>& rhs) {
-    for (size_t r=0 ; r<row_size() ; r++)
-        mValue[r] = rhs[r];
-}
-
-template <typename T>
-template <typename A, typename B, typename C, typename D>
-tmat44<T>::tmat44(const tvec4<A>& v0, const tvec4<B>& v1, const tvec4<C>& v2, const tvec4<D>& v3) {
-    mValue[0] = v0;
-    mValue[1] = v1;
-    mValue[2] = v2;
-    mValue[3] = v3;
-}
-
-template <typename T>
-template <typename U>
-tmat44<T>::tmat44(U const* rawArray) {
-    for (size_t r=0 ; r<row_size() ; r++)
-        for (size_t c=0 ; c<col_size() ; c++)
-            mValue[r][c] = *rawArray++;
-}
-
-// ----------------------------------------------------------------------------------------
-// Helpers
-// ----------------------------------------------------------------------------------------
-
-template <typename T>
-tmat44<T> tmat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
-    tmat44<T> m;
-    m[0][0] =  2 / (right - left);
-    m[1][1] =  2 / (top   - bottom);
-    m[2][2] = -2 / (far   - near);
-    m[3][0] = -(right + left)   / (right - left);
-    m[3][1] = -(top   + bottom) / (top   - bottom);
-    m[3][2] = -(far   + near)   / (far   - near);
-    return m;
-}
-
-template <typename T>
-tmat44<T> tmat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
-    tmat44<T> m;
-    T A = (right + left)   / (right - left);
-    T B = (top   + bottom) / (top   - bottom);
-    T C = (far   + near)   / (far   - near);
-    T D = (2 * far * near) / (far   - near);
-    m[0][0] = (2 * near) / (right - left);
-    m[1][1] = (2 * near) / (top   - bottom);
-    m[2][0] = A;
-    m[2][1] = B;
-    m[2][2] = C;
-    m[2][3] =-1;
-    m[3][2] = D;
-    m[3][3] = 0;
-    return m;
-}
-
-template <typename T>
-template <typename A, typename B, typename C>
-tmat44<T> tmat44<T>::lookAt(const tvec3<A>& eye, const tvec3<B>& center, const tvec3<C>& up) {
-    tvec3<T> L(normalize(center - eye));
-    tvec3<T> S(normalize( cross(L, up) ));
-    tvec3<T> U(cross(S, L));
-    return tmat44<T>(
-            tvec4<T>( S, 0),
-            tvec4<T>( U, 0),
-            tvec4<T>(-L, 0),
-            tvec4<T>(-eye, 1));
-}
-
-template <typename T>
-template <typename A>
-tmat44<T> tmat44<T>::translate(const tvec4<A>& t) {
-    tmat44<T> r;
-    r[3] = t;
-    return r;
-}
-
-template <typename T>
-template <typename A>
-tmat44<T> tmat44<T>::scale(const tvec4<A>& s) {
-    tmat44<T> r;
-    r[0][0] = s[0];
-    r[1][1] = s[1];
-    r[2][2] = s[2];
-    r[3][3] = s[3];
-    return r;
-}
-
-template <typename T>
-template <typename A, typename B>
-tmat44<T> tmat44<T>::rotate(A radian, const tvec3<B>& about) {
-    tmat44<T> rotation;
-    T* r = const_cast<T*>(rotation.asArray());
-    T c = cos(radian);
-    T s = sin(radian);
-    if (about.x==1 && about.y==0 && about.z==0) {
-        r[5] = c;   r[10]= c;
-        r[6] = s;   r[9] = -s;
-    } else if (about.x==0 && about.y==1 && about.z==0) {
-        r[0] = c;   r[10]= c;
-        r[8] = s;   r[2] = -s;
-    } else if (about.x==0 && about.y==0 && about.z==1) {
-        r[0] = c;   r[5] = c;
-        r[1] = s;   r[4] = -s;
-    } else {
-        tvec3<B> nabout = normalize(about);
-        B x = nabout.x;
-        B y = nabout.y;
-        B z = nabout.z;
-        T nc = 1 - c;
-        T xy = x * y;
-        T yz = y * z;
-        T zx = z * x;
-        T xs = x * s;
-        T ys = y * s;
-        T zs = z * s;
-        r[ 0] = x*x*nc +  c;    r[ 4] =  xy*nc - zs;    r[ 8] =  zx*nc + ys;
-        r[ 1] =  xy*nc + zs;    r[ 5] = y*y*nc +  c;    r[ 9] =  yz*nc - xs;
-        r[ 2] =  zx*nc - ys;    r[ 6] =  yz*nc + xs;    r[10] = z*z*nc +  c;
-    }
-    return rotation;
-}
-
-// ----------------------------------------------------------------------------------------
-// Arithmetic operators outside of class
-// ----------------------------------------------------------------------------------------
-
-/* We use non-friend functions here to prevent the compiler from using
- * implicit conversions, for instance of a scalar to a vector. The result would
- * not be what the caller expects.
- *
- * Also note that the order of the arguments in the inner loop is important since
- * it determines the output type (only relevant when T != U).
- */
-
-// matrix * vector, result is a vector of the same type than the input vector
-template <typename T, typename U>
-typename tmat44<U>::col_type PURE operator *(const tmat44<T>& lv, const tvec4<U>& rv) {
-    typename tmat44<U>::col_type result;
-    for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
-        result += rv[r]*lv[r];
-    return result;
-}
-
-// vector * matrix, result is a vector of the same type than the input vector
-template <typename T, typename U>
-typename tmat44<U>::row_type PURE operator *(const tvec4<U>& rv, const tmat44<T>& lv) {
-    typename tmat44<U>::row_type result(tmat44<U>::row_type::NO_INIT);
-    for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
-        result[r] = dot(rv, lv[r]);
-    return result;
-}
-
-// matrix * scalar, result is a matrix of the same type than the input matrix
-template <typename T, typename U>
-tmat44<T> PURE operator *(const tmat44<T>& lv, U rv) {
-    tmat44<T> result(tmat44<T>::NO_INIT);
-    for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
-        result[r] = lv[r]*rv;
-    return result;
-}
-
-// scalar * matrix, result is a matrix of the same type than the input matrix
-template <typename T, typename U>
-tmat44<T> PURE operator *(U rv, const tmat44<T>& lv) {
-    tmat44<T> result(tmat44<T>::NO_INIT);
-    for (size_t r=0 ; r<tmat44<T>::row_size() ; r++)
-        result[r] = lv[r]*rv;
-    return result;
-}
-
-// ----------------------------------------------------------------------------------------
-
-/* FIXME: this should go into TMatSquareFunctions<> but for some reason
- * BASE<T>::col_type is not accessible from there (???)
- */
-template<typename T>
-typename tmat44<T>::col_type PURE diag(const tmat44<T>& m) {
-    return matrix::diag(m);
-}
-
-// ----------------------------------------------------------------------------------------
-
-typedef tmat44<float> mat4;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#undef PURE
-
-#endif /* UI_MAT4_H */
diff --git a/include/ui/vec2.h b/include/ui/vec2.h
deleted file mode 100644
index c31d0e4..0000000
--- a/include/ui/vec2.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UI_VEC2_H
-#define UI_VEC2_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#define TVEC_IMPLEMENTATION
-#include <ui/TVecHelpers.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec2 :   public TVecProductOperators<tvec2, T>,
-                public TVecAddOperators<tvec2, T>,
-                public TVecUnaryOperators<tvec2, T>,
-                public TVecComparisonOperators<tvec2, T>,
-                public TVecFunctions<tvec2, T>
-{
-public:
-    enum no_init { NO_INIT };
-    typedef T value_type;
-    typedef T& reference;
-    typedef T const& const_reference;
-    typedef size_t size_type;
-
-    union {
-        struct { T x, y; };
-        struct { T s, t; };
-        struct { T r, g; };
-    };
-
-    enum { SIZE = 2 };
-    inline static size_type size() { return SIZE; }
-
-    // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
-
-    // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
-
-    // constructors
-
-    // leaves object uninitialized. use with caution.
-    explicit tvec2(no_init) { }
-
-    // default constructor
-    tvec2() : x(0), y(0) { }
-
-    // handles implicit conversion to a tvec4. must not be explicit.
-    template<typename A>
-    tvec2(A v) : x(v), y(v) { }
-
-    template<typename A, typename B>
-    tvec2(A x, B y) : x(x), y(y) { }
-
-    template<typename A>
-    explicit tvec2(const tvec2<A>& v) : x(v.x), y(v.y) { }
-
-    template<typename A>
-    tvec2(const Impersonator< tvec2<A> >& v)
-        : x(((const tvec2<A>&)v).x),
-          y(((const tvec2<A>&)v).y) { }
-};
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec2<float> vec2;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/ui/vec3.h b/include/ui/vec3.h
deleted file mode 100644
index dde59a9..0000000
--- a/include/ui/vec3.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UI_VEC3_H
-#define UI_VEC3_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec2.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec3 :   public TVecProductOperators<tvec3, T>,
-                public TVecAddOperators<tvec3, T>,
-                public TVecUnaryOperators<tvec3, T>,
-                public TVecComparisonOperators<tvec3, T>,
-                public TVecFunctions<tvec3, T>
-{
-public:
-    enum no_init { NO_INIT };
-    typedef T value_type;
-    typedef T& reference;
-    typedef T const& const_reference;
-    typedef size_t size_type;
-
-    union {
-        struct { T x, y, z; };
-        struct { T s, t, p; };
-        struct { T r, g, b; };
-        Impersonator< tvec2<T> > xy;
-        Impersonator< tvec2<T> > st;
-        Impersonator< tvec2<T> > rg;
-    };
-
-    enum { SIZE = 3 };
-    inline static size_type size() { return SIZE; }
-
-    // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
-
-    // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
-
-    // constructors
-    // leaves object uninitialized. use with caution.
-    explicit tvec3(no_init) { }
-
-    // default constructor
-    tvec3() : x(0), y(0), z(0) { }
-
-    // handles implicit conversion to a tvec4. must not be explicit.
-    template<typename A>
-    tvec3(A v) : x(v), y(v), z(v) { }
-
-    template<typename A, typename B, typename C>
-    tvec3(A x, B y, C z) : x(x), y(y), z(z) { }
-
-    template<typename A, typename B>
-    tvec3(const tvec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
-
-    template<typename A>
-    explicit tvec3(const tvec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
-
-    template<typename A>
-    tvec3(const Impersonator< tvec3<A> >& v)
-        : x(((const tvec3<A>&)v).x),
-          y(((const tvec3<A>&)v).y),
-          z(((const tvec3<A>&)v).z) { }
-
-    template<typename A, typename B>
-    tvec3(const Impersonator< tvec2<A> >& v, B z)
-        : x(((const tvec2<A>&)v).x),
-          y(((const tvec2<A>&)v).y),
-          z(z) { }
-
-    // cross product works only on vectors of size 3
-    template <typename RT>
-    friend inline
-    tvec3 __attribute__((pure)) cross(const tvec3& u, const tvec3<RT>& v) {
-        return tvec3(
-                u.y*v.z - u.z*v.y,
-                u.z*v.x - u.x*v.z,
-                u.x*v.y - u.y*v.x);
-    }
-};
-
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec3<float> vec3;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/ui/vec4.h b/include/ui/vec4.h
deleted file mode 100644
index e03d331..0000000
--- a/include/ui/vec4.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef UI_VEC4_H
-#define UI_VEC4_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/vec3.h>
-
-namespace android {
-// -------------------------------------------------------------------------------------
-
-template <typename T>
-class tvec4 :   public TVecProductOperators<tvec4, T>,
-                public TVecAddOperators<tvec4, T>,
-                public TVecUnaryOperators<tvec4, T>,
-                public TVecComparisonOperators<tvec4, T>,
-                public TVecFunctions<tvec4, T>
-{
-public:
-    enum no_init { NO_INIT };
-    typedef T value_type;
-    typedef T& reference;
-    typedef T const& const_reference;
-    typedef size_t size_type;
-
-    union {
-        struct { T x, y, z, w; };
-        struct { T s, t, p, q; };
-        struct { T r, g, b, a; };
-        Impersonator< tvec2<T> > xy;
-        Impersonator< tvec2<T> > st;
-        Impersonator< tvec2<T> > rg;
-        Impersonator< tvec3<T> > xyz;
-        Impersonator< tvec3<T> > stp;
-        Impersonator< tvec3<T> > rgb;
-    };
-
-    enum { SIZE = 4 };
-    inline static size_type size() { return SIZE; }
-
-    // array access
-    inline T const& operator [] (size_t i) const { return (&x)[i]; }
-    inline T&       operator [] (size_t i)       { return (&x)[i]; }
-
-    // -----------------------------------------------------------------------
-    // we don't provide copy-ctor and operator= on purpose
-    // because we want the compiler generated versions
-
-    // constructors
-
-    // leaves object uninitialized. use with caution.
-    explicit tvec4(no_init) { }
-
-    // default constructor
-    tvec4() : x(0), y(0), z(0), w(0) { }
-
-    // handles implicit conversion to a tvec4. must not be explicit.
-    template<typename A>
-    tvec4(A v) : x(v), y(v), z(v), w(v) { }
-
-    template<typename A, typename B, typename C, typename D>
-    tvec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
-
-    template<typename A, typename B, typename C>
-    tvec4(const tvec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
-
-    template<typename A, typename B>
-    tvec4(const tvec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
-
-    template<typename A>
-    explicit tvec4(const tvec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
-
-    template<typename A>
-    tvec4(const Impersonator< tvec4<A> >& v)
-        : x(((const tvec4<A>&)v).x),
-          y(((const tvec4<A>&)v).y),
-          z(((const tvec4<A>&)v).z),
-          w(((const tvec4<A>&)v).w) { }
-
-    template<typename A, typename B>
-    tvec4(const Impersonator< tvec3<A> >& v, B w)
-        : x(((const tvec3<A>&)v).x),
-          y(((const tvec3<A>&)v).y),
-          z(((const tvec3<A>&)v).z),
-          w(w) { }
-
-    template<typename A, typename B, typename C>
-    tvec4(const Impersonator< tvec2<A> >& v, B z, C w)
-        : x(((const tvec2<A>&)v).x),
-          y(((const tvec2<A>&)v).y),
-          z(z),
-          w(w) { }
-};
-
-// ----------------------------------------------------------------------------------------
-
-typedef tvec4<float> vec4;
-
-// ----------------------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* UI_VEC4_H */
diff --git a/include/vr/vr_manager/vr_manager.h b/include/vr/vr_manager/vr_manager.h
new file mode 100644
index 0000000..0c5da19
--- /dev/null
+++ b/include/vr/vr_manager/vr_manager.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_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,
+};
+
+};  // namespace android
+
+#endif // ANDROID_VR_MANAGER_H
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
new file mode 100644
index 0000000..0d25176
--- /dev/null
+++ b/libs/arect/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+ndk_headers {
+    name: "libarect_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/*.h"],
+    license: "NOTICE",
+}
+
+cc_library_static {
+    name: "libarect",
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
diff --git a/libs/arect/MODULE_LICENSE_APACHE2 b/libs/arect/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/arect/MODULE_LICENSE_APACHE2
diff --git a/libs/arect/NOTICE b/libs/arect/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/arect/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/include/android/rect.h b/libs/arect/include/android/rect.h
similarity index 100%
rename from include/android/rect.h
rename to libs/arect/include/android/rect.h
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 4780757..2cb44eb 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,8 @@
         "IProcessInfoService.cpp",
         "IResultReceiver.cpp",
         "IServiceManager.cpp",
+        "IShellCallback.cpp",
+        "HalToken.cpp",
         "MemoryBase.cpp",
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
@@ -43,6 +46,8 @@
         "Static.cpp",
         "Status.cpp",
         "TextOutput.cpp",
+        "IpPrefix.cpp",
+        "Value.cpp",
     ],
 
     cflags: [
@@ -61,6 +66,8 @@
         "liblog",
         "libcutils",
         "libutils",
+        "libhidlbase",
+        "android.hidl.token@1.0",
     ],
     export_shared_lib_headers: [
         "libbase",
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/HalToken.cpp b/libs/binder/HalToken.cpp
new file mode 100644
index 0000000..6e71a52
--- /dev/null
+++ b/libs/binder/HalToken.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "HalToken"
+
+#include <utils/Log.h>
+#include <binder/HalToken.h>
+
+#include <android/hidl/token/1.0/ITokenManager.h>
+
+namespace android {
+
+using ::android::hidl::token::V1_0::ITokenManager;
+
+sp<ITokenManager> gTokenManager = nullptr;
+
+ITokenManager* getTokenManager() {
+    if (gTokenManager != nullptr) {
+        return gTokenManager.get();
+    }
+    gTokenManager = ITokenManager::getService();
+    if (gTokenManager == nullptr) {
+        ALOGE("Cannot retrieve TokenManager.");
+    }
+    return gTokenManager.get();
+}
+
+sp<HInterface> retrieveHalInterface(const HalToken& token) {
+    auto transaction = getTokenManager()->get(token);
+    if (!transaction.isOk()) {
+        ALOGE("getHalInterface: Cannot obtain interface from token.");
+        return nullptr;
+    }
+    return static_cast<sp<HInterface> >(transaction);
+}
+
+bool createHalToken(const sp<HInterface>& interface, HalToken* token) {
+    auto transaction = getTokenManager()->createToken(interface);
+    if (!transaction.isOk()) {
+        ALOGE("createHalToken: Cannot create token from interface.");
+        return false;
+    }
+    *token = static_cast<HalToken>(transaction);
+    return true;
+}
+
+bool deleteHalToken(const HalToken& token) {
+    auto transaction = getTokenManager()->unregister(token);
+    if (!transaction.isOk()) {
+        ALOGE("deleteHalToken: Cannot unregister hal token.");
+        return false;
+    }
+    return true;
+}
+
+}; // namespace android
+
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
new file mode 100644
index 0000000..50a8b28
--- /dev/null
+++ b/libs/binder/IActivityManager.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <binder/IActivityManager.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+// ------------------------------------------------------------------------------------
+
+class BpActivityManager : public BpInterface<IActivityManager>
+{
+public:
+    explicit BpActivityManager(const sp<IBinder>& impl)
+        : BpInterface<IActivityManager>(impl)
+    {
+    }
+
+    virtual int openContentUri(const String16& stringUri)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+        data.writeString16(stringUri);
+        status_t ret = remote()->transact(OPEN_CONTENT_URI_TRANSACTION, data, & reply);
+        int fd = -1;
+        if (ret == NO_ERROR) {
+            int32_t exceptionCode = reply.readExceptionCode();
+            if (!exceptionCode) {
+                // Success is indicated here by a nonzero int followed by the fd;
+                // failure by a zero int with no data following.
+                if (reply.readInt32() != 0) {
+                    fd = fcntl(reply.readParcelFileDescriptor(), F_DUPFD_CLOEXEC, 0);
+                }
+            } else {
+                // An exception was thrown back; fall through to return failure
+                ALOGD("openContentUri(%s) caught exception %d\n",
+                        String8(stringUri).string(), exceptionCode);
+            }
+        }
+        return fd;
+    }
+};
+
+// ------------------------------------------------------------------------------------
+
+IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager");
+
+}; // namespace android
diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp
index 4800f5b..77e3d23 100644
--- a/libs/binder/IMediaResourceMonitor.cpp
+++ b/libs/binder/IMediaResourceMonitor.cpp
@@ -25,7 +25,7 @@
 
 class BpMediaResourceMonitor : public BpInterface<IMediaResourceMonitor> {
 public:
-    BpMediaResourceMonitor(const sp<IBinder>& impl)
+    explicit BpMediaResourceMonitor(const sp<IBinder>& impl)
         : BpInterface<IMediaResourceMonitor>(impl) {}
 
     virtual void notifyResourceGranted(/*in*/ int32_t pid, /*in*/ const int32_t type)
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 6b5b1af..5c1a4f4 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "IMemory"
 
 #include <atomic>
+#include <stdatomic.h>
+
 #include <fcntl.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -28,6 +30,7 @@
 #include <binder/IMemory.h>
 #include <binder/Parcel.h>
 #include <log/log.h>
+
 #include <utils/CallStack.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
@@ -287,7 +290,7 @@
                 mBase   = heap->mBase;
                 mSize   = heap->mSize;
                 mOffset = heap->mOffset;
-                int fd = dup(heap->mHeapId.load(memory_order_relaxed));
+                int fd = fcntl(heap->mHeapId.load(memory_order_relaxed), F_DUPFD_CLOEXEC, 0);
                 ALOGE_IF(fd==-1, "cannot dup fd=%d",
                         heap->mHeapId.load(memory_order_relaxed));
                 mHeapId.store(fd, memory_order_release);
@@ -322,7 +325,7 @@
 
         Mutex::Autolock _l(mLock);
         if (mHeapId.load(memory_order_relaxed) == -1) {
-            int fd = dup( parcel_fd );
+            int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0);
             ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)",
                     parcel_fd, size, err, strerror(errno));
 
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp
index 2a22b69..646809e 100644
--- a/libs/binder/IResultReceiver.cpp
+++ b/libs/binder/IResultReceiver.cpp
@@ -31,7 +31,7 @@
 class BpResultReceiver : public BpInterface<IResultReceiver>
 {
 public:
-    BpResultReceiver(const sp<IBinder>& impl)
+    explicit BpResultReceiver(const sp<IBinder>& impl)
         : BpInterface<IResultReceiver>(impl)
     {
     }
diff --git a/libs/binder/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/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp
index fb28643..8939d9c 100644
--- a/libs/binder/ProcessInfoService.cpp
+++ b/libs/binder/ProcessInfoService.cpp
@@ -57,6 +57,40 @@
     return TIMED_OUT;
 }
 
+status_t ProcessInfoService::getProcessStatesScoresImpl(size_t length,
+        /*in*/ int32_t* pids, /*out*/ int32_t* states,
+        /*out*/ int32_t *scores) {
+    status_t err = NO_ERROR;
+    sp<IProcessInfoService> pis;
+    mProcessInfoLock.lock();
+    pis = mProcessInfoService;
+    mProcessInfoLock.unlock();
+
+    for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) {
+
+        if (pis != NULL) {
+            err = pis->getProcessStatesAndOomScoresFromPids(length,
+                    /*in*/ pids, /*out*/ states, /*out*/ scores);
+            if (err == NO_ERROR) return NO_ERROR; // success
+            if (IInterface::asBinder(pis)->isBinderAlive()) return err;
+        }
+        sleep(1);
+
+        mProcessInfoLock.lock();
+        if (pis == mProcessInfoService) {
+            updateBinderLocked();
+        }
+        pis = mProcessInfoService;
+        mProcessInfoLock.unlock();
+    }
+
+    ALOGW("%s: Could not retrieve process states and scores "
+            "from ProcessInfoService after %d retries.", __FUNCTION__,
+            BINDER_ATTEMPT_LIMIT);
+
+    return TIMED_OUT;
+}
+
 void ProcessInfoService::updateBinderLocked() {
     const sp<IServiceManager> sm(defaultServiceManager());
     if (sm != NULL) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 5b70501..98107c5 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -319,7 +319,8 @@
             fd = -1;
         }
         if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
-            ALOGE("Binder driver protocol does not match user space protocol!");
+          ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
+                vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
             close(fd);
             fd = -1;
         }
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..a1b4abc 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -38,9 +38,8 @@
         // Don't warn about struct padding
         "-Wno-padded",
 
-        // android/sensors.h uses nested anonymous unions and anonymous structs
-        "-Wno-nested-anon-types",
-        "-Wno-gnu-anonymous-struct",
+        // We are aware of the risks inherent in comparing floats for equality
+        "-Wno-float-equal",
 
         "-DDEBUG_ONLY_CODE=0",
     ],
@@ -58,8 +57,6 @@
     },
 
     srcs: [
-        "IGraphicBufferConsumer.cpp",
-        "IConsumerListener.cpp",
         "BitTube.cpp",
         "BufferItem.cpp",
         "BufferItemConsumer.cpp",
@@ -71,43 +68,41 @@
         "ConsumerBase.cpp",
         "CpuConsumer.cpp",
         "DisplayEventReceiver.cpp",
+        "FrameTimestamps.cpp",
         "GLConsumer.cpp",
         "GraphicBufferAlloc.cpp",
-        "GraphicsEnv.cpp",
         "GuiConfig.cpp",
         "IDisplayEventConnection.cpp",
+        "IConsumerListener.cpp",
         "IGraphicBufferAlloc.cpp",
+        "IGraphicBufferConsumer.cpp",
         "IGraphicBufferProducer.cpp",
         "IProducerListener.cpp",
-        "ISensorEventConnection.cpp",
-        "ISensorServer.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
         "LayerState.cpp",
         "OccupancyTracker.cpp",
-        "Sensor.cpp",
-        "SensorEventQueue.cpp",
-        "SensorManager.cpp",
         "StreamSplitter.cpp",
         "Surface.cpp",
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
+        "view/Surface.cpp",
     ],
 
     shared_libs: [
-        "libnativeloader",
+        "libsync",
         "libbinder",
         "libcutils",
         "libEGL",
         "libGLESv2",
-        "libsync",
         "libui",
         "libutils",
+        "libnativewindow",
         "liblog",
     ],
 
-    export_shared_lib_headers: ["libbinder"],
+    export_shared_lib_headers: ["libbinder", "libui"],
 }
 
 subdirs = ["tests"]
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
index b653c5b..51a8d67 100644
--- a/libs/gui/BitTube.cpp
+++ b/libs/gui/BitTube.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <private/gui/BitTube.h>
+
 #include <stdint.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -25,7 +27,6 @@
 
 #include <binder/Parcel.h>
 
-#include <gui/BitTube.h>
 
 namespace android {
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 1357a4a..69b5962 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -81,6 +81,9 @@
     addAligned(size, mIsDroppable);
     addAligned(size, mAcquireCalled);
     addAligned(size, mTransformToDisplayInverse);
+    addAligned(size, mAutoRefresh);
+    addAligned(size, mQueuedBuffer);
+    addAligned(size, mIsStale);
     return size;
 }
 
@@ -88,11 +91,11 @@
     size_t size = sizeof(uint32_t); // Flags
     if (mGraphicBuffer != 0) {
         size += mGraphicBuffer->getFlattenedSize();
-        FlattenableUtils::align<4>(size);
+        size = FlattenableUtils::align<4>(size);
     }
     if (mFence != 0) {
         size += mFence->getFlattenedSize();
-        FlattenableUtils::align<4>(size);
+        size = FlattenableUtils::align<4>(size);
     }
     size += mSurfaceDamage.getFlattenedSize();
     size = FlattenableUtils::align<8>(size);
@@ -166,6 +169,9 @@
     writeAligned(buffer, size, mIsDroppable);
     writeAligned(buffer, size, mAcquireCalled);
     writeAligned(buffer, size, mTransformToDisplayInverse);
+    writeAligned(buffer, size, mAutoRefresh);
+    writeAligned(buffer, size, mQueuedBuffer);
+    writeAligned(buffer, size, mIsStale);
 
     return NO_ERROR;
 }
@@ -198,6 +204,8 @@
         status_t err = mFence->unflatten(buffer, size, fds, count);
         if (err) return err;
         size -= FlattenableUtils::align<4>(buffer);
+
+        mFenceTime = std::make_shared<FenceTime>(mFence);
     }
 
     status_t err = mSurfaceDamage.unflatten(buffer, size);
@@ -227,6 +235,9 @@
     readAligned(buffer, size, mIsDroppable);
     readAligned(buffer, size, mAcquireCalled);
     readAligned(buffer, size, mTransformToDisplayInverse);
+    readAligned(buffer, size, mAutoRefresh);
+    readAligned(buffer, size, mQueuedBuffer);
+    readAligned(buffer, size, mIsStale);
 
     return NO_ERROR;
 }
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 6de98f5..13692eb 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -31,6 +31,13 @@
 
 BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
 
+void BufferQueue::ProxyConsumerListener::onDisconnect() {
+    sp<ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != NULL) {
+        listener->onDisconnect();
+    }
+}
+
 void BufferQueue::ProxyConsumerListener::onFrameAvailable(
         const BufferItem& item) {
     sp<ConsumerListener> listener(mConsumerListener.promote());
@@ -61,18 +68,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 +90,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/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 6e6cce2..9e3fecb 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -29,6 +29,7 @@
 #include <inttypes.h>
 
 #include <cutils/properties.h>
+#include <cutils/atomic.h>
 
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueCore.h>
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 13b900c..27ced61 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
@@ -1174,6 +1222,9 @@
         }
 
         if (api == BufferQueueCore::CURRENTLY_CONNECTED_API) {
+            if (mCore->mConnectedApi == NATIVE_WINDOW_API_MEDIA) {
+                ALOGD("About to force-disconnect API_MEDIA, mode=%d", mode);
+            }
             api = mCore->mConnectedApi;
             // If we're asked to disconnect the currently connected api but
             // nobody is connected, it's not really an error.
@@ -1224,6 +1275,7 @@
     // Call back without lock held
     if (listener != NULL) {
         listener->onBuffersReleased();
+        listener->onDisconnect();
     }
 
     return status;
@@ -1279,8 +1331,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 +1484,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..8acdfed 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -27,6 +27,8 @@
 
 #include <hardware/hardware.h>
 
+#include <cutils/atomic.h>
+
 #include <gui/BufferItem.h>
 #include <gui/IGraphicBufferAlloc.h>
 #include <gui/ISurfaceComposer.h>
@@ -56,7 +58,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());
 
@@ -317,16 +320,16 @@
         return OK;
     }
 
-    auto signaled = mSlots[slot].mFence->hasSignaled();
+    auto status = mSlots[slot].mFence->getStatus();
 
-    if (!signaled) {
+    if (status == Fence::Status::Invalid) {
         CB_LOGE("fence has invalid state");
         return BAD_VALUE;
     }
 
-    if (*signaled) {
+    if (status == Fence::Status::Signaled) {
         mSlots[slot].mFence = fence;
-    } else {
+    } else {  // status == Fence::Status::Unsignaled
         char fenceName[32] = {};
         snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
         sp<Fence> mergedFence = Fence::merge(
@@ -366,6 +369,7 @@
         freeBufferLocked(slot);
     }
 
+    mPrevFinalReleaseFence = mSlots[slot].mFence;
     mSlots[slot].mFence = Fence::NO_FENCE;
 
     return err;
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 8393160..ae7c65c 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -64,6 +64,8 @@
     switch (static_cast<int>(format)) {
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBA_1010102:
         case HAL_PIXEL_FORMAT_RGB_888:
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_BGRA_8888:
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 9973e8d..07e07e0 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -18,13 +18,14 @@
 
 #include <utils/Errors.h>
 
-#include <gui/BitTube.h>
 #include <gui/DisplayEventReceiver.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/ISurfaceComposer.h>
 
 #include <private/gui/ComposerService.h>
 
+#include <private/gui/BitTube.h>
+
 // ---------------------------------------------------------------------------
 
 namespace android {
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000..019a11e
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,735 @@
+/*
+* Copyright 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <gui/FrameTimestamps.h>
+
+#define LOG_TAG "FrameEvents"
+
+#include <cutils/compiler.h>  // For CC_[UN]LIKELY
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <algorithm>
+#include <limits>
+#include <numeric>
+
+namespace android {
+
+
+// ============================================================================
+// FrameEvents
+// ============================================================================
+
+bool FrameEvents::hasPostedInfo() const {
+    return FrameEvents::isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+    return FrameEvents::isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+    return FrameEvents::isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+    return FrameEvents::isValidTimestamp(firstRefreshStartTime);
+}
+
+bool FrameEvents::hasLastRefreshStartInfo() const {
+    // The last refresh start time may continue to update until a new frame
+    // is latched. We know we have the final value once the release or retire
+    // info is set. See ConsumerFrameEventHistory::addRetire/Release.
+    return addRetireCalled || addReleaseCalled;
+}
+
+bool FrameEvents::hasDequeueReadyInfo() const {
+    return FrameEvents::isValidTimestamp(dequeueReadyTime);
+}
+
+bool FrameEvents::hasAcquireInfo() const {
+    return acquireFence->isValid();
+}
+
+bool FrameEvents::hasGpuCompositionDoneInfo() const {
+    // We may not get a gpuCompositionDone in addPostComposite if
+    // client/gles compositing isn't needed.
+    return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayPresentInfo() const {
+    // We may not get a displayPresent in addPostComposite for HWC1.
+    return addPostCompositeCalled;
+}
+
+bool FrameEvents::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 (FrameEvents::isValidTimestamp(latchTime)) {
+        outString.appendFormat("%" PRId64 "\n", latchTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    outString.appendFormat("--- Refresh (First)\t");
+    if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) {
+        outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    outString.appendFormat("--- Refresh (Last)\t");
+    if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) {
+        outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    dumpFenceTime(outString, "Acquire           \t",
+            true, *acquireFence);
+    dumpFenceTime(outString, "GPU Composite Done\t",
+            !addPostCompositeCalled, *gpuCompositionDoneFence);
+    dumpFenceTime(outString, "Display Present   \t",
+            !addPostCompositeCalled, *displayPresentFence);
+    dumpFenceTime(outString, "Display Retire    \t",
+            !addRetireCalled, *displayRetireFence);
+
+    outString.appendFormat("--- DequeueReady  \t");
+    if (FrameEvents::isValidTimestamp(dequeueReadyTime)) {
+        outString.appendFormat("%" PRId64 "\n", dequeueReadyTime);
+    } else {
+        outString.appendFormat("Pending\n");
+    }
+
+    dumpFenceTime(outString, "Release           \t",
+            true, *releaseFence);
+}
+
+
+// ============================================================================
+// FrameEventHistory
+// ============================================================================
+
+namespace {
+
+struct FrameNumberEqual {
+    FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {}
+    bool operator()(const FrameEvents& frame) {
+        return frame.valid && mFrameNumber == frame.frameNumber;
+    }
+    const uint64_t mFrameNumber;
+};
+
+}  // namespace
+
+FrameEventHistory::~FrameEventHistory() = default;
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
+    auto frame = std::find_if(
+            mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber));
+    return frame == mFrames.end() ? nullptr : &(*frame);
+}
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) {
+    *iHint = std::min(*iHint, mFrames.size());
+    auto hint = mFrames.begin() + *iHint;
+    auto frame = std::find_if(
+            hint, mFrames.end(), FrameNumberEqual(frameNumber));
+    if (frame == mFrames.end()) {
+        frame = std::find_if(
+                mFrames.begin(), hint, FrameNumberEqual(frameNumber));
+        if (frame == hint) {
+            return nullptr;
+        }
+    }
+    *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+    return &(*frame);
+}
+
+void FrameEventHistory::checkFencesForCompletion() {
+    for (auto& frame : mFrames) {
+        frame.checkFencesForCompletion();
+    }
+}
+
+// Uses !|valid| as the MSB.
+static bool FrameNumberLessThan(
+        const FrameEvents& lhs, const FrameEvents& rhs) {
+    if (lhs.valid == rhs.valid) {
+        return lhs.frameNumber < rhs.frameNumber;
+    }
+    return lhs.valid;
+}
+
+void FrameEventHistory::dump(String8& outString) const {
+    auto earliestFrame = std::min_element(
+            mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+    if (!earliestFrame->valid) {
+        outString.appendFormat("-- N/A\n");
+        return;
+    }
+    for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+        frame->dump(outString);
+    }
+    for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+        frame->dump(outString);
+    }
+}
+
+
+// ============================================================================
+// ProducerFrameEventHistory
+// ============================================================================
+
+ProducerFrameEventHistory::~ProducerFrameEventHistory() = default;
+
+nsecs_t ProducerFrameEventHistory::snapToNextTick(
+        nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) {
+    nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval;
+    // Integer modulo rounds towards 0 and not -inf before taking the remainder,
+    // so adjust the offset if it is negative.
+    if (tickOffset < 0) {
+        tickOffset += tickInterval;
+    }
+    return timestamp + tickOffset;
+}
+
+nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline(
+        const nsecs_t now) const{
+    return snapToNextTick(
+            now, mCompositorTiming.deadline, mCompositorTiming.interval);
+}
+
+void ProducerFrameEventHistory::updateAcquireFence(
+        uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) {
+    FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
+    if (frame == nullptr) {
+        ALOGE("updateAcquireFence: Did not find frame.");
+        return;
+    }
+
+    if (acquire->isValid()) {
+        mAcquireTimeline.push(acquire);
+        frame->acquireFence = std::move(acquire);
+    } else {
+        // If there isn't an acquire fence, assume that buffer was
+        // ready for the consumer when posted.
+        frame->acquireFence = std::make_shared<FenceTime>(frame->postedTime);
+    }
+}
+
+void ProducerFrameEventHistory::applyDelta(
+        const FrameEventHistoryDelta& delta) {
+    mCompositorTiming = delta.mCompositorTiming;
+
+    for (auto& d : delta.mDeltas) {
+        // Avoid out-of-bounds access.
+        if (CC_UNLIKELY(d.mIndex >= mFrames.size())) {
+            ALOGE("applyDelta: Bad index.");
+            return;
+        }
+
+        FrameEvents& frame = mFrames[d.mIndex];
+
+        frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
+        frame.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 || dst->get() == nullptr)) {
+        ALOGE("applyFenceDelta: dst is null.");
+        return;
+    }
+
+    switch (src.state) {
+        case FenceTime::Snapshot::State::EMPTY:
+            return;
+        case FenceTime::Snapshot::State::FENCE:
+            ALOGE_IF((*dst)->isValid(), "applyFenceDelta: Unexpected fence.");
+            *dst = createFenceTime(src.fence);
+            timeline->push(*dst);
+            return;
+        case FenceTime::Snapshot::State::SIGNAL_TIME:
+            if ((*dst)->isValid()) {
+                (*dst)->applyTrustedSnapshot(src);
+            } else {
+                *dst = std::make_shared<FenceTime>(src.signalTime);
+            }
+            return;
+    }
+}
+
+std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime(
+        const sp<Fence>& fence) const {
+    return std::make_shared<FenceTime>(fence);
+}
+
+
+// ============================================================================
+// ConsumerFrameEventHistory
+// ============================================================================
+
+ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
+
+void ConsumerFrameEventHistory::onDisconnect() {
+    mCurrentConnectId++;
+    mProducerWantsEvents = false;
+}
+
+void ConsumerFrameEventHistory::initializeCompositorTiming(
+        const CompositorTiming& compositorTiming) {
+    mCompositorTiming = compositorTiming;
+}
+
+void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) {
+    // Overwrite all fields of the frame with default values unless set here.
+    FrameEvents newTimestamps;
+    newTimestamps.connectId = mCurrentConnectId;
+    newTimestamps.frameNumber = newEntry.frameNumber;
+    newTimestamps.postedTime = newEntry.postedTime;
+    newTimestamps.requestedPresentTime = newEntry.requestedPresentTime;
+    newTimestamps.acquireFence = newEntry.acquireFence;
+    newTimestamps.valid = true;
+    mFrames[mQueueOffset] = newTimestamps;
+
+    // Note: We avoid sending the acquire fence back to the caller since
+    // they have the original one already, so there is no need to set the
+    // acquire dirty bit.
+    mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>();
+
+    mQueueOffset = (mQueueOffset + 1) % mFrames.size();
+}
+
+void ConsumerFrameEventHistory::addLatch(
+        uint64_t frameNumber, nsecs_t latchTime) {
+    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+    if (frame == nullptr) {
+        ALOGE_IF(mProducerWantsEvents, "addLatch: Did not find frame.");
+        return;
+    }
+    frame->latchTime = latchTime;
+    mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>();
+}
+
+void ConsumerFrameEventHistory::addPreComposition(
+        uint64_t frameNumber, nsecs_t refreshStartTime) {
+    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+    if (frame == nullptr) {
+        ALOGE_IF(mProducerWantsEvents,
+                "addPreComposition: Did not find frame.");
+        return;
+    }
+    frame->lastRefreshStartTime = refreshStartTime;
+    mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
+    if (!FrameEvents::isValidTimestamp(frame->firstRefreshStartTime)) {
+        frame->firstRefreshStartTime = refreshStartTime;
+        mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
+    }
+}
+
+void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
+        const std::shared_ptr<FenceTime>& gpuCompositionDone,
+        const std::shared_ptr<FenceTime>& displayPresent,
+        const CompositorTiming& compositorTiming) {
+    mCompositorTiming = compositorTiming;
+
+    FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+    if (frame == nullptr) {
+        ALOGE_IF(mProducerWantsEvents,
+                "addPostComposition: Did not find frame.");
+        return;
+    }
+    // Only get GPU and present info for the first composite.
+    if (!frame->addPostCompositeCalled) {
+        frame->addPostCompositeCalled = true;
+        frame->gpuCompositionDoneFence = gpuCompositionDone;
+        mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GPU_COMPOSITION_DONE>();
+        if (!frame->displayPresentFence->isValid()) {
+            frame->displayPresentFence = displayPresent;
+            mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
+        }
+    }
+}
+
+void ConsumerFrameEventHistory::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_IF(mProducerWantsEvents, "addRelease: Did not find frame.");
+        return;
+    }
+    frame->addReleaseCalled = true;
+    frame->dequeueReadyTime = dequeueReadyTime;
+    frame->releaseFence = std::move(release);
+    mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
+}
+
+void ConsumerFrameEventHistory::getFrameDelta(
+        FrameEventHistoryDelta* delta,
+        const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) {
+    mProducerWantsEvents = true;
+    size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+    if (mFramesDirty[i].anyDirty()) {
+        // Make sure only to send back deltas for the current connection
+        // since the producer won't have the correct state to apply a delta
+        // from a previous connection.
+        if (mFrames[i].connectId == mCurrentConnectId) {
+            delta->mDeltas.emplace_back(i, *frame, mFramesDirty[i]);
+        }
+        mFramesDirty[i].reset();
+    }
+}
+
+void ConsumerFrameEventHistory::getAndResetDelta(
+        FrameEventHistoryDelta* delta) {
+    mProducerWantsEvents = true;
+    delta->mCompositorTiming = mCompositorTiming;
+
+    // Write these in order of frame number so that it is easy to
+    // add them to a FenceTimeline in the proper order producer side.
+    delta->mDeltas.reserve(mFramesDirty.size());
+    auto earliestFrame = std::min_element(
+            mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+    for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+        getFrameDelta(delta, frame);
+    }
+    for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+        getFrameDelta(delta, frame);
+    }
+}
+
+
+// ============================================================================
+// FrameEventsDelta
+// ============================================================================
+
+FrameEventsDelta::FrameEventsDelta(
+        size_t index,
+        const FrameEvents& frameTimestamps,
+        const FrameEventDirtyFields& dirtyFields)
+    : mIndex(index),
+      mFrameNumber(frameTimestamps.frameNumber),
+      mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled),
+      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::GPU_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();
+    }
+}
+
+constexpr size_t FrameEventsDelta::minFlattenedSize() {
+    return 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);
+}
+
+// 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) {
+    mCompositorTiming = src.mCompositorTiming;
+
+    if (CC_UNLIKELY(!mDeltas.empty())) {
+        ALOGE("FrameEventHistoryDelta assign clobbering history.");
+    }
+    mDeltas = std::move(src.mDeltas);
+    ALOGE_IF(src.mDeltas.empty(), "Source mDeltas not empty.");
+    return *this;
+}
+
+constexpr size_t FrameEventHistoryDelta::minFlattenedSize() {
+    return sizeof(uint32_t) + // mDeltas.size()
+            sizeof(mCompositorTiming);
+}
+
+size_t FrameEventHistoryDelta::getFlattenedSize() const {
+    return minFlattenedSize() +
+            std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+                    [](size_t a, const FrameEventsDelta& delta) {
+                            return a + delta.getFlattenedSize();
+                    });
+}
+
+size_t FrameEventHistoryDelta::getFdCount() const {
+    return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+            [](size_t a, const FrameEventsDelta& delta) {
+                    return a + delta.getFdCount();
+            });
+}
+
+status_t FrameEventHistoryDelta::flatten(
+            void*& buffer, size_t& size, int*& fds, size_t& count) const {
+    if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, mCompositorTiming);
+
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint32_t>(mDeltas.size()));
+    for (auto& d : mDeltas) {
+        status_t status = d.flatten(buffer, size, fds, count);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t FrameEventHistoryDelta::unflatten(
+            void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, mCompositorTiming);
+
+    uint32_t deltaCount = 0;
+    FlattenableUtils::read(buffer, size, deltaCount);
+    if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    mDeltas.resize(deltaCount);
+    for (auto& d : mDeltas) {
+        status_t status = d.unflatten(buffer, size, fds, count);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+
+} // namespace android
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 10e999c..55e0d26 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,8 @@
             switch (buf->getPixelFormat()) {
                 case PIXEL_FORMAT_RGBA_8888:
                 case PIXEL_FORMAT_RGBX_8888:
+                case PIXEL_FORMAT_RGBA_FP16:
+                case PIXEL_FORMAT_RGBA_1010102:
                 case PIXEL_FORMAT_RGB_888:
                 case PIXEL_FORMAT_RGB_565:
                 case PIXEL_FORMAT_BGRA_8888:
@@ -911,15 +919,26 @@
     return mCurrentTimestamp;
 }
 
+android_dataspace GLConsumer::getCurrentDataSpace() {
+    GLC_LOGV("getCurrentDataSpace");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentDataSpace;
+}
+
 uint64_t GLConsumer::getFrameNumber() {
     GLC_LOGV("getFrameNumber");
     Mutex::Autolock lock(mMutex);
     return mCurrentFrameNumber;
 }
 
-sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
+sp<GraphicBuffer> GLConsumer::getCurrentBuffer(int* outSlot) const {
     Mutex::Autolock lock(mMutex);
-    return (mCurrentTextureImage == NULL) ?
+
+    if (outSlot != nullptr) {
+        *outSlot = mCurrentTexture;
+    }
+
+    return (mCurrentTextureImage == nullptr) ?
             NULL : mCurrentTextureImage->graphicBuffer();
 }
 
@@ -981,6 +1000,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..cc7d403 100644
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ b/libs/gui/GraphicBufferAlloc.cpp
@@ -15,41 +15,35 @@
  ** limitations under the License.
  */
 
-#include <log/log.h>
-
-#include <ui/GraphicBuffer.h>
-
 #include <gui/GraphicBufferAlloc.h>
 
-// ----------------------------------------------------------------------------
+#include <log/log.h>
+
+
 namespace android {
-// ----------------------------------------------------------------------------
 
-GraphicBufferAlloc::GraphicBufferAlloc() {
-}
-
-GraphicBufferAlloc::~GraphicBufferAlloc() {
-}
+GraphicBufferAlloc::GraphicBufferAlloc() = default;
+GraphicBufferAlloc::~GraphicBufferAlloc() = default;
 
 sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,
-        uint32_t height, PixelFormat format, uint32_t usage,
+        uint32_t height, PixelFormat format, uint32_t layerCount,
+        uint64_t producerUsage, uint64_t consumerUsage,
         std::string requestorName, status_t* error) {
     sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
-            width, height, format, usage, std::move(requestorName)));
+            width, height, format, layerCount, producerUsage, consumerUsage,
+            std::move(requestorName)));
     status_t err = graphicBuffer->initCheck();
     *error = err;
     if (err != 0 || graphicBuffer->handle == 0) {
         if (err == NO_MEMORY) {
             GraphicBuffer::dumpAllocationsToSystemLog();
         }
-        ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
-             "failed (%s), handle=%p",
-                width, height, strerror(-err), graphicBuffer->handle);
-        return 0;
+        ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p",
+                width, height, layerCount, strerror(-err),
+                graphicBuffer->handle);
+        graphicBuffer.clear();
     }
     return graphicBuffer;
 }
 
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/gui/GraphicsEnv.cpp b/libs/gui/GraphicsEnv.cpp
deleted file mode 100644
index 68f0f98..0000000
--- a/libs/gui/GraphicsEnv.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 1
-#define LOG_TAG "GraphicsEnv"
-#include <gui/GraphicsEnv.h>
-
-#include <mutex>
-
-#include <log/log.h>
-#include <nativeloader/dlext_namespaces.h>
-
-namespace android {
-
-/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
-    static GraphicsEnv env;
-    return env;
-}
-
-void GraphicsEnv::setDriverPath(const std::string path) {
-    if (!mDriverPath.empty()) {
-        ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
-                mDriverPath.c_str(), path.c_str());
-        return;
-    }
-    ALOGV("setting driver path to '%s'", path.c_str());
-    mDriverPath = path;
-}
-
-android_namespace_t* GraphicsEnv::getDriverNamespace() {
-    static std::once_flag once;
-    std::call_once(once, [this]() {
-        // TODO; In the next version of Android, all graphics drivers will be
-        // loaded into a custom namespace. To minimize risk for this release,
-        // only updated drivers use a custom namespace.
-        //
-        // Additionally, the custom namespace will be
-        // ANDROID_NAMESPACE_TYPE_ISOLATED, and will only have access to a
-        // subset of the system.
-        if (mDriverPath.empty())
-            return;
-
-        char defaultPath[PATH_MAX];
-        android_get_LD_LIBRARY_PATH(defaultPath, sizeof(defaultPath));
-        size_t defaultPathLen = strlen(defaultPath);
-
-        std::string path;
-        path.reserve(mDriverPath.size() + 1 + defaultPathLen);
-        path.append(mDriverPath);
-        path.push_back(':');
-        path.append(defaultPath, defaultPathLen);
-
-        mDriverNamespace = android_create_namespace(
-                "gfx driver",
-                nullptr,                    // ld_library_path
-                path.c_str(),               // default_library_path
-                ANDROID_NAMESPACE_TYPE_SHARED,
-                nullptr,                    // permitted_when_isolated_path
-                nullptr);                   // parent
-    });
-    return mDriverNamespace;
-}
-
-} // namespace android
-
-extern "C" android_namespace_t* android_getDriverNamespace() {
-    return android::GraphicsEnv::getInstance().getDriverNamespace();
-}
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index ff7b83a..8cadc4d 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -28,7 +28,8 @@
 // ---------------------------------------------------------------------------
 
 enum {
-    ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION,
+    ON_DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
+    ON_FRAME_AVAILABLE,
     ON_BUFFER_RELEASED,
     ON_SIDEBAND_STREAM_CHANGED,
     GET_FRAME_TIMESTAMPS
@@ -43,6 +44,12 @@
 
     virtual ~BpConsumerListener();
 
+    virtual void onDisconnect() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
+        remote()->transact(ON_DISCONNECT, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
     virtual void onFrameAvailable(const BufferItem& item) {
         Parcel data, reply;
         data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
@@ -61,42 +68,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
@@ -111,6 +82,10 @@
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
     switch(code) {
+        case ON_DISCONNECT: {
+            CHECK_INTERFACE(IConsumerListener, data, reply);
+            onDisconnect();
+            return NO_ERROR; }
         case ON_FRAME_AVAILABLE: {
             CHECK_INTERFACE(IConsumerListener, data, reply);
             BufferItem item;
@@ -125,30 +100,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/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index b1d3b00..e5c3c48 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -15,17 +15,15 @@
  */
 
 #include <stdint.h>
-#include <sys/types.h>
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
-#include <utils/Timers.h>
 
 #include <binder/Parcel.h>
-#include <binder/IInterface.h>
 
 #include <gui/IDisplayEventConnection.h>
-#include <gui/BitTube.h>
+
+#include <private/gui/BitTube.h>
 
 namespace android {
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
index 2fb380c..21a0dd5 100644
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ b/libs/gui/IGraphicBufferAlloc.cpp
@@ -45,14 +45,17 @@
     virtual ~BpGraphicBufferAlloc();
 
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t usage,
+            uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t producerUsage, uint64_t consumerUsage,
             std::string requestorName, status_t* error) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor());
         data.writeUint32(width);
         data.writeUint32(height);
         data.writeInt32(static_cast<int32_t>(format));
-        data.writeUint32(usage);
+        data.writeUint32(layerCount);
+        data.writeUint64(producerUsage);
+        data.writeUint64(consumerUsage);
         if (requestorName.empty()) {
             requestorName += "[PID ";
             requestorName += std::to_string(getpid());
@@ -106,12 +109,15 @@
             uint32_t width = data.readUint32();
             uint32_t height = data.readUint32();
             PixelFormat format = static_cast<PixelFormat>(data.readInt32());
-            uint32_t usage = data.readUint32();
+            uint32_t layerCount = data.readUint32();
+            uint64_t producerUsage = data.readUint64();
+            uint64_t consumerUsage = data.readUint64();
             status_t error = NO_ERROR;
             std::string requestorName;
             data.readUtf8FromUtf16(&requestorName);
             sp<GraphicBuffer> result = createGraphicBuffer(width, height,
-                    format, usage, requestorName, &error);
+                    format, layerCount, producerUsage, consumerUsage,
+                    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
deleted file mode 100644
index 59ecee7..0000000
--- a/libs/gui/ISensorEventConnection.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
-#include <gui/ISensorEventConnection.h>
-#include <gui/BitTube.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-enum {
-    GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
-    ENABLE_DISABLE,
-    SET_EVENT_RATE,
-    FLUSH_SENSOR
-};
-
-class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
-{
-public:
-    explicit BpSensorEventConnection(const sp<IBinder>& impl)
-        : BpInterface<ISensorEventConnection>(impl)
-    {
-    }
-
-    virtual ~BpSensorEventConnection();
-
-    virtual sp<BitTube> getSensorChannel() const
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
-        remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
-        return new BitTube(reply);
-    }
-
-    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
-                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
-        data.writeInt32(handle);
-        data.writeInt32(enabled);
-        data.writeInt64(samplingPeriodNs);
-        data.writeInt64(maxBatchReportLatencyNs);
-        data.writeInt32(reservedFlags);
-        remote()->transact(ENABLE_DISABLE, data, &reply);
-        return reply.readInt32();
-    }
-
-    virtual status_t setEventRate(int handle, nsecs_t ns)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
-        data.writeInt32(handle);
-        data.writeInt64(ns);
-        remote()->transact(SET_EVENT_RATE, data, &reply);
-        return reply.readInt32();
-    }
-
-    virtual status_t flush() {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
-        remote()->transact(FLUSH_SENSOR, data, &reply);
-        return reply.readInt32();
-    }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpSensorEventConnection::~BpSensorEventConnection() {}
-
-IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection");
-
-// ----------------------------------------------------------------------------
-
-status_t BnSensorEventConnection::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code) {
-        case GET_SENSOR_CHANNEL: {
-            CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            sp<BitTube> channel(getSensorChannel());
-            channel->writeToParcel(reply);
-            return NO_ERROR;
-        }
-        case ENABLE_DISABLE: {
-            CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            int handle = data.readInt32();
-            int enabled = data.readInt32();
-            nsecs_t samplingPeriodNs = data.readInt64();
-            nsecs_t maxBatchReportLatencyNs = data.readInt64();
-            int reservedFlags = data.readInt32();
-            status_t result = enableDisable(handle, enabled, samplingPeriodNs,
-                                            maxBatchReportLatencyNs, reservedFlags);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case SET_EVENT_RATE: {
-            CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            int handle = data.readInt32();
-            nsecs_t ns = data.readInt64();
-            status_t result = setEventRate(handle, ns);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-        case FLUSH_SENSOR: {
-            CHECK_INTERFACE(ISensorEventConnection, data, reply);
-            status_t result = flush();
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
-    }
-    return BBinder::onTransact(code, data, reply, flags);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp
deleted file mode 100644
index 07c507a..0000000
--- a/libs/gui/ISensorServer.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
-
-#include <binder/Parcel.h>
-#include <binder/IInterface.h>
-
-#include <gui/Sensor.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-enum {
-    GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION,
-    CREATE_SENSOR_EVENT_CONNECTION,
-    ENABLE_DATA_INJECTION,
-    GET_DYNAMIC_SENSOR_LIST,
-};
-
-class BpSensorServer : public BpInterface<ISensorServer>
-{
-public:
-    explicit BpSensorServer(const sp<IBinder>& impl)
-        : BpInterface<ISensorServer>(impl)
-    {
-    }
-
-    virtual ~BpSensorServer();
-
-    virtual Vector<Sensor> getSensorList(const String16& opPackageName)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
-        data.writeString16(opPackageName);
-        remote()->transact(GET_SENSOR_LIST, data, &reply);
-        Sensor s;
-        Vector<Sensor> v;
-        uint32_t n = reply.readUint32();
-        v.setCapacity(n);
-        while (n--) {
-            reply.read(s);
-            v.add(s);
-        }
-        return v;
-    }
-
-    virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
-        data.writeString16(opPackageName);
-        remote()->transact(GET_DYNAMIC_SENSOR_LIST, data, &reply);
-        Sensor s;
-        Vector<Sensor> v;
-        uint32_t n = reply.readUint32();
-        v.setCapacity(n);
-        while (n--) {
-            reply.read(s);
-            v.add(s);
-        }
-        return v;
-    }
-
-    virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
-             int mode, const String16& opPackageName)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
-        data.writeString8(packageName);
-        data.writeInt32(mode);
-        data.writeString16(opPackageName);
-        remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
-        return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
-    }
-
-    virtual int isDataInjectionEnabled() {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
-        remote()->transact(ENABLE_DATA_INJECTION, data, &reply);
-        return reply.readInt32();
-    }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this
-// translation unit (see clang warning -Wweak-vtables)
-BpSensorServer::~BpSensorServer() {}
-
-IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer");
-
-// ----------------------------------------------------------------------
-
-status_t BnSensorServer::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code) {
-        case GET_SENSOR_LIST: {
-            CHECK_INTERFACE(ISensorServer, data, reply);
-            const String16& opPackageName = data.readString16();
-            Vector<Sensor> v(getSensorList(opPackageName));
-            size_t n = v.size();
-            reply->writeUint32(static_cast<uint32_t>(n));
-            for (size_t i = 0; i < n; i++) {
-                reply->write(v[i]);
-            }
-            return NO_ERROR;
-        }
-        case CREATE_SENSOR_EVENT_CONNECTION: {
-            CHECK_INTERFACE(ISensorServer, data, reply);
-            String8 packageName = data.readString8();
-            int32_t mode = data.readInt32();
-            const String16& opPackageName = data.readString16();
-            sp<ISensorEventConnection> connection(createSensorEventConnection(packageName, mode,
-                    opPackageName));
-            reply->writeStrongBinder(IInterface::asBinder(connection));
-            return NO_ERROR;
-        }
-        case ENABLE_DATA_INJECTION: {
-            CHECK_INTERFACE(ISensorServer, data, reply);
-            int32_t ret = isDataInjectionEnabled();
-            reply->writeInt32(static_cast<int32_t>(ret));
-            return NO_ERROR;
-        }
-        case GET_DYNAMIC_SENSOR_LIST: {
-            CHECK_INTERFACE(ISensorServer, data, reply);
-            const String16& opPackageName = data.readString16();
-            Vector<Sensor> v(getDynamicSensorList(opPackageName));
-            size_t n = v.size();
-            reply->writeUint32(static_cast<uint32_t>(n));
-            for (size_t i = 0; i < n; i++) {
-                reply->write(v[i]);
-            }
-            return NO_ERROR;
-        }
-    }
-    return BBinder::onTransact(code, data, reply, flags);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0a8e6a5..4d2692f 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -21,14 +21,14 @@
 #include <sys/types.h>
 
 #include <binder/Parcel.h>
-#include <binder/IMemory.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
-#include <gui/BitTube.h>
 #include <gui/IDisplayEventConnection.h>
-#include <gui/ISurfaceComposer.h>
+#include <gui/IGraphicBufferAlloc.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/ISurfaceComposerClient.h>
 
 #include <private/gui/LayerState.h>
 
@@ -44,8 +44,6 @@
 
 namespace android {
 
-class IDisplayEventConnection;
-
 class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
 {
 public:
@@ -64,6 +62,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 +112,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 +123,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 +166,50 @@
         return result != 0;
     }
 
+    virtual status_t getSupportedFrameTimestamps(
+            std::vector<FrameEvent>* outSupported) const {
+        if (!outSupported) {
+            return UNEXPECTED_NULL;
+        }
+        outSupported->clear();
+
+        Parcel data, reply;
+
+        status_t err = data.writeInterfaceToken(
+                ISurfaceComposer::getInterfaceDescriptor());
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        err = remote()->transact(
+                BnSurfaceComposer::GET_SUPPORTED_FRAME_TIMESTAMPS,
+                data, &reply);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        int32_t result = 0;
+        err = reply.readInt32(&result);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        std::vector<int32_t> supported;
+        err = reply.readInt32Vector(&supported);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        outSupported->reserve(supported.size());
+        for (int32_t s : supported) {
+            outSupported->push_back(static_cast<FrameEvent>(s));
+        }
+        return NO_ERROR;
+    }
+
     virtual sp<IDisplayEventConnection> createDisplayEventConnection()
     {
         Parcel data, reply;
@@ -379,10 +431,52 @@
         }
         result = reply.readInt32();
         if (result == NO_ERROR) {
-            result = reply.readParcelable(outCapabilities);
+            result = reply.read(*outCapabilities);
         }
         return result;
     }
+
+    virtual status_t enableVSyncInjections(bool enable) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("enableVSyncInjections failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeBool(enable);
+        if (result != NO_ERROR) {
+            ALOGE("enableVSyncInjections failed to writeBool: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS,
+                data, &reply, TF_ONE_WAY);
+        if (result != NO_ERROR) {
+            ALOGE("enableVSyncInjections failed to transact: %d", result);
+            return result;
+        }
+        return result;
+    }
+
+    virtual status_t injectVSync(nsecs_t when) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("injectVSync failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeInt64(when);
+        if (result != NO_ERROR) {
+            ALOGE("injectVSync failed to writeInt64: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, TF_ONE_WAY);
+        if (result != NO_ERROR) {
+            ALOGE("injectVSync failed to transact: %d", result);
+            return result;
+        }
+        return result;
+    }
+
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -403,6 +497,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 +560,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 +580,25 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case GET_SUPPORTED_FRAME_TIMESTAMPS: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            std::vector<FrameEvent> supportedTimestamps;
+            status_t result = getSupportedFrameTimestamps(&supportedTimestamps);
+            status_t err = reply->writeInt32(result);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            if (result != NO_ERROR) {
+                return result;
+            }
+
+            std::vector<int32_t> supported;
+            supported.reserve(supportedTimestamps.size());
+            for (FrameEvent s : supportedTimestamps) {
+                supported.push_back(static_cast<int32_t>(s));
+            }
+            return reply->writeInt32Vector(supported);
+        }
         case CREATE_DISPLAY_EVENT_CONNECTION: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IDisplayEventConnection> connection(createDisplayEventConnection());
@@ -631,10 +752,30 @@
             result = getHdrCapabilities(display, &capabilities);
             reply->writeInt32(result);
             if (result == NO_ERROR) {
-                reply->writeParcelable(capabilities);
+                reply->write(capabilities);
             }
             return NO_ERROR;
         }
+        case ENABLE_VSYNC_INJECTIONS: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            bool enable = false;
+            status_t result = data.readBool(&enable);
+            if (result != NO_ERROR) {
+                ALOGE("enableVSyncInjections failed to readBool: %d", result);
+                return result;
+            }
+            return enableVSyncInjections(enable);
+        }
+        case INJECT_VSYNC: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            int64_t when = 0;
+            status_t result = data.readInt64(&when);
+            if (result != NO_ERROR) {
+                ALOGE("enableVSyncInjections failed to readInt64: %d", result);
+                return result;
+            }
+            return injectVSync(when);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 47cb047..db8a517 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -22,7 +22,6 @@
 #include <sys/types.h>
 
 #include <binder/Parcel.h>
-#include <binder/IMemory.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
@@ -56,8 +55,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 +64,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 +149,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..2461cba 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -28,7 +28,7 @@
     output.writeUint32(what);
     output.writeFloat(x);
     output.writeFloat(y);
-    output.writeUint32(z);
+    output.writeInt32(z);
     output.writeUint32(w);
     output.writeUint32(h);
     output.writeUint32(layerStack);
@@ -39,9 +39,11 @@
             output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
     output.write(crop);
     output.write(finalCrop);
-    output.writeStrongBinder(handle);
+    output.writeStrongBinder(barrierHandle);
+    output.writeStrongBinder(reparentHandle);
     output.writeUint64(frameNumber);
     output.writeInt32(overrideScalingMode);
+    output.writeStrongBinder(IInterface::asBinder(barrierGbp));
     output.write(transparentRegion);
     return NO_ERROR;
 }
@@ -52,7 +54,7 @@
     what = input.readUint32();
     x = input.readFloat();
     y = input.readFloat();
-    z = input.readUint32();
+    z = input.readInt32();
     w = input.readUint32();
     h = input.readUint32();
     layerStack = input.readUint32();
@@ -67,9 +69,12 @@
     }
     input.read(crop);
     input.read(finalCrop);
-    handle = input.readStrongBinder();
+    barrierHandle = input.readStrongBinder();
+    reparentHandle = input.readStrongBinder();
     frameNumber = input.readUint64();
     overrideScalingMode = input.readInt32();
+    barrierGbp =
+        interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
     input.read(transparentRegion);
     return NO_ERROR;
 }
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
deleted file mode 100644
index 2c87562..0000000
--- a/libs/gui/Sensor.cpp
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <sys/limits.h>
-#include <sys/types.h>
-
-#include <binder/AppOpsManager.h>
-#include <binder/IServiceManager.h>
-#include <gui/Sensor.h>
-#include <hardware/sensors.h>
-#include <log/log.h>
-#include <utils/Errors.h>
-#include <utils/String8.h>
-#include <utils/Flattenable.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-Sensor::Sensor(const char * name) :
-        mName(name), mHandle(0), mType(0),
-        mMinValue(0), mMaxValue(0), mResolution(0),
-        mPower(0), mMinDelay(0), mVersion(0), mFifoReservedEventCount(0),
-        mFifoMaxEventCount(0), mRequiredAppOp(0),
-        mMaxDelay(0), mFlags(0) {
-}
-
-Sensor::Sensor(struct sensor_t const* hwSensor, int halVersion) :
-        Sensor(*hwSensor, uuid_t(), halVersion) {
-}
-
-Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion) {
-    mName = hwSensor.name;
-    mVendor = hwSensor.vendor;
-    mVersion = hwSensor.version;
-    mHandle = hwSensor.handle;
-    mType = hwSensor.type;
-    mMinValue = 0;                      // FIXME: minValue
-    mMaxValue = hwSensor.maxRange;     // FIXME: maxValue
-    mResolution = hwSensor.resolution;
-    mPower = hwSensor.power;
-    mMinDelay = hwSensor.minDelay;
-    mFlags = 0;
-    mUuid = uuid;
-
-    // Set fifo event count zero for older devices which do not support batching. Fused
-    // sensors also have their fifo counts set to zero.
-    if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
-        mFifoReservedEventCount = hwSensor.fifoReservedEventCount;
-        mFifoMaxEventCount = hwSensor.fifoMaxEventCount;
-    } else {
-        mFifoReservedEventCount = 0;
-        mFifoMaxEventCount = 0;
-    }
-
-    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
-        if (hwSensor.maxDelay > INT_MAX) {
-            // Max delay is declared as a 64 bit integer for 64 bit architectures. But it should
-            // always fit in a 32 bit integer, log error and cap it to INT_MAX.
-            ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.string(),
-                  static_cast<int64_t>(hwSensor.maxDelay));
-            mMaxDelay = INT_MAX;
-        } else {
-            mMaxDelay = static_cast<int32_t>(hwSensor.maxDelay);
-        }
-    } else {
-        // For older hals set maxDelay to 0.
-        mMaxDelay = 0;
-    }
-
-    // Ensure existing sensors have correct string type, required permissions and reporting mode.
-    // Set reportingMode for all android defined sensor types, set wake-up flag only for proximity
-    // sensor, significant motion, tilt, pick_up gesture, wake gesture and glance gesture on older
-    // HALs. Newer HALs can define both wake-up and non wake-up proximity sensors.
-    // All the OEM defined defined sensors have flags set to whatever is provided by the HAL.
-    switch (mType) {
-    case SENSOR_TYPE_ACCELEROMETER:
-        mStringType = SENSOR_STRING_TYPE_ACCELEROMETER;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_AMBIENT_TEMPERATURE:
-        mStringType = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_GAME_ROTATION_VECTOR:
-        mStringType = SENSOR_STRING_TYPE_GAME_ROTATION_VECTOR;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
-        mStringType = SENSOR_STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_GRAVITY:
-        mStringType = SENSOR_STRING_TYPE_GRAVITY;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_GYROSCOPE:
-        mStringType = SENSOR_STRING_TYPE_GYROSCOPE;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
-        mStringType = SENSOR_STRING_TYPE_GYROSCOPE_UNCALIBRATED;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_HEART_RATE: {
-        mStringType = SENSOR_STRING_TYPE_HEART_RATE;
-        mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
-        AppOpsManager appOps;
-        mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        } break;
-    case SENSOR_TYPE_LIGHT:
-        mStringType = SENSOR_STRING_TYPE_LIGHT;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_LINEAR_ACCELERATION:
-        mStringType = SENSOR_STRING_TYPE_LINEAR_ACCELERATION;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_MAGNETIC_FIELD:
-        mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
-        mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_ORIENTATION:
-        mStringType = SENSOR_STRING_TYPE_ORIENTATION;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_PRESSURE:
-        mStringType = SENSOR_STRING_TYPE_PRESSURE;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_PROXIMITY:
-        mStringType = SENSOR_STRING_TYPE_PROXIMITY;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_RELATIVE_HUMIDITY:
-        mStringType = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_ROTATION_VECTOR:
-        mStringType = SENSOR_STRING_TYPE_ROTATION_VECTOR;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_SIGNIFICANT_MOTION:
-        mStringType = SENSOR_STRING_TYPE_SIGNIFICANT_MOTION;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_STEP_COUNTER:
-        mStringType = SENSOR_STRING_TYPE_STEP_COUNTER;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_STEP_DETECTOR:
-        mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
-        break;
-    case SENSOR_TYPE_TEMPERATURE:
-        mStringType = SENSOR_STRING_TYPE_TEMPERATURE;
-        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-        break;
-    case SENSOR_TYPE_TILT_DETECTOR:
-        mStringType = SENSOR_STRING_TYPE_TILT_DETECTOR;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_WAKE_GESTURE:
-        mStringType = SENSOR_STRING_TYPE_WAKE_GESTURE;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_GLANCE_GESTURE:
-        mStringType = SENSOR_STRING_TYPE_GLANCE_GESTURE;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_PICK_UP_GESTURE:
-        mStringType = SENSOR_STRING_TYPE_PICK_UP_GESTURE;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_WRIST_TILT_GESTURE:
-        mStringType = SENSOR_STRING_TYPE_WRIST_TILT_GESTURE;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_DYNAMIC_SENSOR_META:
-        mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_POSE_6DOF:
-        mStringType = SENSOR_STRING_TYPE_POSE_6DOF;
-        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-        break;
-    case SENSOR_TYPE_STATIONARY_DETECT:
-        mStringType = SENSOR_STRING_TYPE_STATIONARY_DETECT;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_MOTION_DETECT:
-        mStringType = SENSOR_STRING_TYPE_MOTION_DETECT;
-        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags |= SENSOR_FLAG_WAKE_UP;
-        }
-        break;
-    case SENSOR_TYPE_HEART_BEAT:
-        mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
-        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
-        break;
-    default:
-        // Only pipe the stringType, requiredPermission and flags for custom sensors.
-        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
-            mStringType = hwSensor.stringType;
-        }
-        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) {
-            mRequiredPermission = hwSensor.requiredPermission;
-            if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) {
-                AppOpsManager appOps;
-                mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
-            }
-        }
-
-        if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
-            mFlags = static_cast<uint32_t>(hwSensor.flags);
-        } else {
-            // This is an OEM defined sensor on an older HAL. Use minDelay to determine the
-            // reporting mode of the sensor.
-            if (mMinDelay > 0) {
-                mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
-            } else if (mMinDelay == 0) {
-                mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
-            } else if (mMinDelay < 0) {
-                mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
-            }
-        }
-        break;
-    }
-
-    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
-        // Wake-up flag of HAL 1.3 and above is set here
-        mFlags |= (hwSensor.flags & SENSOR_FLAG_WAKE_UP);
-
-        // Log error if the reporting mode is not as expected, but respect HAL setting.
-        int actualReportingMode = (hwSensor.flags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
-        int expectedReportingMode = (mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
-        if (actualReportingMode != expectedReportingMode) {
-            ALOGE("Reporting Mode incorrect: sensor %s handle=%#010" PRIx32 " type=%" PRId32 " "
-                   "actual=%d expected=%d",
-                   mName.string(), mHandle, mType, actualReportingMode, expectedReportingMode);
-        }
-    }
-
-    // Feature flags
-    // Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3.
-    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
-        mFlags |= (hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK));
-    }
-    // Set DATA_INJECTION flag here. Defined in HAL 1_4.
-    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) {
-        mFlags |= (hwSensor.flags & DATA_INJECTION_MASK);
-    }
-
-    if (mRequiredPermission.length() > 0) {
-        // If the sensor is protected by a permission we need to know if it is
-        // a runtime one to determine whether we can use the permission cache.
-        sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
-        if (binder != 0) {
-            sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
-            mRequiredPermissionRuntime = permCtrl->isRuntimePermission(
-                    String16(mRequiredPermission));
-        }
-    }
-}
-
-Sensor::~Sensor() {
-}
-
-const String8& Sensor::getName() const {
-    return mName;
-}
-
-const String8& Sensor::getVendor() const {
-    return mVendor;
-}
-
-int32_t Sensor::getHandle() const {
-    return mHandle;
-}
-
-int32_t Sensor::getType() const {
-    return mType;
-}
-
-float Sensor::getMinValue() const {
-    return mMinValue;
-}
-
-float Sensor::getMaxValue() const {
-    return mMaxValue;
-}
-
-float Sensor::getResolution() const {
-    return mResolution;
-}
-
-float Sensor::getPowerUsage() const {
-    return mPower;
-}
-
-int32_t Sensor::getMinDelay() const {
-    return mMinDelay;
-}
-
-nsecs_t Sensor::getMinDelayNs() const {
-    return getMinDelay() * 1000;
-}
-
-int32_t Sensor::getVersion() const {
-    return mVersion;
-}
-
-uint32_t Sensor::getFifoReservedEventCount() const {
-    return mFifoReservedEventCount;
-}
-
-uint32_t Sensor::getFifoMaxEventCount() const {
-    return mFifoMaxEventCount;
-}
-
-const String8& Sensor::getStringType() const {
-    return mStringType;
-}
-
-const String8& Sensor::getRequiredPermission() const {
-    return mRequiredPermission;
-}
-
-bool Sensor::isRequiredPermissionRuntime() const {
-    return mRequiredPermissionRuntime;
-}
-
-int32_t Sensor::getRequiredAppOp() const {
-    return mRequiredAppOp;
-}
-
-int32_t Sensor::getMaxDelay() const {
-    return mMaxDelay;
-}
-
-uint32_t Sensor::getFlags() const {
-    return mFlags;
-}
-
-bool Sensor::isWakeUpSensor() const {
-    return (mFlags & SENSOR_FLAG_WAKE_UP) != 0;
-}
-
-bool Sensor::isDynamicSensor() const {
-    return (mFlags & SENSOR_FLAG_DYNAMIC_SENSOR) != 0;
-}
-
-bool Sensor::hasAdditionalInfo() const {
-    return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0;
-}
-
-int32_t Sensor::getReportingMode() const {
-    return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
-}
-
-const Sensor::uuid_t& Sensor::getUuid() const {
-    return mUuid;
-}
-
-void Sensor::setId(int32_t id) {
-    mUuid.i64[0] = id;
-    mUuid.i64[1] = 0;
-}
-
-int32_t Sensor::getId() const {
-    return int32_t(mUuid.i64[0]);
-}
-
-size_t Sensor::getFlattenedSize() const {
-    size_t fixedSize =
-            sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) +
-            sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) +
-            sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) +
-            sizeof(mFifoMaxEventCount) + sizeof(mRequiredPermissionRuntime) +
-            sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid);
-
-    size_t variableSize =
-            sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) +
-            sizeof(uint32_t) + FlattenableUtils::align<4>(mVendor.length()) +
-            sizeof(uint32_t) + FlattenableUtils::align<4>(mStringType.length()) +
-            sizeof(uint32_t) + FlattenableUtils::align<4>(mRequiredPermission.length());
-
-    return fixedSize + variableSize;
-}
-
-status_t Sensor::flatten(void* buffer, size_t size) const {
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    flattenString8(buffer, size, mName);
-    flattenString8(buffer, size, mVendor);
-    FlattenableUtils::write(buffer, size, mVersion);
-    FlattenableUtils::write(buffer, size, mHandle);
-    FlattenableUtils::write(buffer, size, mType);
-    FlattenableUtils::write(buffer, size, mMinValue);
-    FlattenableUtils::write(buffer, size, mMaxValue);
-    FlattenableUtils::write(buffer, size, mResolution);
-    FlattenableUtils::write(buffer, size, mPower);
-    FlattenableUtils::write(buffer, size, mMinDelay);
-    FlattenableUtils::write(buffer, size, mFifoReservedEventCount);
-    FlattenableUtils::write(buffer, size, mFifoMaxEventCount);
-    flattenString8(buffer, size, mStringType);
-    flattenString8(buffer, size, mRequiredPermission);
-    FlattenableUtils::write(buffer, size, mRequiredPermissionRuntime);
-    FlattenableUtils::write(buffer, size, mRequiredAppOp);
-    FlattenableUtils::write(buffer, size, mMaxDelay);
-    FlattenableUtils::write(buffer, size, mFlags);
-    if (mUuid.i64[1] != 0) {
-        // We should never hit this case with our current API, but we
-        // could via a careless API change.  If that happens,
-        // this code will keep us from leaking our UUID (while probably
-        // breaking dynamic sensors).  See b/29547335.
-        ALOGW("Sensor with UUID being flattened; sending 0.  Expect "
-              "bad dynamic sensor behavior");
-        uuid_t tmpUuid;  // default constructor makes this 0.
-        FlattenableUtils::write(buffer, size, tmpUuid);
-    } else {
-        FlattenableUtils::write(buffer, size, mUuid);
-    }
-    return NO_ERROR;
-}
-
-status_t Sensor::unflatten(void const* buffer, size_t size) {
-    if (!unflattenString8(buffer, size, mName)) {
-        return NO_MEMORY;
-    }
-    if (!unflattenString8(buffer, size, mVendor)) {
-        return NO_MEMORY;
-    }
-
-    size_t fixedSize1 =
-            sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) + sizeof(mMinValue) +
-            sizeof(mMaxValue) + sizeof(mResolution) + sizeof(mPower) + sizeof(mMinDelay) +
-            sizeof(mFifoMaxEventCount) + sizeof(mFifoMaxEventCount);
-    if (size < fixedSize1) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, mVersion);
-    FlattenableUtils::read(buffer, size, mHandle);
-    FlattenableUtils::read(buffer, size, mType);
-    FlattenableUtils::read(buffer, size, mMinValue);
-    FlattenableUtils::read(buffer, size, mMaxValue);
-    FlattenableUtils::read(buffer, size, mResolution);
-    FlattenableUtils::read(buffer, size, mPower);
-    FlattenableUtils::read(buffer, size, mMinDelay);
-    FlattenableUtils::read(buffer, size, mFifoReservedEventCount);
-    FlattenableUtils::read(buffer, size, mFifoMaxEventCount);
-
-    if (!unflattenString8(buffer, size, mStringType)) {
-        return NO_MEMORY;
-    }
-    if (!unflattenString8(buffer, size, mRequiredPermission)) {
-        return NO_MEMORY;
-    }
-
-    size_t fixedSize2 =
-            sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) +
-            sizeof(mFlags) + sizeof(mUuid);
-    if (size < fixedSize2) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, mRequiredPermissionRuntime);
-    FlattenableUtils::read(buffer, size, mRequiredAppOp);
-    FlattenableUtils::read(buffer, size, mMaxDelay);
-    FlattenableUtils::read(buffer, size, mFlags);
-    FlattenableUtils::read(buffer, size, mUuid);
-    return NO_ERROR;
-}
-
-void Sensor::flattenString8(void*& buffer, size_t& size,
-        const String8& string8) {
-    uint32_t len = static_cast<uint32_t>(string8.length());
-    FlattenableUtils::write(buffer, size, len);
-    memcpy(static_cast<char*>(buffer), string8.string(), len);
-    FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
-}
-
-bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) {
-    uint32_t len;
-    if (size < sizeof(len)) {
-        return false;
-    }
-    FlattenableUtils::read(buffer, size, len);
-    if (size < len) {
-        return false;
-    }
-    outputString8.setTo(static_cast<char const*>(buffer), len);
-    FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
-    return true;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
deleted file mode 100644
index 6d69839..0000000
--- a/libs/gui/SensorEventQueue.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Sensors"
-
-#include <algorithm>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <linux/errno.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Looper.h>
-
-#include <gui/Sensor.h>
-#include <gui/BitTube.h>
-#include <gui/SensorEventQueue.h>
-#include <gui/ISensorEventConnection.h>
-
-#include <android/sensor.h>
-
-using std::min;
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
-    : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0),
-      mNumAcksToSend(0) {
-    mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
-}
-
-SensorEventQueue::~SensorEventQueue() {
-    delete [] mRecBuffer;
-}
-
-void SensorEventQueue::onFirstRef()
-{
-    mSensorChannel = mSensorEventConnection->getSensorChannel();
-}
-
-int SensorEventQueue::getFd() const
-{
-    return mSensorChannel->getFd();
-}
-
-
-ssize_t SensorEventQueue::write(const sp<BitTube>& tube,
-        ASensorEvent const* events, size_t numEvents) {
-    return BitTube::sendObjects(tube, events, numEvents);
-}
-
-ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) {
-    if (mAvailable == 0) {
-        ssize_t err = BitTube::recvObjects(mSensorChannel,
-                mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
-        if (err < 0) {
-            return err;
-        }
-        mAvailable = static_cast<size_t>(err);
-        mConsumed = 0;
-    }
-    size_t count = min(numEvents, mAvailable);
-    memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent));
-    mAvailable -= count;
-    mConsumed += count;
-    return static_cast<ssize_t>(count);
-}
-
-sp<Looper> SensorEventQueue::getLooper() const
-{
-    Mutex::Autolock _l(mLock);
-    if (mLooper == 0) {
-        mLooper = new Looper(true);
-        mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
-    }
-    return mLooper;
-}
-
-status_t SensorEventQueue::waitForEvent() const
-{
-    const int fd = getFd();
-    sp<Looper> looper(getLooper());
-
-    int events;
-    int32_t result;
-    do {
-        result = looper->pollOnce(-1, NULL, &events, NULL);
-        if (result == ALOOPER_POLL_ERROR) {
-            ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
-            result = -EPIPE; // unknown error, so we make up one
-            break;
-        }
-        if (events & ALOOPER_EVENT_HANGUP) {
-            // the other-side has died
-            ALOGE("SensorEventQueue::waitForEvent error HANGUP");
-            result = -EPIPE; // unknown error, so we make up one
-            break;
-        }
-    } while (result != fd);
-
-    return  (result == fd) ? status_t(NO_ERROR) : result;
-}
-
-status_t SensorEventQueue::wake() const
-{
-    sp<Looper> looper(getLooper());
-    looper->wake();
-    return NO_ERROR;
-}
-
-status_t SensorEventQueue::enableSensor(Sensor const* sensor) const {
-    return enableSensor(sensor, SENSOR_DELAY_NORMAL);
-}
-
-status_t SensorEventQueue::enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const {
-    return mSensorEventConnection->enableDisable(sensor->getHandle(), true,
-                                                 us2ns(samplingPeriodUs), 0, 0);
-}
-
-status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
-    return mSensorEventConnection->enableDisable(sensor->getHandle(), false, 0, 0, 0);
-}
-
-status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs,
-                                        int maxBatchReportLatencyUs, int reservedFlags) const {
-    return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs),
-                                                 us2ns(maxBatchReportLatencyUs), reservedFlags);
-}
-
-status_t SensorEventQueue::flush() const {
-    return mSensorEventConnection->flush();
-}
-
-status_t SensorEventQueue::disableSensor(int32_t handle) const {
-    return mSensorEventConnection->enableDisable(handle, false, 0, 0, false);
-}
-
-status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const {
-    return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
-}
-
-status_t SensorEventQueue::injectSensorEvent(const ASensorEvent& event) {
-    do {
-        // Blocking call.
-        ssize_t size = ::send(mSensorChannel->getFd(), &event, sizeof(event), MSG_NOSIGNAL);
-        if (size >= 0) {
-            return NO_ERROR;
-        } else if (size < 0 && errno == EAGAIN) {
-            // If send is returning a "Try again" error, sleep for 100ms and try again. In all
-            // other cases log a failure and exit.
-            usleep(100000);
-        } else {
-            ALOGE("injectSensorEvent failure %s %zd", strerror(errno), size);
-            return INVALID_OPERATION;
-        }
-    } while (true);
-}
-
-void SensorEventQueue::sendAck(const ASensorEvent* events, int count) {
-    for (int i = 0; i < count; ++i) {
-        if (events[i].flags & WAKE_UP_SENSOR_EVENT_NEEDS_ACK) {
-            ++mNumAcksToSend;
-        }
-    }
-    // Send mNumAcksToSend to acknowledge for the wake up sensor events received.
-    if (mNumAcksToSend > 0) {
-        ssize_t size = ::send(mSensorChannel->getFd(), &mNumAcksToSend, sizeof(mNumAcksToSend),
-                MSG_DONTWAIT | MSG_NOSIGNAL);
-        if (size < 0) {
-            ALOGE("sendAck failure %zd %d", size, mNumAcksToSend);
-        } else {
-            mNumAcksToSend = 0;
-        }
-    }
-    return;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
deleted file mode 100644
index 57c3073..0000000
--- a/libs/gui/SensorManager.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Sensors"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Singleton.h>
-
-#include <binder/IBinder.h>
-#include <binder/IServiceManager.h>
-
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-#include <gui/Sensor.h>
-#include <gui/SensorManager.h>
-#include <gui/SensorEventQueue.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-android::Mutex android::SensorManager::sLock;
-std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
-
-SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) {
-    Mutex::Autolock _l(sLock);
-    SensorManager* sensorManager;
-    std::map<String16, SensorManager*>::iterator iterator =
-        sPackageInstances.find(packageName);
-
-    if (iterator != sPackageInstances.end()) {
-        sensorManager = iterator->second;
-    } else {
-        String16 opPackageName = packageName;
-
-        // It is possible that the calling code has no access to the package name.
-        // In this case we will get the packages for the calling UID and pick the
-        // first one for attributing the app op. This will work correctly for
-        // runtime permissions as for legacy apps we will toggle the app op for
-        // all packages in the UID. The caveat is that the operation may be attributed
-        // to the wrong package and stats based on app ops may be slightly off.
-        if (opPackageName.size() <= 0) {
-            sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
-            if (binder != 0) {
-                const uid_t uid = IPCThreadState::self()->getCallingUid();
-                Vector<String16> packages;
-                interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
-                if (!packages.isEmpty()) {
-                    opPackageName = packages[0];
-                } else {
-                    ALOGE("No packages for calling UID");
-                }
-            } else {
-                ALOGE("Cannot get permission service");
-            }
-        }
-
-        sensorManager = new SensorManager(opPackageName);
-
-        // If we had no package name, we looked it up from the UID and the sensor
-        // manager instance we created should also be mapped to the empty package
-        // name, to avoid looking up the packages for a UID and get the same result.
-        if (packageName.size() <= 0) {
-            sPackageInstances.insert(std::make_pair(String16(), sensorManager));
-        }
-
-        // Stash the per package sensor manager.
-        sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
-    }
-
-    return *sensorManager;
-}
-
-SensorManager::SensorManager(const String16& opPackageName)
-    : mSensorList(0), mOpPackageName(opPackageName) {
-    // okay we're not locked here, but it's not needed during construction
-    assertStateLocked();
-}
-
-SensorManager::~SensorManager() {
-    free(mSensorList);
-}
-
-void SensorManager::sensorManagerDied() {
-    Mutex::Autolock _l(mLock);
-    mSensorServer.clear();
-    free(mSensorList);
-    mSensorList = NULL;
-    mSensors.clear();
-}
-
-status_t SensorManager::assertStateLocked() {
-    bool initSensorManager = false;
-    if (mSensorServer == NULL) {
-        initSensorManager = true;
-    } else {
-        // Ping binder to check if sensorservice is alive.
-        status_t err = IInterface::asBinder(mSensorServer)->pingBinder();
-        if (err != NO_ERROR) {
-            initSensorManager = true;
-        }
-    }
-    if (initSensorManager) {
-        // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ...
-        const String16 name("sensorservice");
-        for (int i = 0; i < 60; i++) {
-            status_t err = getService(name, &mSensorServer);
-            if (err == NAME_NOT_FOUND) {
-                sleep(1);
-                continue;
-            }
-            if (err != NO_ERROR) {
-                return err;
-            }
-            break;
-        }
-
-        class DeathObserver : public IBinder::DeathRecipient {
-            SensorManager& mSensorManager;
-            virtual void binderDied(const wp<IBinder>& who) {
-                ALOGW("sensorservice died [%p]", who.unsafe_get());
-                mSensorManager.sensorManagerDied();
-            }
-        public:
-            explicit DeathObserver(SensorManager& mgr) : mSensorManager(mgr) { }
-        };
-
-        LOG_ALWAYS_FATAL_IF(mSensorServer.get() == NULL, "getService(SensorService) NULL");
-
-        mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this));
-        IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver);
-
-        mSensors = mSensorServer->getSensorList(mOpPackageName);
-        size_t count = mSensors.size();
-        mSensorList =
-                static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
-        LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL");
-
-        for (size_t i=0 ; i<count ; i++) {
-            mSensorList[i] = mSensors.array() + i;
-        }
-    }
-
-    return NO_ERROR;
-}
-
-ssize_t SensorManager::getSensorList(Sensor const* const** list) {
-    Mutex::Autolock _l(mLock);
-    status_t err = assertStateLocked();
-    if (err < 0) {
-        return static_cast<ssize_t>(err);
-    }
-    *list = mSensorList;
-    return static_cast<ssize_t>(mSensors.size());
-}
-
-ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) {
-    Mutex::Autolock _l(mLock);
-    status_t err = assertStateLocked();
-    if (err < 0) {
-        return static_cast<ssize_t>(err);
-    }
-
-    dynamicSensors = mSensorServer->getDynamicSensorList(mOpPackageName);
-    size_t count = dynamicSensors.size();
-
-    return static_cast<ssize_t>(count);
-}
-
-Sensor const* SensorManager::getDefaultSensor(int type)
-{
-    Mutex::Autolock _l(mLock);
-    if (assertStateLocked() == NO_ERROR) {
-        bool wakeUpSensor = false;
-        // For the following sensor types, return a wake-up sensor. These types are by default
-        // defined as wake-up sensors. For the rest of the sensor types defined in sensors.h return
-        // a non_wake-up version.
-        if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION ||
-            type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
-            type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
-            type == SENSOR_TYPE_WRIST_TILT_GESTURE) {
-            wakeUpSensor = true;
-        }
-        // For now we just return the first sensor of that type we find.
-        // in the future it will make sense to let the SensorService make
-        // that decision.
-        for (size_t i=0 ; i<mSensors.size() ; i++) {
-            if (mSensorList[i]->getType() == type &&
-                mSensorList[i]->isWakeUpSensor() == wakeUpSensor) {
-                return mSensorList[i];
-            }
-        }
-    }
-    return NULL;
-}
-
-sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
-    sp<SensorEventQueue> queue;
-
-    Mutex::Autolock _l(mLock);
-    while (assertStateLocked() == NO_ERROR) {
-        sp<ISensorEventConnection> connection =
-                mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName);
-        if (connection == NULL) {
-            // SensorService just died or the app doesn't have required permissions.
-            ALOGE("createEventQueue: connection is NULL.");
-            return NULL;
-        }
-        queue = new SensorEventQueue(connection);
-        break;
-    }
-    return queue;
-}
-
-bool SensorManager::isDataInjectionEnabled() {
-    Mutex::Autolock _l(mLock);
-    if (assertStateLocked() == NO_ERROR) {
-        return mSensorServer->isDataInjectionEnabled();
-    }
-    return false;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d1a9cbb..ad1984a 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -18,23 +18,23 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
-#include <android/native_window.h>
+#include <gui/Surface.h>
 
-#include <binder/Parcel.h>
+#include <android/native_window.h>
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
 #include <utils/NativeHandle.h>
 
+#include <ui/DisplayStatInfo.h>
 #include <ui/Fence.h>
+#include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
 
+#include <gui/BufferItem.h>
 #include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <gui/GLConsumer.h>
-#include <gui/Surface.h>
 
+#include <gui/ISurfaceComposer.h>
 #include <private/gui/ComposerService.h>
 
 namespace android {
@@ -49,7 +49,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 +97,14 @@
     }
 }
 
+sp<ISurfaceComposer> Surface::composerService() const {
+    return ComposerService::getComposerService();
+}
+
+nsecs_t Surface::now() const {
+    return systemTime();
+}
+
 sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const {
     return mGraphicBufferProducer;
 }
@@ -135,37 +147,208 @@
             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) {
+status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) {
     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;
+    DisplayStatInfo stats;
+    status_t err = composerService()->getDisplayStats(NULL, &stats);
+
+    *outRefreshDuration = stats.vsyncPeriod;
+
+    return NO_ERROR;
+}
+
+void Surface::enableFrameTimestamps(bool enable) {
+    Mutex::Autolock lock(mMutex);
+    // If going from disabled to enabled, get the initial values for
+    // compositor and display timing.
+    if (!mEnableFrameTimestamps && enable) {
+        FrameEventHistoryDelta delta;
+        mGraphicBufferProducer->getFrameTimestamps(&delta);
+        mFrameEventHistory->applyDelta(delta);
     }
-    return false;
+    mEnableFrameTimestamps = enable;
+}
+
+status_t Surface::getCompositorTiming(
+        nsecs_t* compositeDeadline, nsecs_t* compositeInterval,
+        nsecs_t* compositeToPresentLatency) {
+    Mutex::Autolock lock(mMutex);
+    if (!mEnableFrameTimestamps) {
+        return INVALID_OPERATION;
+    }
+
+    if (compositeDeadline != nullptr) {
+        *compositeDeadline =
+                mFrameEventHistory->getNextCompositeDeadline(now());
+    }
+    if (compositeInterval != nullptr) {
+        *compositeInterval = mFrameEventHistory->getCompositeInterval();
+    }
+    if (compositeToPresentLatency != nullptr) {
+        *compositeToPresentLatency =
+                mFrameEventHistory->getCompositeToPresentLatency();
+    }
+    return NO_ERROR;
+}
+
+static bool checkConsumerForUpdates(
+        const FrameEvents* e, const uint64_t lastFrameNumber,
+        const nsecs_t* outLatchTime,
+        const nsecs_t* outFirstRefreshStartTime,
+        const nsecs_t* outLastRefreshStartTime,
+        const nsecs_t* outGpuCompositionDoneTime,
+        const nsecs_t* outDisplayPresentTime,
+        const nsecs_t* outDisplayRetireTime,
+        const nsecs_t* outDequeueReadyTime,
+        const nsecs_t* outReleaseTime) {
+    bool checkForLatch = (outLatchTime != nullptr) && !e->hasLatchInfo();
+    bool checkForFirstRefreshStart = (outFirstRefreshStartTime != nullptr) &&
+            !e->hasFirstRefreshStartInfo();
+    bool checkForGpuCompositionDone = (outGpuCompositionDoneTime != nullptr) &&
+            !e->hasGpuCompositionDoneInfo();
+    bool checkForDisplayPresent = (outDisplayPresentTime != nullptr) &&
+            !e->hasDisplayPresentInfo();
+
+    // LastRefreshStart, 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 || checkForGpuCompositionDone ||
+            checkForDisplayPresent || checkForDisplayRetire ||
+            checkForDequeueReady || checkForRelease;
+}
+
+static void getFrameTimestamp(nsecs_t *dst, const nsecs_t& src) {
+    if (dst != nullptr) {
+        *dst = FrameEvents::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* outGpuCompositionDoneTime,
+        nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime,
+        nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime) {
+    ATRACE_CALL();
+
+    Mutex::Autolock lock(mMutex);
+
+    if (!mEnableFrameTimestamps) {
+        return INVALID_OPERATION;
+    }
+
+    // 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,
+            outGpuCompositionDoneTime, 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(
+            outGpuCompositionDoneTime, events->gpuCompositionDoneFence);
+    getFrameTimestampFence(
+            outDisplayPresentTime, events->displayPresentFence);
+    getFrameTimestampFence(outDisplayRetireTime, events->displayRetireFence);
+    getFrameTimestampFence(outReleaseTime, events->releaseFence);
+
+    return NO_ERROR;
+}
+
+status_t Surface::getWideColorSupport(bool* supported) {
+    ATRACE_CALL();
+
+    sp<IBinder> display(
+        composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    Vector<android_color_mode_t> colorModes;
+    status_t err =
+        composerService()->getDisplayColorModes(display, &colorModes);
+
+    if (err)
+        return err;
+
+    *supported = false;
+    for (android_color_mode_t colorMode : colorModes) {
+        switch (colorMode) {
+            case HAL_COLOR_MODE_DISPLAY_P3:
+            case HAL_COLOR_MODE_ADOBE_RGB:
+            case HAL_COLOR_MODE_DCI_P3:
+                *supported = true;
+                break;
+            default:
+                break;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t Surface::getHdrSupport(bool* supported) {
+    ATRACE_CALL();
+
+    sp<IBinder> display(
+        composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    HdrCapabilities hdrCapabilities;
+    status_t err =
+        composerService()->getHdrCapabilities(display, &hdrCapabilities);
+
+    if (err)
+        return err;
+
+    *supported = !hdrCapabilities.getSupportedHdrTypes().empty();
+
+    return NO_ERROR;
 }
 
 int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
@@ -271,6 +454,7 @@
     uint32_t reqHeight;
     PixelFormat reqFormat;
     uint32_t reqUsage;
+    bool enableFrameTimestamps;
 
     {
         Mutex::Autolock lock(mMutex);
@@ -281,6 +465,8 @@
         reqFormat = mReqFormat;
         reqUsage = mReqUsage;
 
+        enableFrameTimestamps = mEnableFrameTimestamps;
+
         if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot !=
                 BufferItem::INVALID_BUFFER_SLOT) {
             sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
@@ -295,8 +481,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 +506,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 +628,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 +700,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 +740,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 +778,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 +826,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 +911,27 @@
     case NATIVE_WINDOW_SET_AUTO_REFRESH:
         res = dispatchSetAutoRefresh(args);
         break;
+    case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION:
+        res = dispatchGetDisplayRefreshCycleDuration(args);
+        break;
+    case NATIVE_WINDOW_GET_NEXT_FRAME_ID:
+        res = dispatchGetNextFrameId(args);
+        break;
+    case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS:
+        res = dispatchEnableFrameTimestamps(args);
+        break;
+    case NATIVE_WINDOW_GET_COMPOSITOR_TIMING:
+        res = dispatchGetCompositorTiming(args);
+        break;
     case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
         res = dispatchGetFrameTimestamps(args);
         break;
+    case NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT:
+        res = dispatchGetWideColorSupport(args);
+        break;
+    case NATIVE_WINDOW_GET_HDR_SUPPORT:
+        res = dispatchGetHdrSupport(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -793,18 +1052,58 @@
     return setAutoRefresh(autoRefresh);
 }
 
+int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) {
+    nsecs_t* outRefreshDuration = va_arg(args, int64_t*);
+    return getDisplayRefreshCycleDuration(outRefreshDuration);
+}
+
+int Surface::dispatchGetNextFrameId(va_list args) {
+    uint64_t* nextFrameId = va_arg(args, uint64_t*);
+    *nextFrameId = getNextFrameNumber();
+    return NO_ERROR;
+}
+
+int Surface::dispatchEnableFrameTimestamps(va_list args) {
+    bool enable = va_arg(args, int);
+    enableFrameTimestamps(enable);
+    return NO_ERROR;
+}
+
+int Surface::dispatchGetCompositorTiming(va_list args) {
+    nsecs_t* compositeDeadline = va_arg(args, int64_t*);
+    nsecs_t* compositeInterval = va_arg(args, int64_t*);
+    nsecs_t* compositeToPresentLatency = va_arg(args, int64_t*);
+    return getCompositorTiming(compositeDeadline, compositeInterval,
+            compositeToPresentLatency);
+}
+
 int Surface::dispatchGetFrameTimestamps(va_list args) {
-    uint32_t framesAgo = va_arg(args, uint32_t);
-    nsecs_t* outPostedTime = va_arg(args, int64_t*);
+    uint64_t frameId = va_arg(args, uint64_t);
+    nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*);
     nsecs_t* outAcquireTime = va_arg(args, int64_t*);
-    nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
-    nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
+    nsecs_t* outLatchTime = va_arg(args, int64_t*);
+    nsecs_t* outFirstRefreshStartTime = va_arg(args, int64_t*);
+    nsecs_t* outLastRefreshStartTime = va_arg(args, int64_t*);
+    nsecs_t* outGpuCompositionDoneTime = va_arg(args, int64_t*);
+    nsecs_t* outDisplayPresentTime = va_arg(args, int64_t*);
     nsecs_t* 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(frameId,
+            outRequestedPresentTime, outAcquireTime, outLatchTime,
+            outFirstRefreshStartTime, outLastRefreshStartTime,
+            outGpuCompositionDoneTime, outDisplayPresentTime,
+            outDisplayRetireTime, outDequeueReadyTime, outReleaseTime);
+}
+
+int Surface::dispatchGetWideColorSupport(va_list args) {
+    bool* outSupport = va_arg(args, bool*);
+    return getWideColorSupport(outSupport);
+}
+
+int Surface::dispatchGetHdrSupport(va_list args) {
+    bool* outSupport = va_arg(args, bool*);
+    return getHdrSupport(outSupport);
 }
 
 int Surface::connect(int api) {
@@ -819,17 +1118,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;
@@ -1356,70 +1654,4 @@
     return mGraphicBufferProducer->getUniqueId(outId);
 }
 
-namespace view {
-
-status_t Surface::writeToParcel(Parcel* parcel) const {
-    return writeToParcel(parcel, false);
-}
-
-status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
-    if (parcel == nullptr) return BAD_VALUE;
-
-    status_t res = OK;
-
-    if (!nameAlreadyWritten) {
-        res = parcel->writeString16(name);
-        if (res != OK) return res;
-
-        /* isSingleBuffered defaults to no */
-        res = parcel->writeInt32(0);
-        if (res != OK) return res;
-    }
-
-    res = parcel->writeStrongBinder(
-            IGraphicBufferProducer::asBinder(graphicBufferProducer));
-
-    return res;
-}
-
-status_t Surface::readFromParcel(const Parcel* parcel) {
-    return readFromParcel(parcel, false);
-}
-
-status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
-    if (parcel == nullptr) return BAD_VALUE;
-
-    status_t res = OK;
-    if (!nameAlreadyRead) {
-        name = readMaybeEmptyString16(parcel);
-        // Discard this for now
-        int isSingleBuffered;
-        res = parcel->readInt32(&isSingleBuffered);
-        if (res != OK) {
-            return res;
-        }
-    }
-
-    sp<IBinder> binder;
-
-    res = parcel->readStrongBinder(&binder);
-    if (res != OK) return res;
-
-    graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
-
-    return OK;
-}
-
-String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
-    size_t len;
-    const char16_t* str = parcel->readString16Inplace(&len);
-    if (str != nullptr) {
-        return String16(str, len);
-    } else {
-        return String16();
-    }
-}
-
-} // namespace view
-
 }; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 43506e9..088933a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -26,17 +26,18 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 
-#include <binder/IMemory.h>
 #include <binder/IServiceManager.h>
 
 #include <system/graphics.h>
 
 #include <ui/DisplayInfo.h>
 
+#include <gui/BufferItemConsumer.h>
 #include <gui/CpuConsumer.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
+#include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 
 #include <private/gui/ComposerService.h>
@@ -129,6 +130,8 @@
     void openGlobalTransactionImpl();
     void closeGlobalTransactionImpl(bool synchronous);
     void setAnimationTransactionImpl();
+    status_t enableVSyncInjectionsImpl(bool enable);
+    status_t injectVSyncImpl(nsecs_t when);
 
     layer_state_t* getLayerStateLocked(
             const sp<SurfaceComposerClient>& client, const sp<IBinder>& id);
@@ -145,7 +148,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(
@@ -154,7 +157,7 @@
     status_t setAlpha(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
             float alpha);
     status_t setMatrix(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
-            float dsdx, float dtdx, float dsdy, float dtdy);
+            float dsdx, float dtdx, float dtdy, float dsdy);
     status_t setOrientation(int orientation);
     status_t setCrop(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
             const Rect& crop);
@@ -165,6 +168,14 @@
     status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, const sp<IBinder>& handle,
             uint64_t frameNumber);
+    status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id, const sp<Surface>& barrierSurface,
+            uint64_t frameNumber);
+    status_t reparentChildren(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id,
+            const sp<IBinder>& newParentHandle);
+    status_t detachChildren(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id);
     status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, int32_t overrideScalingMode);
     status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client,
@@ -190,6 +201,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 +272,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 +333,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)
@@ -368,7 +397,7 @@
 
 status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client,
         const sp<IBinder>& id, float dsdx, float dtdx,
-        float dsdy, float dtdy) {
+        float dtdy, float dsdy) {
     Mutex::Autolock _l(mLock);
     layer_state_t* s = getLayerStateLocked(client, id);
     if (!s)
@@ -415,11 +444,51 @@
         return BAD_INDEX;
     }
     s->what |= layer_state_t::eDeferTransaction;
-    s->handle = handle;
+    s->barrierHandle = handle;
     s->frameNumber = frameNumber;
     return NO_ERROR;
 }
 
+status_t Composer::deferTransactionUntil(
+        const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
+        const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+    Mutex::Autolock lock(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+    s->what |= layer_state_t::eDeferTransaction;
+    s->barrierGbp = barrierSurface->getIGraphicBufferProducer();
+    s->frameNumber = frameNumber;
+    return NO_ERROR;
+}
+
+status_t Composer::reparentChildren(
+        const sp<SurfaceComposerClient>& client,
+        const sp<IBinder>& id,
+        const sp<IBinder>& newParentHandle) {
+    Mutex::Autolock lock(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+    s->what |= layer_state_t::eReparentChildren;
+    s->reparentHandle = newParentHandle;
+    return NO_ERROR;
+}
+
+status_t Composer::detachChildren(
+        const sp<SurfaceComposerClient>& client,
+        const sp<IBinder>& id) {
+    Mutex::Autolock lock(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+    s->what |= layer_state_t::eDetachChildren;
+    return NO_ERROR;
+}
+
 status_t Composer::setOverrideScalingMode(
         const sp<SurfaceComposerClient>& client,
         const sp<IBinder>& id, int32_t overrideScalingMode) {
@@ -529,10 +598,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 +652,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 +737,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 +764,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);
 }
 
@@ -706,8 +799,8 @@
 }
 
 status_t SurfaceComposerClient::setMatrix(const sp<IBinder>& id, float dsdx, float dtdx,
-        float dsdy, float dtdy) {
-    return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy);
+        float dtdy, float dsdy) {
+    return getComposer().setMatrix(this, id, dsdx, dtdx, dtdy, dsdy);
 }
 
 status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
@@ -715,6 +808,20 @@
     return getComposer().deferTransactionUntil(this, id, handle, frameNumber);
 }
 
+status_t SurfaceComposerClient::deferTransactionUntil(const sp<IBinder>& id,
+        const sp<Surface>& barrierSurface, uint64_t frameNumber) {
+    return getComposer().deferTransactionUntil(this, id, barrierSurface, frameNumber);
+}
+
+status_t SurfaceComposerClient::reparentChildren(const sp<IBinder>& id,
+        const sp<IBinder>& newParentHandle) {
+    return getComposer().reparentChildren(this, id, newParentHandle);
+}
+
+status_t SurfaceComposerClient::detachChildren(const sp<IBinder>& id) {
+    return getComposer().detachChildren(this, id);
+}
+
 status_t SurfaceComposerClient::setOverrideScalingMode(
         const sp<IBinder>& id, int32_t overrideScalingMode) {
     return getComposer().setOverrideScalingMode(
@@ -824,13 +931,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 +986,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 +1013,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 +1022,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..1e69379 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);
@@ -147,10 +147,10 @@
     if (err < 0) return err;
     return mClient->setAlpha(mHandle, alpha);
 }
-status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
     status_t err = validate();
     if (err < 0) return err;
-    return mClient->setMatrix(mHandle, dsdx, dtdx, dsdy, dtdy);
+    return mClient->setMatrix(mHandle, dsdx, dtdx, dtdy, dsdy);
 }
 status_t SurfaceControl::setCrop(const Rect& crop) {
     status_t err = validate();
@@ -163,13 +163,32 @@
     return mClient->setFinalCrop(mHandle, crop);
 }
 
-status_t SurfaceControl::deferTransactionUntil(sp<IBinder> handle,
+status_t SurfaceControl::deferTransactionUntil(const sp<IBinder>& handle,
         uint64_t frameNumber) {
     status_t err = validate();
     if (err < 0) return err;
     return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
 }
 
+status_t SurfaceControl::deferTransactionUntil(const sp<Surface>& handle,
+        uint64_t frameNumber) {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
+}
+
+status_t SurfaceControl::reparentChildren(const sp<IBinder>& newParentHandle) {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->reparentChildren(mHandle, newParentHandle);
+}
+
+status_t SurfaceControl::detachChildren() {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->detachChildren(mHandle);
+}
+
 status_t SurfaceControl::setOverrideScalingMode(int32_t overrideScalingMode) {
     status_t err = validate();
     if (err < 0) return err;
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 3c7958f..4492a08 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -14,8 +14,6 @@
         "GLTest.cpp",
         "IGraphicBufferProducer_test.cpp",
         "MultiTextureConsumer_test.cpp",
-        "Sensor_test.cpp",
-        "SRGB_test.cpp",
         "StreamSplitter_test.cpp",
         "SurfaceTextureClient_test.cpp",
         "SurfaceTextureFBO_test.cpp",
@@ -35,8 +33,8 @@
         "libbinder",
         "libcutils",
         "libgui",
-        "libsync",
         "libui",
         "libutils",
+        "libnativewindow"
     ],
 }
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/DummyConsumer.h b/libs/gui/tests/DummyConsumer.h
index 0511e16..502bdf9 100644
--- a/libs/gui/tests/DummyConsumer.h
+++ b/libs/gui/tests/DummyConsumer.h
@@ -19,9 +19,9 @@
 namespace android {
 
 struct DummyConsumer : public BnConsumerListener {
-    virtual void onFrameAvailable(const BufferItem& /* item */) {}
-    virtual void onBuffersReleased() {}
-    virtual void onSidebandStreamChanged() {}
+    void onFrameAvailable(const BufferItem& /* item */) override {}
+    void onBuffersReleased() override {}
+    void onSidebandStreamChanged() override {}
 };
 
 } // namespace android
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 9f33047..aa071f6 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "IGraphicBufferProducer_test"
 //#define LOG_NDEBUG 0
 
+#include "DummyConsumer.h"
+
 #include <gtest/gtest.h>
 
 #include <utils/String8.h>
@@ -64,12 +66,6 @@
     const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
 }; // namespace anonymous
 
-struct DummyConsumer : public BnConsumerListener {
-    virtual void onFrameAvailable(const BufferItem& /* item */) {}
-    virtual void onBuffersReleased() {}
-    virtual void onSidebandStreamChanged() {}
-};
-
 class IGraphicBufferProducerTest : public ::testing::Test {
 protected:
 
@@ -196,7 +192,7 @@
     };
 
     status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
-        return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage);
+        return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, nullptr);
     }
 
     void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence,
@@ -210,7 +206,7 @@
 
         ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (mProducer->dequeueBuffer(slot, fence, DEFAULT_WIDTH,
-                DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+                DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr)));
 
         EXPECT_LE(0, *slot);
         EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, *slot);
@@ -349,7 +345,7 @@
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
             (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS)));
+                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
 
     EXPECT_LE(0, dequeuedSlot);
     EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
@@ -366,20 +362,12 @@
     ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
 
     {
-        uint32_t width;
-        uint32_t height;
-        uint32_t transformHint;
-        uint32_t numPendingBuffers;
-        uint64_t nextFrameNumber;
-
-        output.deflate(&width, &height, &transformHint, &numPendingBuffers,
-                &nextFrameNumber);
-
-        EXPECT_EQ(DEFAULT_WIDTH, width);
-        EXPECT_EQ(DEFAULT_HEIGHT, height);
-        EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
-        EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
-        EXPECT_EQ(2u, nextFrameNumber);
+        EXPECT_EQ(DEFAULT_WIDTH, output.width);
+        EXPECT_EQ(DEFAULT_HEIGHT, output.height);
+        EXPECT_EQ(DEFAULT_TRANSFORM_HINT, output.transformHint);
+        // Since queueBuffer was called exactly once
+        EXPECT_EQ(1u, output.numPendingBuffers);
+        EXPECT_EQ(2u, output.nextFrameNumber);
     }
 
     // Buffer was not in the dequeued state
@@ -416,7 +404,7 @@
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
             (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS)));
+                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
 
     // Slot was enqueued without requesting a buffer
     {
@@ -485,7 +473,7 @@
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
             (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                      DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                                     TEST_PRODUCER_USAGE_BITS)));
+                                     TEST_PRODUCER_USAGE_BITS, nullptr)));
 
     // No return code, but at least test that it doesn't blow up...
     // TODO: add a return code
@@ -534,7 +522,7 @@
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                          DEFAULT_WIDTH, DEFAULT_HEIGHT,
                                          DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS)))
+                                         TEST_PRODUCER_USAGE_BITS, nullptr)))
                 << "iteration: " << i << ", slot: " << dequeuedSlot;
     }
 
@@ -571,7 +559,7 @@
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                                          DEFAULT_WIDTH, DEFAULT_HEIGHT,
                                          DEFAULT_FORMAT,
-                                         TEST_PRODUCER_USAGE_BITS)))
+                                         TEST_PRODUCER_USAGE_BITS, nullptr)))
                 << "slot: " << dequeuedSlot;
     }
 
@@ -606,7 +594,8 @@
         ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                 DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                TEST_PRODUCER_USAGE_BITS))) << "slot : " << dequeuedSlot;
+                TEST_PRODUCER_USAGE_BITS, nullptr)))
+                << "slot : " << dequeuedSlot;
         ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
         ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
     }
@@ -622,7 +611,8 @@
         ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
                 DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
-                TEST_PRODUCER_USAGE_BITS))) << "slot: " << dequeuedSlot;
+                TEST_PRODUCER_USAGE_BITS, nullptr)))
+                << "slot: " << dequeuedSlot;
     }
 
     // Abandon buffer queue
@@ -639,7 +629,7 @@
     sp<Fence> fence;
 
     ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
-            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS));
+            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS, nullptr));
 }
 
 TEST_F(IGraphicBufferProducerTest,
@@ -659,7 +649,8 @@
 
     ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
             (mProducer->dequeueBuffer(&slot, &fence, DEFAULT_WIDTH,
-            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS)));
+            DEFAULT_HEIGHT, DEFAULT_FORMAT, TEST_PRODUCER_USAGE_BITS,
+            nullptr)));
 
     EXPECT_LE(0, slot);
     EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, slot);
diff --git a/libs/gui/tests/SRGB_test.cpp b/libs/gui/tests/SRGB_test.cpp
deleted file mode 100644
index c2640cd..0000000
--- a/libs/gui/tests/SRGB_test.cpp
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SRGB_test"
-//#define LOG_NDEBUG 0
-
-// Ignore for this file because it flags every instance of
-// ASSERT_EQ(GL_NO_ERROR, glGetError());
-#pragma clang diagnostic ignored "-Wsign-compare"
-
-#include "GLTest.h"
-
-#include <math.h>
-
-#include <gui/CpuConsumer.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-
-#include <android/native_window.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-
-class SRGBTest : public ::testing::Test {
-protected:
-    // Class constants
-    enum {
-        DISPLAY_WIDTH = 512,
-        DISPLAY_HEIGHT = 512,
-        PIXEL_SIZE = 4, // bytes or components
-        DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT * PIXEL_SIZE,
-        ALPHA_VALUE = 223, // should be in [0, 255]
-        TOLERANCE = 1,
-    };
-    static const char SHOW_DEBUG_STRING[];
-
-    SRGBTest() :
-            mInputSurface(), mCpuConsumer(), mLockedBuffer(),
-            mEglDisplay(EGL_NO_DISPLAY), mEglConfig(),
-            mEglContext(EGL_NO_CONTEXT), mEglSurface(EGL_NO_SURFACE),
-            mComposerClient(), mSurfaceControl(), mOutputSurface() {
-    }
-
-    virtual ~SRGBTest() {
-        if (mEglDisplay != EGL_NO_DISPLAY) {
-            if (mEglSurface != EGL_NO_SURFACE) {
-                eglDestroySurface(mEglDisplay, mEglSurface);
-            }
-            if (mEglContext != EGL_NO_CONTEXT) {
-                eglDestroyContext(mEglDisplay, mEglContext);
-            }
-            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
-                    EGL_NO_CONTEXT);
-            eglTerminate(mEglDisplay);
-        }
-    }
-
-    virtual void SetUp() {
-        sp<IGraphicBufferProducer> producer;
-        sp<IGraphicBufferConsumer> consumer;
-        BufferQueue::createBufferQueue(&producer, &consumer);
-        ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize(
-                DISPLAY_WIDTH, DISPLAY_HEIGHT));
-        mCpuConsumer = new CpuConsumer(consumer, 1);
-        String8 name("CpuConsumer_for_SRGBTest");
-        mCpuConsumer->setName(name);
-        mInputSurface = new Surface(producer);
-
-        ASSERT_NO_FATAL_FAILURE(createEGLSurface(mInputSurface.get()));
-        ASSERT_NO_FATAL_FAILURE(createDebugSurface());
-    }
-
-    virtual void TearDown() {
-        ASSERT_NO_FATAL_FAILURE(copyToDebugSurface());
-        ASSERT_TRUE(mLockedBuffer.data != NULL);
-        ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
-    }
-
-    static float linearToSRGB(float l) {
-        if (l <= 0.0031308f) {
-            return l * 12.92f;
-        } else {
-            return 1.055f * pow(l, (1 / 2.4f)) - 0.055f;
-        }
-    }
-
-    static float srgbToLinear(float s) {
-        if (s <= 0.04045) {
-            return s / 12.92f;
-        } else {
-            return pow(((s + 0.055f) / 1.055f), 2.4f);
-        }
-    }
-
-    static uint8_t srgbToLinear(uint8_t u) {
-        float f = u / 255.0f;
-        return static_cast<uint8_t>(srgbToLinear(f) * 255.0f + 0.5f);
-    }
-
-    void fillTexture(bool writeAsSRGB) {
-        uint8_t* textureData = new uint8_t[DISPLAY_SIZE];
-
-        for (int y = 0; y < DISPLAY_HEIGHT; ++y) {
-            for (int x = 0; x < DISPLAY_WIDTH; ++x) {
-                float realValue = static_cast<float>(x) / (DISPLAY_WIDTH - 1);
-                realValue *= ALPHA_VALUE / 255.0f; // Premultiply by alpha
-                if (writeAsSRGB) {
-                    realValue = linearToSRGB(realValue);
-                }
-
-                int offset = (y * DISPLAY_WIDTH + x) * PIXEL_SIZE;
-                for (int c = 0; c < 3; ++c) {
-                    uint8_t intValue = static_cast<uint8_t>(
-                            realValue * 255.0f + 0.5f);
-                    textureData[offset + c] = intValue;
-                }
-                textureData[offset + 3] = ALPHA_VALUE;
-            }
-        }
-
-        glTexImage2D(GL_TEXTURE_2D, 0, writeAsSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8,
-                DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE,
-                textureData);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-
-        delete[] textureData;
-    }
-
-    void initShaders() {
-        static const char vertexSource[] =
-            "attribute vec4 vPosition;\n"
-            "varying vec2 texCoords;\n"
-            "void main() {\n"
-            "  texCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
-            "  gl_Position = vPosition;\n"
-            "}\n";
-
-        static const char fragmentSource[] =
-            "precision mediump float;\n"
-            "uniform sampler2D texSampler;\n"
-            "varying vec2 texCoords;\n"
-            "void main() {\n"
-            "  gl_FragColor = texture2D(texSampler, texCoords);\n"
-            "}\n";
-
-        GLuint program;
-        {
-            SCOPED_TRACE("Creating shader program");
-            ASSERT_NO_FATAL_FAILURE(GLTest::createProgram(
-                    vertexSource, fragmentSource, &program));
-        }
-
-        GLint positionHandle = glGetAttribLocation(program, "vPosition");
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        ASSERT_NE(-1, positionHandle);
-
-        GLint samplerHandle = glGetUniformLocation(program, "texSampler");
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        ASSERT_NE(-1, samplerHandle);
-
-        static const GLfloat vertices[] = {
-            -1.0f, 1.0f,
-            -1.0f, -1.0f,
-            1.0f, -1.0f,
-            1.0f, 1.0f,
-        };
-
-        glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glEnableVertexAttribArray(positionHandle);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-
-        glUseProgram(program);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glUniform1i(samplerHandle, 0);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-
-        GLuint textureHandle;
-        glGenTextures(1, &textureHandle);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glBindTexture(GL_TEXTURE_2D, textureHandle);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-    }
-
-    void drawTexture(bool asSRGB, GLint x, GLint y, GLsizei width,
-            GLsizei height) {
-        ASSERT_NO_FATAL_FAILURE(fillTexture(asSRGB));
-        glViewport(x, y, width, height);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-        ASSERT_EQ(GL_NO_ERROR, glGetError());
-    }
-
-    void checkLockedBuffer(PixelFormat format, android_dataspace dataSpace) {
-        ASSERT_EQ(mLockedBuffer.format, format);
-        ASSERT_EQ(mLockedBuffer.width, DISPLAY_WIDTH);
-        ASSERT_EQ(mLockedBuffer.height, DISPLAY_HEIGHT);
-        ASSERT_EQ(mLockedBuffer.dataSpace, dataSpace);
-    }
-
-    static bool withinTolerance(int a, int b) {
-        int diff = a - b;
-        return diff >= 0 ? diff <= TOLERANCE : -diff <= TOLERANCE;
-    }
-
-    // Primary producer and consumer
-    sp<Surface> mInputSurface;
-    sp<CpuConsumer> mCpuConsumer;
-    CpuConsumer::LockedBuffer mLockedBuffer;
-
-    EGLDisplay mEglDisplay;
-    EGLConfig mEglConfig;
-    EGLContext mEglContext;
-    EGLSurface mEglSurface;
-
-    // Auxiliary display output
-    sp<SurfaceComposerClient> mComposerClient;
-    sp<SurfaceControl> mSurfaceControl;
-    sp<Surface> mOutputSurface;
-
-private:
-    void createEGLSurface(Surface* inputSurface) {
-        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
-
-        EXPECT_TRUE(eglInitialize(mEglDisplay, NULL, NULL));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-        static const EGLint configAttribs[] = {
-            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
-            EGL_RED_SIZE, 8,
-            EGL_GREEN_SIZE, 8,
-            EGL_BLUE_SIZE, 8,
-            EGL_ALPHA_SIZE, 8,
-            EGL_NONE };
-
-        EGLint numConfigs = 0;
-        EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1,
-                &numConfigs));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_GT(numConfigs, 0);
-
-        static const EGLint contextAttribs[] = {
-            EGL_CONTEXT_CLIENT_VERSION, 3,
-            EGL_NONE } ;
-
-        mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
-                contextAttribs);
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
-
-        mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
-                inputSurface, NULL);
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
-        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
-                mEglContext));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    }
-
-    void createDebugSurface() {
-        if (getenv(SHOW_DEBUG_STRING) == NULL) return;
-
-        mComposerClient = new SurfaceComposerClient;
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        mSurfaceControl = mComposerClient->createSurface(
-                String8("SRGBTest Surface"), DISPLAY_WIDTH, DISPLAY_HEIGHT,
-                PIXEL_FORMAT_RGBA_8888);
-
-        ASSERT_TRUE(mSurfaceControl != NULL);
-        ASSERT_TRUE(mSurfaceControl->isValid());
-
-        SurfaceComposerClient::openGlobalTransaction();
-        ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
-        ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
-        SurfaceComposerClient::closeGlobalTransaction();
-
-        ANativeWindow_Buffer outBuffer;
-        ARect inOutDirtyBounds;
-        mOutputSurface = mSurfaceControl->getSurface();
-        mOutputSurface->lock(&outBuffer, &inOutDirtyBounds);
-        uint8_t* bytePointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
-        for (int y = 0; y < outBuffer.height; ++y) {
-            int rowOffset = y * outBuffer.stride; // pixels
-            for (int x = 0; x < outBuffer.width; ++x) {
-                int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
-                for (int c = 0; c < PIXEL_SIZE; ++c) {
-                    int offset = colOffset + c;
-                    bytePointer[offset] = ((c + 1) * 56) - 1;
-                }
-            }
-        }
-        mOutputSurface->unlockAndPost();
-    }
-
-    void copyToDebugSurface() {
-        if (!mOutputSurface.get()) return;
-
-        size_t bufferSize = mLockedBuffer.height * mLockedBuffer.stride *
-                PIXEL_SIZE;
-
-        ANativeWindow_Buffer outBuffer;
-        ARect outBufferBounds;
-        mOutputSurface->lock(&outBuffer, &outBufferBounds);
-        ASSERT_EQ(mLockedBuffer.width, static_cast<uint32_t>(outBuffer.width));
-        ASSERT_EQ(mLockedBuffer.height, static_cast<uint32_t>(outBuffer.height));
-        ASSERT_EQ(mLockedBuffer.stride, static_cast<uint32_t>(outBuffer.stride));
-
-        if (mLockedBuffer.format == outBuffer.format) {
-            memcpy(outBuffer.bits, mLockedBuffer.data, bufferSize);
-        } else {
-            ASSERT_EQ(mLockedBuffer.format, PIXEL_FORMAT_RGBA_8888);
-            ASSERT_EQ(mLockedBuffer.dataSpace, HAL_DATASPACE_SRGB);
-            ASSERT_EQ(outBuffer.format, PIXEL_FORMAT_RGBA_8888);
-            uint8_t* outPointer = reinterpret_cast<uint8_t*>(outBuffer.bits);
-            for (int y = 0; y < outBuffer.height; ++y) {
-                int rowOffset = y * outBuffer.stride; // pixels
-                for (int x = 0; x < outBuffer.width; ++x) {
-                    int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes
-
-                    // RGB are converted
-                    for (int c = 0; c < (PIXEL_SIZE - 1); ++c) {
-                        outPointer[colOffset + c] = srgbToLinear(
-                                mLockedBuffer.data[colOffset + c]);
-                    }
-
-                    // Alpha isn't converted
-                    outPointer[colOffset + 3] =
-                            mLockedBuffer.data[colOffset + 3];
-                }
-            }
-        }
-        mOutputSurface->unlockAndPost();
-
-        int sleepSeconds = atoi(getenv(SHOW_DEBUG_STRING));
-        sleep(sleepSeconds);
-    }
-};
-
-const char SRGBTest::SHOW_DEBUG_STRING[] = "DEBUG_OUTPUT_SECONDS";
-
-TEST_F(SRGBTest, GLRenderFromSRGBTexture) {
-    ASSERT_NO_FATAL_FAILURE(initShaders());
-
-    // The RGB texture is displayed in the top half
-    ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, DISPLAY_HEIGHT / 2,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
-
-    // The SRGB texture is displayed in the bottom half
-    ASSERT_NO_FATAL_FAILURE(drawTexture(true, 0, 0,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT / 2));
-
-    eglSwapBuffers(mEglDisplay, mEglSurface);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Lock
-    ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
-    ASSERT_NO_FATAL_FAILURE(
-        checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
-
-    // Compare a pixel in the middle of each texture
-    int midSRGBOffset = (DISPLAY_HEIGHT / 4) * mLockedBuffer.stride *
-            PIXEL_SIZE;
-    int midRGBOffset = midSRGBOffset * 3;
-    midRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
-    midSRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
-    for (int c = 0; c < PIXEL_SIZE; ++c) {
-        int expectedValue = mLockedBuffer.data[midRGBOffset + c];
-        int actualValue = mLockedBuffer.data[midSRGBOffset + c];
-        ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
-    }
-
-    // mLockedBuffer is unlocked in TearDown so we can copy data from it to
-    // the debug surface if necessary
-}
-
-// XXX: Disabled since we don't currently expect this to work
-TEST_F(SRGBTest, DISABLED_RenderToSRGBSurface) {
-    ASSERT_NO_FATAL_FAILURE(initShaders());
-
-    // By default, the first buffer we write into will be RGB
-
-    // Render an RGB texture across the whole surface
-    ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT));
-    eglSwapBuffers(mEglDisplay, mEglSurface);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Lock
-    ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
-    ASSERT_NO_FATAL_FAILURE(
-        checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_UNKNOWN));
-
-    // Save the values of the middle pixel for later comparison against SRGB
-    uint8_t values[PIXEL_SIZE] = {};
-    int middleOffset = (DISPLAY_HEIGHT / 2) * mLockedBuffer.stride *
-            PIXEL_SIZE;
-    middleOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE;
-    for (int c = 0; c < PIXEL_SIZE; ++c) {
-        values[c] = mLockedBuffer.data[middleOffset + c];
-    }
-
-    // Unlock
-    ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer));
-
-    // Switch to SRGB window surface
-    static const int srgbAttribs[] = {
-        EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
-        EGL_NONE,
-    };
-
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
-            mEglContext));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig,
-            mInputSurface.get(), srgbAttribs);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
-
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
-            mEglContext));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Render the texture again
-    ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT));
-    eglSwapBuffers(mEglDisplay, mEglSurface);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Lock
-    ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer));
-
-    // Make sure we actually got the SRGB buffer on the consumer side
-    ASSERT_NO_FATAL_FAILURE(
-        checkLockedBuffer(PIXEL_FORMAT_RGBA_8888, HAL_DATASPACE_SRGB));
-
-    // Verify that the stored value is the same, accounting for RGB/SRGB
-    for (int c = 0; c < PIXEL_SIZE; ++c) {
-        // The alpha value should be equivalent before linear->SRGB
-        float rgbAsSRGB = (c == 3) ? values[c] / 255.0f :
-                linearToSRGB(values[c] / 255.0f);
-        int expectedValue = rgbAsSRGB * 255.0f + 0.5f;
-        int actualValue = mLockedBuffer.data[middleOffset + c];
-        ASSERT_PRED2(withinTolerance, expectedValue, actualValue);
-    }
-
-    // mLockedBuffer is unlocked in TearDown so we can copy data from it to
-    // the debug surface if necessary
-}
-
-} // namespace android
diff --git a/libs/gui/tests/Sensor_test.cpp b/libs/gui/tests/Sensor_test.cpp
deleted file mode 100644
index fbf282d..0000000
--- a/libs/gui/tests/Sensor_test.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Sensor_test"
-
-#include <gui/Sensor.h>
-#include <hardware/sensors.h>
-#include <utils/Errors.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-
-// Returns true if the two sensors have the same attributes. Does not compare
-// UUID since that should not be transmitted via flatten/unflatten.
-static bool sensorsMatch(const Sensor& a, const Sensor& b) {
-    return a.getName() == b.getName () &&
-        a.getVendor() == b.getVendor () &&
-        a.getHandle() == b.getHandle () &&
-        a.getType() == b.getType () &&
-        a.getMinValue() == b.getMinValue () &&
-        a.getMaxValue() == b.getMaxValue () &&
-        a.getResolution() == b.getResolution () &&
-        a.getPowerUsage() == b.getPowerUsage () &&
-        a.getMinDelay() == b.getMinDelay () &&
-        a.getMinDelayNs() == b.getMinDelayNs () &&
-        a.getVersion() == b.getVersion () &&
-        a.getFifoReservedEventCount() == b.getFifoReservedEventCount () &&
-        a.getFifoMaxEventCount() == b.getFifoMaxEventCount () &&
-        a.getStringType() == b.getStringType () &&
-        a.getRequiredPermission() == b.getRequiredPermission () &&
-        a.isRequiredPermissionRuntime() == b.isRequiredPermissionRuntime () &&
-        a.getRequiredAppOp() == b.getRequiredAppOp () &&
-        a.getMaxDelay() == b.getMaxDelay () &&
-        a.getFlags() == b.getFlags () &&
-        a.isWakeUpSensor() == b.isWakeUpSensor () &&
-        a.isDynamicSensor() == b.isDynamicSensor () &&
-        a.hasAdditionalInfo() == b.hasAdditionalInfo () &&
-        a.getReportingMode() == b.getReportingMode();
-}
-
-// Creates and returns a sensor_t struct with some default values filled in.
-static sensor_t getTestSensorT() {
-    sensor_t hwSensor = {};
-    hwSensor.name = "Test Sensor";
-    hwSensor.vendor = "Test Vendor";
-    hwSensor.version = 1;
-    hwSensor.handle = 2;
-    hwSensor.type = SENSOR_TYPE_ACCELEROMETER;
-    hwSensor.maxRange = 10.f;
-    hwSensor.resolution = 1.f;
-    hwSensor.power = 5.f;
-    hwSensor.minDelay = 1000;
-    hwSensor.fifoReservedEventCount = 50;
-    hwSensor.fifoMaxEventCount = 100;
-    hwSensor.stringType = SENSOR_STRING_TYPE_ACCELEROMETER;
-    hwSensor.requiredPermission = "";
-    hwSensor.maxDelay = 5000;
-    hwSensor.flags = SENSOR_FLAG_CONTINUOUS_MODE;
-    return hwSensor;
-}
-
-TEST(SensorTest, FlattenAndUnflatten) {
-    sensor_t hwSensor = getTestSensorT();
-    Sensor sensor1(&hwSensor, SENSORS_DEVICE_API_VERSION_1_4);
-    Sensor sensor2;
-
-    std::vector<uint8_t> buffer(sensor1.getFlattenedSize());
-    ASSERT_EQ(OK, sensor1.flatten(buffer.data(), buffer.size()));
-    ASSERT_EQ(OK, sensor2.unflatten(buffer.data(), buffer.size()));
-
-    EXPECT_TRUE(sensorsMatch(sensor1, sensor2));
-}
-
-} // namespace android
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 498492e..80e30da 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -81,7 +81,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -115,7 +115,7 @@
     // received the buffer back from the output BufferQueue
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
 }
 
 TEST_F(StreamSplitterTest, OneInputMultipleOutputs) {
@@ -153,7 +153,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     uint32_t* dataIn;
@@ -190,7 +190,7 @@
     // received the buffer back from the output BufferQueues
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
 }
 
 TEST_F(StreamSplitterTest, OutputAbandonment) {
@@ -217,7 +217,7 @@
     sp<GraphicBuffer> buffer;
     ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
             inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-                    GRALLOC_USAGE_SW_WRITE_OFTEN));
+                    GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
     ASSERT_EQ(OK, inputProducer->requestBuffer(slot, &buffer));
 
     // Abandon the output
@@ -230,7 +230,7 @@
 
     // Input should be abandoned
     ASSERT_EQ(NO_INIT, inputProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
-            GRALLOC_USAGE_SW_WRITE_OFTEN));
+            GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr));
 }
 
 } // namespace android
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index b10d4eb..bd598e4 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -23,6 +23,7 @@
 #include <gtest/gtest.h>
 #include <gui/GLConsumer.h>
 #include <gui/Surface.h>
+#include <gui/BufferQueue.h>
 #include <system/graphics.h>
 #include <utils/Log.h>
 #include <utils/Thread.h>
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0de60c9..da6f13d 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -18,19 +18,29 @@
 
 #include <gtest/gtest.h>
 
-#include <binder/IMemory.h>
+#include <binder/ProcessState.h>
+#include <cutils/properties.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/IDisplayEventConnection.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-#include <gui/BufferItemConsumer.h>
+#include <private/gui/ComposerService.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
 
-#include <private/gui/ComposerService.h>
-#include <binder/ProcessState.h>
+#include <limits>
+#include <thread>
 
 namespace android {
 
+using namespace std::chrono_literals;
+
+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;
@@ -232,6 +253,35 @@
     EXPECT_STREQ("TestConsumer", surface->getConsumerName().string());
 }
 
+TEST_F(SurfaceTest, GetWideColorSupport) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+    consumer->consumerConnect(dummyConsumer, false);
+    consumer->setConsumerName(String8("TestConsumer"));
+
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+    native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+
+    bool supported;
+    surface->getWideColorSupport(&supported);
+
+    // TODO(courtneygo): How can we know what device we are on to
+    // verify that this is correct?
+    char product[PROPERTY_VALUE_MAX] = "0";
+    property_get("ro.build.product", product, "0");
+    std::cerr << "[          ] product = " << product << std::endl;
+
+    if (strcmp("marlin", product) == 0 || strcmp("sailfish", product) == 0) {
+        ASSERT_EQ(true, supported);
+    } else {
+        ASSERT_EQ(false, supported);
+    }
+}
+
 TEST_F(SurfaceTest, DynamicSetBufferCount) {
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
@@ -258,4 +308,1237 @@
     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::GPU_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;
+    }
+
+    nsecs_t now() const override {
+        return mNow;
+    }
+
+    void setNow(nsecs_t now) {
+        mNow = now;
+    }
+
+public:
+    sp<FakeSurfaceComposer> mFakeSurfaceComposer;
+    nsecs_t mNow = 0;
+
+    // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory,
+    // but this raw pointer gives access to test functionality.
+    FakeProducerFrameEventHistory* mFakeFrameEventHistory;
+};
+
+
+class GetFrameTimestampsTest : public 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),
+            kCompositorTiming(
+                {refreshStart, refreshStart + 1, refreshStart + 2 }),
+            kStartTime(refreshStart + 3),
+            kGpuCompositionDoneTime(refreshStart + 4),
+            kPresentTime(refreshStart + 5) {}
+
+        void signalPostCompositeFences() {
+            mFenceMap.signalAllForTest(
+                        mGpuCompositionDone.mFence, kGpuCompositionDoneTime);
+            mFenceMap.signalAllForTest(mPresent.mFence, kPresentTime);
+        }
+
+        FenceToFenceTimeMap& mFenceMap;
+
+        FenceAndFenceTime mGpuCompositionDone { mFenceMap };
+        FenceAndFenceTime mPresent { mFenceMap };
+
+        const CompositorTiming kCompositorTiming;
+
+        const nsecs_t kStartTime;
+        const nsecs_t kGpuCompositionDoneTime;
+        const nsecs_t kPresentTime;
+    };
+
+    struct FrameEvents {
+        FrameEvents(FenceToFenceTimeMap& fenceMap, nsecs_t frameStartTime)
+            : mFenceMap(fenceMap),
+              kPostedTime(frameStartTime + 100),
+              kRequestedPresentTime(frameStartTime + 200),
+              kProducerAcquireTime(frameStartTime + 300),
+              kConsumerAcquireTime(frameStartTime + 301),
+              kLatchTime(frameStartTime + 500),
+              kDequeueReadyTime(frameStartTime + 600),
+              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 disableFrameTimestamps() {
+        mFakeConsumer->mGetFrameTimestampsEnabled = false;
+        native_window_enable_frame_timestamps(mWindow.get(), 0);
+        mFrameTimestampsEnabled = false;
+    }
+
+    void enableFrameTimestamps() {
+        mFakeConsumer->mGetFrameTimestampsEnabled = true;
+        native_window_enable_frame_timestamps(mWindow.get(), 1);
+        mFrameTimestampsEnabled = true;
+    }
+
+    int getAllFrameTimestamps(uint64_t frameId) {
+        return native_window_get_frame_timestamps(mWindow.get(), frameId,
+                &outRequestedPresentTime, &outAcquireTime, &outLatchTime,
+                &outFirstRefreshStartTime, &outLastRefreshStartTime,
+                &outGpuCompositionDoneTime, &outDisplayPresentTime,
+                &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;
+    }
+
+    uint64_t getNextFrameId() {
+        uint64_t frameId = -1;
+        int status = native_window_get_next_frame_id(mWindow.get(), &frameId);
+        EXPECT_EQ(status, NO_ERROR);
+        return frameId;
+    }
+
+    void dequeueAndQueue(uint64_t frameIndex) {
+        int fence = -1;
+        ANativeWindowBuffer* buffer = nullptr;
+        ASSERT_EQ(NO_ERROR,
+                mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+
+        int oldAddFrameTimestampsCount =
+                mFakeConsumer->mAddFrameTimestampsCount;
+
+        FrameEvents* frame = &mFrames[frameIndex];
+        uint64_t frameNumber = frameIndex + 1;
+
+        NewFrameEventsEntry fe;
+        fe.frameNumber = frameNumber;
+        fe.postedTime = frame->kPostedTime;
+        fe.requestedPresentTime = frame->kRequestedPresentTime;
+        fe.acquireFence = frame->mAcquireConsumer.mFenceTime;
+        mFakeConsumer->mNewFrameEntryOverride = fe;
+
+        mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+                    frame->mAcquireProducer.mFenceTime,
+                    frame->mAcquireConsumer.mFenceTime);
+
+        ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+
+        EXPECT_EQ(frameNumber, mFakeConsumer->mLastAddedFrameNumber);
+
+        EXPECT_EQ(
+                oldAddFrameTimestampsCount + (mFrameTimestampsEnabled ? 1 : 0),
+                mFakeConsumer->mAddFrameTimestampsCount);
+    }
+
+    void addFrameEvents(
+            bool gpuComposited, uint64_t iOldFrame, int64_t iNewFrame) {
+        FrameEvents* oldFrame =
+                (iOldFrame == NO_FRAME_INDEX) ? nullptr : &mFrames[iOldFrame];
+        FrameEvents* newFrame = &mFrames[iNewFrame];
+
+        uint64_t nOldFrame = iOldFrame + 1;
+        uint64_t nNewFrame = iNewFrame + 1;
+
+        // Latch, Composite, 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,
+                    oldFrame->mRefreshes[2].kCompositorTiming);
+        }
+
+        // Latch the new frame.
+        mCfeh->addLatch(nNewFrame, newFrame->kLatchTime);
+
+        mCfeh->addPreComposition(nNewFrame, newFrame->mRefreshes[0].kStartTime);
+        gpuDoneFenceTime = gpuComposited ?
+                newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime :
+                FenceTime::NO_FENCE;
+        // HWC2 releases the previous buffer after a new latch just before
+        // calling postComposition.
+        if (oldFrame != nullptr) {
+            mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime,
+                    std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime));
+        }
+        mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime,
+                newFrame->mRefreshes[0].mPresent.mFenceTime,
+                newFrame->mRefreshes[0].kCompositorTiming);
+
+        // 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,
+                newFrame->mRefreshes[1].kCompositorTiming);
+    }
+
+    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[3] {
+        { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } };
+};
+
+
+// This test verifies that the frame timestamps are not retrieved when not
+// explicitly enabled via native_window_enable_frame_timestamps.
+// We want to check this to make sure there's no overhead for users
+// that don't need the timestamp information.
+TEST_F(GetFrameTimestampsTest, DefaultDisabled) {
+    int fence;
+    ANativeWindowBuffer* buffer;
+
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    const uint64_t fId = getNextFrameId();
+
+    // Verify the producer doesn't get frame timestamps piggybacked on dequeue.
+    ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify the producer doesn't get frame timestamps piggybacked on queue.
+    // It is okay that frame timestamps are added in the consumer since it is
+    // still needed for SurfaceFlinger dumps.
+    ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+    EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify attempts to get frame timestamps fail.
+    int result = getAllFrameTimestamps(fId);
+    EXPECT_EQ(INVALID_OPERATION, result);
+    EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify compositor timing query fails.
+    nsecs_t compositeDeadline = 0;
+    nsecs_t compositeInterval = 0;
+    nsecs_t compositeToPresentLatency = 0;
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(INVALID_OPERATION, result);
+}
+
+// This test verifies that the frame timestamps are retrieved if explicitly
+// enabled via native_window_enable_frame_timestamps.
+TEST_F(GetFrameTimestampsTest, EnabledSimple) {
+    CompositorTiming initialCompositorTiming {
+        1000000000, // 1s deadline
+        16666667, // 16ms interval
+        50000000, // 50ms present latency
+    };
+    mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+    enableFrameTimestamps();
+
+    // Verify the compositor timing query gets the initial compositor values
+    // after timststamps are enabled; even before the first frame is queued
+    // or dequeued.
+    nsecs_t compositeDeadline = 0;
+    nsecs_t compositeInterval = 0;
+    nsecs_t compositeToPresentLatency = 0;
+    mSurface->setNow(initialCompositorTiming.deadline - 1);
+    int result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+    EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+    EXPECT_EQ(initialCompositorTiming.presentLatency,
+              compositeToPresentLatency);
+
+    int fence;
+    ANativeWindowBuffer* buffer;
+
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount);
+
+    const uint64_t fId1 = getNextFrameId();
+
+    // Verify getFrameTimestamps is piggybacked on dequeue.
+    ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
+    EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount);
+
+    NewFrameEventsEntry f1;
+    f1.frameNumber = 1;
+    f1.postedTime = mFrames[0].kPostedTime;
+    f1.requestedPresentTime = mFrames[0].kRequestedPresentTime;
+    f1.acquireFence = mFrames[0].mAcquireConsumer.mFenceTime;
+    mSurface->mFakeFrameEventHistory->setAcquireFenceOverride(
+            mFrames[0].mAcquireProducer.mFenceTime,
+            mFrames[0].mAcquireConsumer.mFenceTime);
+    mFakeConsumer->mNewFrameEntryOverride = f1;
+    mFrames[0].signalQueueFences();
+
+    // Verify getFrameTimestamps is piggybacked on queue.
+    ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence));
+    EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount);
+    EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber);
+    EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount);
+
+    // Verify queries for timestamps that the producer doesn't know about
+    // triggers a call to see if the consumer has any new timestamps.
+    result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount);
+}
+
+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);
+}
+
+TEST_F(GetFrameTimestampsTest, SnapToNextTickBasic) {
+    nsecs_t phase = 4000;
+    nsecs_t interval = 1000;
+
+    // Timestamp in previous interval.
+    nsecs_t timestamp = 3500;
+    EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp in next interval.
+    timestamp = 4500;
+    EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp multiple intervals before.
+    timestamp = 2500;
+    EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp multiple intervals after.
+    timestamp = 6500;
+    EXPECT_EQ(7000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp on previous interval.
+    timestamp = 3000;
+    EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp on next interval.
+    timestamp = 5000;
+    EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+
+    // Timestamp equal to phase.
+    timestamp = 4000;
+    EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick(
+            timestamp, phase, interval));
+}
+
+// int(big_timestamp / interval) < 0, which can cause a crash or invalid result
+// if the number of intervals elapsed is internally stored in an int.
+TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) {
+      nsecs_t phase = 0;
+      nsecs_t interval = 4000;
+      nsecs_t big_timestamp = 8635916564000;
+      int32_t intervals = big_timestamp / interval;
+
+      EXPECT_LT(intervals, 0);
+      EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick(
+            big_timestamp, phase, interval));
+      EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick(
+            big_timestamp, big_timestamp, interval));
+}
+
+// This verifies the compositor timing is updated by refresh events
+// and piggy backed on a queue, dequeue, and enabling of timestamps..
+TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) {
+    CompositorTiming initialCompositorTiming {
+        1000000000, // 1s deadline
+        16666667, // 16ms interval
+        50000000, // 50ms present latency
+    };
+    mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+    enableFrameTimestamps();
+
+    // We get the initial values before any frames are submitted.
+    nsecs_t compositeDeadline = 0;
+    nsecs_t compositeInterval = 0;
+    nsecs_t compositeToPresentLatency = 0;
+    mSurface->setNow(initialCompositorTiming.deadline - 1);
+    int result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+    EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+    EXPECT_EQ(initialCompositorTiming.presentLatency,
+              compositeToPresentLatency);
+
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+
+    // Still get the initial values because the frame events for frame 0
+    // didn't get a chance to piggyback on a queue or dequeue yet.
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+    EXPECT_EQ(initialCompositorTiming.interval, compositeInterval);
+    EXPECT_EQ(initialCompositorTiming.presentLatency,
+              compositeToPresentLatency);
+
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    addFrameEvents(true, 0, 1);
+
+    // Now expect the composite values associated with frame 1.
+    mSurface->setNow(mFrames[0].mRefreshes[1].kCompositorTiming.deadline);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.deadline,
+            compositeDeadline);
+    EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.interval,
+            compositeInterval);
+    EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.presentLatency,
+            compositeToPresentLatency);
+
+    dequeueAndQueue(2);
+    addFrameEvents(true, 1, 2);
+
+    // Now expect the composite values associated with frame 2.
+    mSurface->setNow(mFrames[1].mRefreshes[1].kCompositorTiming.deadline);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.deadline,
+            compositeDeadline);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.interval,
+            compositeInterval);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.presentLatency,
+            compositeToPresentLatency);
+
+    // Re-enabling frame timestamps should get the latest values.
+    disableFrameTimestamps();
+    enableFrameTimestamps();
+
+    // Now expect the composite values associated with frame 3.
+    mSurface->setNow(mFrames[2].mRefreshes[1].kCompositorTiming.deadline);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.deadline,
+            compositeDeadline);
+    EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.interval,
+            compositeInterval);
+    EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.presentLatency,
+            compositeToPresentLatency);
+}
+
+// This verifies the compositor deadline properly snaps to the the next
+// deadline based on the current time.
+TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) {
+    CompositorTiming initialCompositorTiming {
+        1000000000, // 1s deadline
+        16666667, // 16ms interval
+        50000000, // 50ms present latency
+    };
+    mCfeh->initializeCompositorTiming(initialCompositorTiming);
+
+    enableFrameTimestamps();
+
+    nsecs_t compositeDeadline = 0;
+    nsecs_t compositeInterval = 0;
+    nsecs_t compositeToPresentLatency = 0;
+
+    // A "now" just before the deadline snaps to the deadline.
+    mSurface->setNow(initialCompositorTiming.deadline - 1);
+    int result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline);
+    nsecs_t expectedDeadline = initialCompositorTiming.deadline;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+
+    // A "now" just after the deadline snaps properly.
+    mSurface->setNow(initialCompositorTiming.deadline + 1);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    expectedDeadline =
+            initialCompositorTiming.deadline +initialCompositorTiming.interval;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    addFrameEvents(true, 0, 1);
+
+    // A "now" just after the next interval snaps properly.
+    mSurface->setNow(
+            mFrames[0].mRefreshes[1].kCompositorTiming.deadline +
+            mFrames[0].mRefreshes[1].kCompositorTiming.interval + 1);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    expectedDeadline =
+            mFrames[0].mRefreshes[1].kCompositorTiming.deadline +
+            mFrames[0].mRefreshes[1].kCompositorTiming.interval * 2;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+    dequeueAndQueue(2);
+    addFrameEvents(true, 1, 2);
+
+    // A "now" over 1 interval before the deadline snaps properly.
+    mSurface->setNow(
+            mFrames[1].mRefreshes[1].kCompositorTiming.deadline -
+            mFrames[1].mRefreshes[1].kCompositorTiming.interval - 1);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    expectedDeadline =
+            mFrames[1].mRefreshes[1].kCompositorTiming.deadline -
+            mFrames[1].mRefreshes[1].kCompositorTiming.interval;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+
+    // Re-enabling frame timestamps should get the latest values.
+    disableFrameTimestamps();
+    enableFrameTimestamps();
+
+    // A "now" over 2 intervals before the deadline snaps properly.
+    mSurface->setNow(
+            mFrames[2].mRefreshes[1].kCompositorTiming.deadline -
+            mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2 - 1);
+    result = native_window_get_compositor_timing(mWindow.get(),
+        &compositeDeadline, &compositeInterval, &compositeToPresentLatency);
+    EXPECT_EQ(NO_ERROR, result);
+    expectedDeadline =
+            mFrames[2].mRefreshes[1].kCompositorTiming.deadline -
+            mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2;
+    EXPECT_EQ(expectedDeadline, compositeDeadline);
+}
+
+// This verifies the timestamps recorded in the consumer's
+// FrameTimestampsHistory are properly retrieved by the producer for the
+// correct frames.
+TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) {
+    enableFrameTimestamps();
+
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+    mFrames[0].signalRefreshFences();
+    addFrameEvents(true, 0, 1);
+    mFrames[0].signalReleaseFences();
+    mFrames[1].signalRefreshFences();
+
+    // Verify timestamps are correct for frame 1.
+    resetTimestamps();
+    int result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+            outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].kRetireTime, outDisplayRetireTime);
+    EXPECT_EQ(mFrames[0].kDequeueReadyTime, outDequeueReadyTime);
+    EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
+
+    // Verify timestamps are correct for frame 2.
+    resetTimestamps();
+    result = getAllFrameTimestamps(fId2);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kGpuCompositionDoneTime,
+            outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(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);
+
+    // Dequeue and queue frame 1.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+
+    // Verify queue-related timestamps for f1 are available immediately in the
+    // producer without asking the consumer again, even before signaling the
+    // acquire fence.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr, nullptr, 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(), fId1,
+            &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.
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+
+    // Verify queue-related timestamps for f2 are available immediately in the
+    // producer without asking the consumer again, even before signaling the
+    // acquire fence.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+            &outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
+            nullptr, nullptr, nullptr, nullptr, nullptr, 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(), fId2,
+            &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.
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+    mFrames[0].signalRefreshFences();
+    addFrameEvents(true, 0, 1);
+    mFrames[0].signalReleaseFences();
+    mFrames[1].signalRefreshFences();
+
+    // Verify a request for no timestamps doesn't result in a sync call.
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = native_window_get_frame_timestamps(mWindow.get(), fId2,
+            nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+            nullptr, nullptr, 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.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    // Dequeue and queue frame 2.
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(true, NO_FRAME_INDEX, 0);
+    addFrameEvents(true, 0, 1);
+
+    // Verify available timestamps are correct for frame 1, before any
+    // fence has been signaled.
+    // Note: A sync call is necessary here since the events triggered by
+    // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(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.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(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.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kGpuCompositionDoneTime,
+            outGpuCompositionDoneTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kPresentTime, outDisplayPresentTime);
+    EXPECT_EQ(mFrames[0].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);
+
+    // Dequeue and queue frame 1.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    // Dequeue and queue frame 2.
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(false, NO_FRAME_INDEX, 0);
+    addFrameEvents(false, 0, 1);
+
+    // Verify available timestamps are correct for frame 1, before any
+    // fence has been signaled.
+    // Note: A sync call is necessary here since the events triggered by
+    // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(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(fId1);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(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.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+    mFrames[0].signalQueueFences();
+
+    // Dequeue and queue frame 2.
+    const uint64_t fId2 = getNextFrameId();
+    dequeueAndQueue(1);
+    mFrames[1].signalQueueFences();
+
+    addFrameEvents(false, NO_FRAME_INDEX, 0);
+    addFrameEvents(false, 0, 1);
+
+    // Verify available timestamps are correct for frame 1, before any
+    // fence has been signaled.
+    // Note: A sync call is necessary here since the events triggered by
+    // addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = getAllFrameTimestamps(fId1);
+    EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[0].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[0].mRefreshes[2].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(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.
+    resetTimestamps();
+    oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    result = getAllFrameTimestamps(fId2);
+    EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
+    EXPECT_EQ(NO_ERROR, result);
+    EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
+    EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
+    EXPECT_EQ(mFrames[1].kLatchTime, outLatchTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[0].kStartTime, outFirstRefreshStartTime);
+    EXPECT_EQ(mFrames[1].mRefreshes[1].kStartTime, outLastRefreshStartTime);
+    EXPECT_EQ(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.
+    const uint64_t fId1 = getNextFrameId();
+    dequeueAndQueue(0);
+
+    // Verify a query for the Present and Retire times do not trigger
+    // a sync call if they are not supported.
+    resetTimestamps();
+    int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
+    int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
+            nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
+            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/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
new file mode 100644
index 0000000..5ed3d3b
--- /dev/null
+++ b/libs/gui/view/Surface.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Surface"
+
+#include <gui/view/Surface.h>
+
+#include <binder/Parcel.h>
+
+#include <utils/Log.h>
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+namespace view {
+
+status_t Surface::writeToParcel(Parcel* parcel) const {
+    return writeToParcel(parcel, false);
+}
+
+status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const {
+    if (parcel == nullptr) return BAD_VALUE;
+
+    status_t res = OK;
+
+    if (!nameAlreadyWritten) {
+        res = parcel->writeString16(name);
+        if (res != OK) return res;
+
+        /* isSingleBuffered defaults to no */
+        res = parcel->writeInt32(0);
+        if (res != OK) return res;
+    }
+
+    res = parcel->writeStrongBinder(
+            IGraphicBufferProducer::asBinder(graphicBufferProducer));
+
+    return res;
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel) {
+    return readFromParcel(parcel, false);
+}
+
+status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
+    if (parcel == nullptr) return BAD_VALUE;
+
+    status_t res = OK;
+    if (!nameAlreadyRead) {
+        name = readMaybeEmptyString16(parcel);
+        // Discard this for now
+        int isSingleBuffered;
+        res = parcel->readInt32(&isSingleBuffered);
+        if (res != OK) {
+            ALOGE("Can't read isSingleBuffered");
+            return res;
+        }
+    }
+
+    sp<IBinder> binder;
+
+    res = parcel->readNullableStrongBinder(&binder);
+    if (res != OK) {
+        ALOGE("%s: Can't read strong binder", __FUNCTION__);
+        return res;
+    }
+
+    graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
+
+    return OK;
+}
+
+String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
+    size_t len;
+    const char16_t* str = parcel->readString16Inplace(&len);
+    if (str != nullptr) {
+        return String16(str, len);
+    } else {
+        return String16();
+    }
+}
+
+} // namespace view
+} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 9485d5b..9294419 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -17,7 +17,11 @@
 cc_library {
     name: "libinput",
     host_supported: true,
-
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
     srcs: [
         "Input.cpp",
         "InputDevice.cpp",
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index d755ed3..4287abe 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,6 +21,7 @@
 #include <ctype.h>
 
 #include <input/InputDevice.h>
+#include <input/InputEventLabels.h>
 
 namespace android {
 
@@ -87,17 +88,23 @@
         const String8& name, InputDeviceConfigurationFileType type) {
     // Search system repository.
     String8 path;
-    path.setTo(getenv("ANDROID_ROOT"));
-    path.append("/usr/");
-    appendInputDeviceConfigurationFileRelativePath(path, name, type);
+
+    // Treblized input device config files will be located /odm/usr or /vendor/usr.
+    const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
+    for (size_t i = 0; i < size(rootsForPartition); i++) {
+        path.setTo(rootsForPartition[i]);
+        path.append("/usr/");
+        appendInputDeviceConfigurationFileRelativePath(path, name, type);
 #if DEBUG_PROBE
-    ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
+        ALOGD("Probing for system provided input device configuration file: path='%s'",
+              path.string());
 #endif
-    if (!access(path.string(), R_OK)) {
+        if (!access(path.string(), R_OK)) {
 #if DEBUG_PROBE
-        ALOGD("Found");
+            ALOGD("Found");
 #endif
-        return path;
+            return path;
+        }
     }
 
     // Search user repository.
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 9a01395..07f2289 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -208,7 +208,6 @@
 }
 
 int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
-    int32_t mask;
     switch (keyCode) {
     case AKEYCODE_ALT_LEFT:
         return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
new file mode 100644
index 0000000..3ef8b4a
--- /dev/null
+++ b/libs/math/Android.bp
@@ -0,0 +1,21 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+    name: "libmath",
+    host_supported: true,
+    export_include_dirs: ["include"],
+}
+
+subdirs = ["tests"]
diff --git a/libs/math/MODULE_LICENSE_APACHE2 b/libs/math/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/math/MODULE_LICENSE_APACHE2
diff --git a/libs/math/NOTICE b/libs/math/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/math/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libs/math/include/math/TMatHelpers.h b/libs/math/include/math/TMatHelpers.h
new file mode 100644
index 0000000..478e702
--- /dev/null
+++ b/libs/math/include/math/TMatHelpers.h
@@ -0,0 +1,637 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <exception>
+#include <iomanip>
+#include <stdexcept>
+
+#include <math/quat.h>
+#include <math/TVecHelpers.h>
+
+#include  <utils/String8.h>
+
+#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 {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/mat*.h
+ */
+
+
+/*
+ * Matrix utilities
+ */
+
+namespace matrix {
+
+inline constexpr int     transpose(int v)    { return v; }
+inline constexpr float   transpose(float v)  { return v; }
+inline constexpr double  transpose(double v) { return v; }
+
+inline constexpr int     trace(int v)    { return v; }
+inline constexpr float   trace(float v)  { return v; }
+inline constexpr double  trace(double v) { return v; }
+
+/*
+ * Matrix inversion
+ */
+template<typename MATRIX>
+MATRIX PURE gaussJordanInverse(const MATRIX& src) {
+    typedef typename MATRIX::value_type T;
+    static constexpr unsigned int N = MATRIX::NUM_ROWS;
+    MATRIX tmp(src);
+    MATRIX inverted(1);
+
+    for (size_t i = 0; i < N; ++i) {
+        // look for largest element in i'th column
+        size_t swap = i;
+        T t = std::abs(tmp[i][i]);
+        for (size_t j = i + 1; j < N; ++j) {
+            const T t2 = std::abs(tmp[j][i]);
+            if (t2 > t) {
+                swap = j;
+                t = t2;
+            }
+        }
+
+        if (swap != i) {
+            // swap columns.
+            std::swap(tmp[i], tmp[swap]);
+            std::swap(inverted[i], inverted[swap]);
+        }
+
+        const T denom(tmp[i][i]);
+        for (size_t k = 0; k < N; ++k) {
+            tmp[i][k] /= denom;
+            inverted[i][k] /= denom;
+        }
+
+        // Factor out the lower triangle
+        for (size_t j = 0; j < N; ++j) {
+            if (j != i) {
+                const T d = tmp[j][i];
+                for (size_t k = 0; k < N; ++k) {
+                    tmp[j][k] -= tmp[i][k] * d;
+                    inverted[j][k] -= inverted[i][k] * d;
+                }
+            }
+        }
+    }
+
+    return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// 2x2 matrix inverse is easy.
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse2(const MATRIX& x) {
+    typedef typename MATRIX::value_type T;
+
+    // Assuming the input matrix is:
+    // | a b |
+    // | c d |
+    //
+    // The analytic inverse is
+    // | d -b |
+    // | -c a | / (a d - b c)
+    //
+    // Importantly, our matrices are column-major!
+
+    MATRIX inverted(MATRIX::NO_INIT);
+
+    const T a = x[0][0];
+    const T c = x[0][1];
+    const T b = x[1][0];
+    const T d = x[1][1];
+
+    const T det((a * d) - (b * c));
+    inverted[0][0] =  d / det;
+    inverted[0][1] = -c / det;
+    inverted[1][0] = -b / det;
+    inverted[1][1] =  a / det;
+    return inverted;
+}
+
+
+//------------------------------------------------------------------------------
+// From the Wikipedia article on matrix inversion's section on fast 3x3
+// matrix inversion:
+// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE fastInverse3(const MATRIX& x) {
+    typedef typename MATRIX::value_type T;
+
+    // Assuming the input matrix is:
+    // | a b c |
+    // | d e f |
+    // | g h i |
+    //
+    // The analytic inverse is
+    // | A B C |^T
+    // | D E F |
+    // | G H I | / determinant
+    //
+    // Which is
+    // | A D G |
+    // | B E H |
+    // | C F I | / determinant
+    //
+    // Where:
+    // A = (ei - fh), B = (fg - di), C = (dh - eg)
+    // D = (ch - bi), E = (ai - cg), F = (bg - ah)
+    // G = (bf - ce), H = (cd - af), I = (ae - bd)
+    //
+    // and the determinant is a*A + b*B + c*C (The rule of Sarrus)
+    //
+    // Importantly, our matrices are column-major!
+
+    MATRIX inverted(MATRIX::NO_INIT);
+
+    const T a = x[0][0];
+    const T b = x[1][0];
+    const T c = x[2][0];
+    const T d = x[0][1];
+    const T e = x[1][1];
+    const T f = x[2][1];
+    const T g = x[0][2];
+    const T h = x[1][2];
+    const T i = x[2][2];
+
+    // Do the full analytic inverse
+    const T A = e * i - f * h;
+    const T B = f * g - d * i;
+    const T C = d * h - e * g;
+    inverted[0][0] = A;                 // A
+    inverted[0][1] = B;                 // B
+    inverted[0][2] = C;                 // C
+    inverted[1][0] = c * h - b * i;     // D
+    inverted[1][1] = a * i - c * g;     // E
+    inverted[1][2] = b * g - a * h;     // F
+    inverted[2][0] = b * f - c * e;     // G
+    inverted[2][1] = c * d - a * f;     // H
+    inverted[2][2] = a * e - b * d;     // I
+
+    const T det(a * A + b * B + c * C);
+    for (size_t col = 0; col < 3; ++col) {
+        for (size_t row = 0; row < 3; ++row) {
+            inverted[col][row] /= det;
+        }
+    }
+
+    return inverted;
+}
+
+/**
+ * Inversion function which switches on the matrix size.
+ * @warning This function assumes the matrix is invertible. The result is
+ * undefined if it is not. It is the responsibility of the caller to
+ * make sure the matrix is not singular.
+ */
+template <typename MATRIX>
+inline constexpr MATRIX PURE inverse(const MATRIX& matrix) {
+    static_assert(MATRIX::NUM_ROWS == MATRIX::NUM_COLS, "only square matrices can be inverted");
+    return (MATRIX::NUM_ROWS == 2) ? fastInverse2<MATRIX>(matrix) :
+          ((MATRIX::NUM_ROWS == 3) ? fastInverse3<MATRIX>(matrix) :
+                    gaussJordanInverse<MATRIX>(matrix));
+}
+
+template<typename MATRIX_R, typename MATRIX_A, typename MATRIX_B>
+CONSTEXPR MATRIX_R PURE multiply(const MATRIX_A& lhs, const MATRIX_B& rhs) {
+    // pre-requisite:
+    //  lhs : D columns, R rows
+    //  rhs : C columns, D rows
+    //  res : C columns, R rows
+
+    static_assert(MATRIX_A::NUM_COLS == MATRIX_B::NUM_ROWS,
+            "matrices can't be multiplied. invalid dimensions.");
+    static_assert(MATRIX_R::NUM_COLS == MATRIX_B::NUM_COLS,
+            "invalid dimension of matrix multiply result.");
+    static_assert(MATRIX_R::NUM_ROWS == MATRIX_A::NUM_ROWS,
+            "invalid dimension of matrix multiply result.");
+
+    MATRIX_R res(MATRIX_R::NO_INIT);
+    for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) {
+        res[col] = lhs * rhs[col];
+    }
+    return res;
+}
+
+// transpose. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR MATRIX PURE transpose(const MATRIX& m) {
+    // for now we only handle square matrix transpose
+    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "transpose only supports square matrices");
+    MATRIX result(MATRIX::NO_INIT);
+    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+        for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) {
+            result[col][row] = transpose(m[row][col]);
+        }
+    }
+    return result;
+}
+
+// trace. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR typename MATRIX::value_type PURE trace(const MATRIX& m) {
+    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "trace only defined for square matrices");
+    typename MATRIX::value_type result(0);
+    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+        result += trace(m[col][col]);
+    }
+    return result;
+}
+
+// diag. this handles matrices of matrices
+template <typename MATRIX>
+CONSTEXPR typename MATRIX::col_type PURE diag(const MATRIX& m) {
+    static_assert(MATRIX::NUM_COLS == MATRIX::NUM_ROWS, "diag only defined for square matrices");
+    typename MATRIX::col_type result(MATRIX::col_type::NO_INIT);
+    for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) {
+        result[col] = m[col][col];
+    }
+    return result;
+}
+
+//------------------------------------------------------------------------------
+// This is taken from the Imath MatrixAlgo code, and is identical to Eigen.
+template <typename MATRIX>
+TQuaternion<typename MATRIX::value_type> extractQuat(const MATRIX& mat) {
+    typedef typename MATRIX::value_type T;
+
+    TQuaternion<T> quat(TQuaternion<T>::NO_INIT);
+
+    // Compute the trace to see if it is positive or not.
+    const T trace = mat[0][0] + mat[1][1] + mat[2][2];
+
+    // check the sign of the trace
+    if (LIKELY(trace > 0)) {
+        // trace is positive
+        T s = std::sqrt(trace + 1);
+        quat.w = T(0.5) * s;
+        s = T(0.5) / s;
+        quat.x = (mat[1][2] - mat[2][1]) * s;
+        quat.y = (mat[2][0] - mat[0][2]) * s;
+        quat.z = (mat[0][1] - mat[1][0]) * s;
+    } else {
+        // trace is negative
+
+        // Find the index of the greatest diagonal
+        size_t i = 0;
+        if (mat[1][1] > mat[0][0]) { i = 1; }
+        if (mat[2][2] > mat[i][i]) { i = 2; }
+
+        // Get the next indices: (n+1)%3
+        static constexpr size_t next_ijk[3] = { 1, 2, 0 };
+        size_t j = next_ijk[i];
+        size_t k = next_ijk[j];
+        T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1);
+        quat[i] = T(0.5) * s;
+        if (s != 0) {
+            s = T(0.5) / s;
+        }
+        quat.w  = (mat[j][k] - mat[k][j]) * s;
+        quat[j] = (mat[i][j] + mat[j][i]) * s;
+        quat[k] = (mat[i][k] + mat[k][i]) * s;
+    }
+    return quat;
+}
+
+template <typename MATRIX>
+String8 asString(const MATRIX& m) {
+    String8 s;
+    for (size_t c = 0; c < MATRIX::col_size(); c++) {
+        s.append("|  ");
+        for (size_t r = 0; r < MATRIX::row_size(); r++) {
+            s.appendFormat("%7.2f  ", m[r][c]);
+        }
+        s.append("|\n");
+    }
+    return s;
+}
+
+}  // namespace matrix
+
+// -------------------------------------------------------------------------------------
+
+/*
+ * TMatProductOperators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TMatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class BASE, typename T>
+class TMatProductOperators {
+public:
+    // multiply by a scalar
+    BASE<T>& operator *= (T v) {
+        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+        for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+            lhs[col] *= v;
+        }
+        return lhs;
+    }
+
+    //  matrix *= matrix
+    template<typename U>
+    const BASE<T>& operator *= (const BASE<U>& rhs) {
+        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+        lhs = matrix::multiply<BASE<T> >(lhs, rhs);
+        return lhs;
+    }
+
+    // divide by a scalar
+    BASE<T>& operator /= (T v) {
+        BASE<T>& lhs(static_cast< BASE<T>& >(*this));
+        for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+            lhs[col] /= v;
+        }
+        return lhs;
+    }
+
+    // matrix * matrix, result is a matrix of the same type than the lhs matrix
+    template<typename U>
+    friend CONSTEXPR BASE<T> PURE operator *(const BASE<T>& lhs, const BASE<U>& rhs) {
+        return matrix::multiply<BASE<T> >(lhs, rhs);
+    }
+};
+
+/*
+ * TMatSquareFunctions implements functions on a matrix of type BASE<T>.
+ *
+ * BASE only needs to implement:
+ *  - operator[]
+ *  - col_type
+ *  - row_type
+ *  - COL_SIZE
+ *  - ROW_SIZE
+ *
+ * By simply inheriting from TMatSquareFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template<template<typename U> class BASE, typename T>
+class TMatSquareFunctions {
+public:
+
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    friend inline CONSTEXPR BASE<T> PURE inverse(const BASE<T>& matrix) {
+        return matrix::inverse(matrix);
+    }
+    friend inline constexpr BASE<T> PURE transpose(const BASE<T>& m) {
+        return matrix::transpose(m);
+    }
+    friend inline constexpr T PURE trace(const BASE<T>& m) {
+        return matrix::trace(m);
+    }
+};
+
+template<template<typename U> class BASE, typename T>
+class TMatHelpers {
+public:
+    constexpr inline size_t getColumnSize() const   { return BASE<T>::COL_SIZE; }
+    constexpr inline size_t getRowSize() const      { return BASE<T>::ROW_SIZE; }
+    constexpr inline size_t getColumnCount() const  { return BASE<T>::NUM_COLS; }
+    constexpr inline size_t getRowCount() const     { return BASE<T>::NUM_ROWS; }
+    constexpr inline size_t size()  const           { return BASE<T>::ROW_SIZE; }  // for TVec*<>
+
+    // array access
+    constexpr T const* asArray() const {
+        return &static_cast<BASE<T> const &>(*this)[0][0];
+    }
+
+    // element access
+    inline constexpr T const& operator()(size_t row, size_t col) const {
+        return static_cast<BASE<T> const &>(*this)[col][row];
+    }
+
+    inline T& operator()(size_t row, size_t col) {
+        return static_cast<BASE<T>&>(*this)[col][row];
+    }
+
+    template <typename VEC>
+    static CONSTEXPR BASE<T> translate(const VEC& t) {
+        BASE<T> r;
+        r[BASE<T>::NUM_COLS-1] = t;
+        return r;
+    }
+
+    template <typename VEC>
+    static constexpr BASE<T> scale(const VEC& s) {
+        return BASE<T>(s);
+    }
+
+    friend inline CONSTEXPR BASE<T> PURE abs(BASE<T> m) {
+        for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+            m[col] = abs(m[col]);
+        }
+        return m;
+    }
+};
+
+// functions for 3x3 and 4x4 matrices
+template<template<typename U> class BASE, typename T>
+class TMatTransform {
+public:
+    inline constexpr TMatTransform() {
+        static_assert(BASE<T>::NUM_ROWS == 3 || BASE<T>::NUM_ROWS == 4, "3x3 or 4x4 matrices only");
+    }
+
+    template <typename A, typename VEC>
+    static CONSTEXPR BASE<T> rotate(A radian, const VEC& about) {
+        BASE<T> r;
+        T c = std::cos(radian);
+        T s = std::sin(radian);
+        if (about.x == 1 && about.y == 0 && about.z == 0) {
+            r[1][1] = c;   r[2][2] = c;
+            r[1][2] = s;   r[2][1] = -s;
+        } else if (about.x == 0 && about.y == 1 && about.z == 0) {
+            r[0][0] = c;   r[2][2] = c;
+            r[2][0] = s;   r[0][2] = -s;
+        } else if (about.x == 0 && about.y == 0 && about.z == 1) {
+            r[0][0] = c;   r[1][1] = c;
+            r[0][1] = s;   r[1][0] = -s;
+        } else {
+            VEC nabout = normalize(about);
+            typename VEC::value_type x = nabout.x;
+            typename VEC::value_type y = nabout.y;
+            typename VEC::value_type z = nabout.z;
+            T nc = 1 - c;
+            T xy = x * y;
+            T yz = y * z;
+            T zx = z * x;
+            T xs = x * s;
+            T ys = y * s;
+            T zs = z * s;
+            r[0][0] = x*x*nc +  c;    r[1][0] =  xy*nc - zs;    r[2][0] =  zx*nc + ys;
+            r[0][1] =  xy*nc + zs;    r[1][1] = y*y*nc +  c;    r[2][1] =  yz*nc - xs;
+            r[0][2] =  zx*nc - ys;    r[1][2] =  yz*nc + xs;    r[2][2] = z*z*nc +  c;
+
+            // Clamp results to -1, 1.
+            for (size_t col = 0; col < 3; ++col) {
+                for (size_t row = 0; row < 3; ++row) {
+                    r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+                }
+            }
+        }
+        return r;
+    }
+
+    /**
+     * Create a matrix from euler angles using YPR around YXZ respectively
+     * @param yaw about Y axis
+     * @param pitch about X axis
+     * @param roll about Z axis
+     */
+    template <
+        typename Y, typename P, typename R,
+        typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+        typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+        typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+    >
+    static CONSTEXPR BASE<T> eulerYXZ(Y yaw, P pitch, R roll) {
+        return eulerZYX(roll, pitch, yaw);
+    }
+
+    /**
+     * Create a matrix from euler angles using YPR around ZYX respectively
+     * @param roll about X axis
+     * @param pitch about Y axis
+     * @param yaw about Z axis
+     *
+     * The euler angles are applied in ZYX order. i.e: a vector is first rotated
+     * about X (roll) then Y (pitch) and then Z (yaw).
+     */
+    template <
+    typename Y, typename P, typename R,
+    typename = typename std::enable_if<std::is_arithmetic<Y>::value >::type,
+    typename = typename std::enable_if<std::is_arithmetic<P>::value >::type,
+    typename = typename std::enable_if<std::is_arithmetic<R>::value >::type
+    >
+    static CONSTEXPR BASE<T> eulerZYX(Y yaw, P pitch, R roll) {
+        BASE<T> r;
+        T cy = std::cos(yaw);
+        T sy = std::sin(yaw);
+        T cp = std::cos(pitch);
+        T sp = std::sin(pitch);
+        T cr = std::cos(roll);
+        T sr = std::sin(roll);
+        T cc = cr * cy;
+        T cs = cr * sy;
+        T sc = sr * cy;
+        T ss = sr * sy;
+        r[0][0] = cp * cy;
+        r[0][1] = cp * sy;
+        r[0][2] = -sp;
+        r[1][0] = sp * sc - cs;
+        r[1][1] = sp * ss + cc;
+        r[1][2] = cp * sr;
+        r[2][0] = sp * cc + ss;
+        r[2][1] = sp * cs - sc;
+        r[2][2] = cp * cr;
+
+        // Clamp results to -1, 1.
+        for (size_t col = 0; col < 3; ++col) {
+            for (size_t row = 0; row < 3; ++row) {
+                r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1));
+            }
+        }
+        return r;
+    }
+
+    TQuaternion<T> toQuaternion() const {
+        return matrix::extractQuat(static_cast<const BASE<T>&>(*this));
+    }
+};
+
+
+template <template<typename T> class BASE, typename T>
+class TMatDebug {
+public:
+    friend std::ostream& operator<<(std::ostream& stream, const BASE<T>& m) {
+        for (size_t row = 0; row < BASE<T>::NUM_ROWS; ++row) {
+            if (row != 0) {
+                stream << std::endl;
+            }
+            if (row == 0) {
+                stream << "/ ";
+            } else if (row == BASE<T>::NUM_ROWS-1) {
+                stream << "\\ ";
+            } else {
+                stream << "| ";
+            }
+            for (size_t col = 0; col < BASE<T>::NUM_COLS; ++col) {
+                stream << std::setw(10) << std::to_string(m[col][row]);
+            }
+            if (row == 0) {
+                stream << " \\";
+            } else if (row == BASE<T>::NUM_ROWS-1) {
+                stream << " /";
+            } else {
+                stream << " |";
+            }
+        }
+        return stream;
+    }
+
+    String8 asString() const {
+        return matrix::asString(static_cast<const BASE<T>&>(*this));
+    }
+};
+
+// -------------------------------------------------------------------------------------
+}  // namespace details
+}  // namespace android
+
+#undef LIKELY
+#undef UNLIKELY
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/TQuatHelpers.h b/libs/math/include/math/TQuatHelpers.h
new file mode 100644
index 0000000..f0a71ae
--- /dev/null
+++ b/libs/math/include/math/TQuatHelpers.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <iostream>
+
+#include <math/vec3.h>
+
+#define PURE __attribute__((pure))
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/quat.h
+ */
+
+
+/*
+ * TQuatProductOperators implements basic arithmetic and basic compound assignment
+ * operators on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatProductOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class QUATERNION, typename T>
+class TQuatProductOperators {
+public:
+    /* compound assignment from a another quaternion of the same size but different
+     * element type.
+     */
+    template <typename OTHER>
+    QUATERNION<T>& operator *= (const QUATERNION<OTHER>& r) {
+        QUATERNION<T>& q = static_cast<QUATERNION<T>&>(*this);
+        q = q * r;
+        return q;
+    }
+
+    /* compound assignment products by a scalar
+     */
+    QUATERNION<T>& operator *= (T v) {
+        QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+        for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+            lhs[i] *= v;
+        }
+        return lhs;
+    }
+    QUATERNION<T>& operator /= (T v) {
+        QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
+        for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
+            lhs[i] /= v;
+        }
+        return lhs;
+    }
+
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+
+    /* The operators below handle operation between quaternion of the same size
+     * but of a different element type.
+     */
+    template<typename RT>
+    friend inline
+    constexpr QUATERNION<T> PURE operator *(const QUATERNION<T>& q, const QUATERNION<RT>& r) {
+        // could be written as:
+        //  return QUATERNION<T>(
+        //            q.w*r.w - dot(q.xyz, r.xyz),
+        //            q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz));
+
+        return QUATERNION<T>(
+                q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+                q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+                q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+                q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+    }
+
+    template<typename RT>
+    friend inline
+    constexpr TVec3<T> PURE operator *(const QUATERNION<T>& q, const TVec3<RT>& v) {
+        // note: if q is known to be a unit quaternion, then this simplifies to:
+        //  TVec3<T> t = 2 * cross(q.xyz, v)
+        //  return v + (q.w * t) + cross(q.xyz, t)
+        return imaginary(q * QUATERNION<T>(v, 0) * inverse(q));
+    }
+
+
+    /* For quaternions, we use explicit "by a scalar" products because it's much faster
+     * than going (implicitly) through the quaternion multiplication.
+     * For reference: we could use the code below instead, but it would be a lot slower.
+     *  friend inline
+     *  constexpr BASE<T> PURE operator *(const BASE<T>& q, const BASE<T>& r) {
+     *      return BASE<T>(
+     *              q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z,
+     *              q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y,
+     *              q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x,
+     *              q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
+     *
+     */
+    friend inline
+    constexpr QUATERNION<T> PURE operator *(QUATERNION<T> q, T scalar) {
+        // don't pass q by reference because we need a copy anyways
+        return q *= scalar;
+    }
+    friend inline
+    constexpr QUATERNION<T> PURE operator *(T scalar, QUATERNION<T> q) {
+        // don't pass q by reference because we need a copy anyways
+        return q *= scalar;
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE operator /(QUATERNION<T> q, T scalar) {
+        // don't pass q by reference because we need a copy anyways
+        return q /= scalar;
+    }
+};
+
+
+/*
+ * TQuatFunctions implements functions on a quaternion of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatFunctions {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+
+    template<typename RT>
+    friend inline
+    constexpr T PURE dot(const QUATERNION<T>& p, const QUATERNION<RT>& q) {
+        return p.x * q.x +
+               p.y * q.y +
+               p.z * q.z +
+               p.w * q.w;
+    }
+
+    friend inline
+    constexpr T PURE norm(const QUATERNION<T>& q) {
+        return std::sqrt( dot(q, q) );
+    }
+
+    friend inline
+    constexpr T PURE length(const QUATERNION<T>& q) {
+        return norm(q);
+    }
+
+    friend inline
+    constexpr T PURE length2(const QUATERNION<T>& q) {
+        return dot(q, q);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE normalize(const QUATERNION<T>& q) {
+        return length(q) ? q / length(q) : QUATERNION<T>(1);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE conj(const QUATERNION<T>& q) {
+        return QUATERNION<T>(q.w, -q.x, -q.y, -q.z);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE inverse(const QUATERNION<T>& q) {
+        return conj(q) * (1 / dot(q, q));
+    }
+
+    friend inline
+    constexpr T PURE real(const QUATERNION<T>& q) {
+        return q.w;
+    }
+
+    friend inline
+    constexpr TVec3<T> PURE imaginary(const QUATERNION<T>& q) {
+        return q.xyz;
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE unreal(const QUATERNION<T>& q) {
+        return QUATERNION<T>(q.xyz, 0);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE cross(const QUATERNION<T>& p, const QUATERNION<T>& q) {
+        return unreal(p*q);
+    }
+
+    friend inline
+    QUATERNION<T> PURE exp(const QUATERNION<T>& q) {
+        const T nq(norm(q.xyz));
+        return std::exp(q.w)*QUATERNION<T>((sin(nq)/nq)*q.xyz, cos(nq));
+    }
+
+    friend inline
+    QUATERNION<T> PURE log(const QUATERNION<T>& q) {
+        const T nq(norm(q));
+        return QUATERNION<T>((std::acos(q.w/nq)/norm(q.xyz))*q.xyz, log(nq));
+    }
+
+    friend inline
+    QUATERNION<T> PURE pow(const QUATERNION<T>& q, T a) {
+        // could also be computed as: exp(a*log(q));
+        const T nq(norm(q));
+        const T theta(a*std::acos(q.w / nq));
+        return std::pow(nq, a) * QUATERNION<T>(normalize(q.xyz) * std::sin(theta), std::cos(theta));
+    }
+
+    friend inline
+    QUATERNION<T> PURE slerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+        // could also be computed as: pow(q * inverse(p), t) * p;
+        const T d = dot(p, q);
+        const T npq = sqrt(dot(p, p) * dot(q, q));  // ||p|| * ||q||
+        const T a = std::acos(std::abs(d) / npq);
+        const T a0 = a * (1 - t);
+        const T a1 = a * t;
+        const T isina = 1 / sin(a);
+        const T s0 = std::sin(a0) * isina;
+        const T s1 = std::sin(a1) * isina;
+        // ensure we're taking the "short" side
+        return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE lerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+        return ((1 - t) * p) + (t * q);
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE nlerp(const QUATERNION<T>& p, const QUATERNION<T>& q, T t) {
+        return normalize(lerp(p, q, t));
+    }
+
+    friend inline
+    constexpr QUATERNION<T> PURE positive(const QUATERNION<T>& q) {
+        return q.w < 0 ? -q : q;
+    }
+};
+
+/*
+ * TQuatDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TQuatDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template <template<typename T> class QUATERNION, typename T>
+class TQuatDebug {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    friend std::ostream& operator<< (std::ostream& stream, const QUATERNION<T>& q) {
+        return stream << "< " << q.w << " + " << q.x << "i + " << q.y << "j + " << q.z << "k >";
+    }
+};
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+}  // namespace details
+}  // namespace android
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
new file mode 100644
index 0000000..20f852f
--- /dev/null
+++ b/libs/math/include/math/TVecHelpers.h
@@ -0,0 +1,608 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#pragma once
+
+#include <math.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cmath>
+#include <limits>
+#include <iostream>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+namespace details {
+// -------------------------------------------------------------------------------------
+
+/*
+ * No user serviceable parts here.
+ *
+ * Don't use this file directly, instead include ui/vec{2|3|4}.h
+ */
+
+/*
+ * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
+ * operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+
+template <template<typename T> class VECTOR, typename T>
+class TVecAddOperators {
+public:
+    /* compound assignment from a another vector of the same size but different
+     * element type.
+     */
+    template<typename OTHER>
+    VECTOR<T>& operator +=(const VECTOR<OTHER>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] += v[i];
+        }
+        return lhs;
+    }
+    template<typename OTHER>
+    VECTOR<T>& operator -=(const VECTOR<OTHER>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] -= v[i];
+        }
+        return lhs;
+    }
+
+    /* compound assignment from a another vector of the same type.
+     * These operators can be used for implicit conversion and  handle operations
+     * like "vector *= scalar" by letting the compiler implicitly convert a scalar
+     * to a vector (assuming the BASE<T> allows it).
+     */
+    VECTOR<T>& operator +=(const VECTOR<T>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] += v[i];
+        }
+        return lhs;
+    }
+    VECTOR<T>& operator -=(const VECTOR<T>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] -= v[i];
+        }
+        return lhs;
+    }
+
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+
+    /* The operators below handle operation between vectors of the same size
+     * but of a different element type.
+     */
+    template<typename RT>
+    friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<RT>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv += rv;
+    }
+    template<typename RT>
+    friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<RT>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv -= rv;
+    }
+
+    /* The operators below (which are not templates once this class is instanced,
+     * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
+     * These handle operations like "vector + scalar" and "scalar + vector" by
+     * letting the compiler implicitly convert a scalar to a vector (assuming
+     * the BASE<T> allows it).
+     */
+    friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<T>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv += rv;
+    }
+    friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<T>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv -= rv;
+    }
+};
+
+template<template<typename T> class VECTOR, typename T>
+class TVecProductOperators {
+public:
+    /* compound assignment from a another vector of the same size but different
+     * element type.
+     */
+    template<typename OTHER>
+    VECTOR<T>& operator *=(const VECTOR<OTHER>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] *= v[i];
+        }
+        return lhs;
+    }
+    template<typename OTHER>
+    VECTOR<T>& operator /=(const VECTOR<OTHER>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] /= v[i];
+        }
+        return lhs;
+    }
+
+    /* compound assignment from a another vector of the same type.
+     * These operators can be used for implicit conversion and  handle operations
+     * like "vector *= scalar" by letting the compiler implicitly convert a scalar
+     * to a vector (assuming the BASE<T> allows it).
+     */
+    VECTOR<T>& operator *=(const VECTOR<T>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] *= v[i];
+        }
+        return lhs;
+    }
+    VECTOR<T>& operator /=(const VECTOR<T>& v) {
+        VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < lhs.size(); i++) {
+            lhs[i] /= v[i];
+        }
+        return lhs;
+    }
+
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+
+    /* The operators below handle operation between vectors of the same size
+     * but of a different element type.
+     */
+    template<typename RT>
+    friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<RT>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv *= rv;
+    }
+    template<typename RT>
+    friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<RT>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv /= rv;
+    }
+
+    /* The operators below (which are not templates once this class is instanced,
+     * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
+     * These handle operations like "vector * scalar" and "scalar * vector" by
+     * letting the compiler implicitly convert a scalar to a vector (assuming
+     * the BASE<T> allows it).
+     */
+    friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<T>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv *= rv;
+    }
+    friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<T>& rv) {
+        // don't pass lv by reference because we need a copy anyways
+        return lv /= rv;
+    }
+};
+
+/*
+ * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ *
+ * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecUnaryOperators {
+public:
+    VECTOR<T>& operator ++() {
+        VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < rhs.size(); i++) {
+            ++rhs[i];
+        }
+        return rhs;
+    }
+
+    VECTOR<T>& operator --() {
+        VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
+        for (size_t i = 0; i < rhs.size(); i++) {
+            --rhs[i];
+        }
+        return rhs;
+    }
+
+    CONSTEXPR VECTOR<T> operator -() const {
+        VECTOR<T> r(VECTOR<T>::NO_INIT);
+        VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+        for (size_t i = 0; i < r.size(); i++) {
+            r[i] = -rv[i];
+        }
+        return r;
+    }
+};
+
+/*
+ * TVecComparisonOperators implements relational/comparison operators
+ * on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecComparisonOperators {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    template<typename RT>
+    friend inline
+    bool PURE operator ==(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        for (size_t i = 0; i < lv.size(); i++)
+            if (lv[i] != rv[i])
+                return false;
+        return true;
+    }
+
+    template<typename RT>
+    friend inline
+    bool PURE operator !=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return !operator ==(lv, rv);
+    }
+
+    template<typename RT>
+    friend inline
+    bool PURE operator >(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        for (size_t i = 0; i < lv.size(); i++) {
+            if (lv[i] == rv[i]) {
+                continue;
+            }
+            return lv[i] > rv[i];
+        }
+        return false;
+    }
+
+    template<typename RT>
+    friend inline
+    constexpr bool PURE operator <=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return !(lv > rv);
+    }
+
+    template<typename RT>
+    friend inline
+    bool PURE operator <(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        for (size_t i = 0; i < lv.size(); i++) {
+            if (lv[i] == rv[i]) {
+                continue;
+            }
+            return lv[i] < rv[i];
+        }
+        return false;
+    }
+
+    template<typename RT>
+    friend inline
+    constexpr bool PURE operator >=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return !(lv < rv);
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] == rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] != rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] < rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] <= rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] > rv[i];
+        }
+        return r;
+    }
+
+    template<typename RT>
+    friend inline
+    CONSTEXPR VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        VECTOR<bool> r;
+        for (size_t i = 0; i < lv.size(); i++) {
+            r[i] = lv[i] >= rv[i];
+        }
+        return r;
+    }
+};
+
+/*
+ * TVecFunctions implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecFunctions {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    template<typename RT>
+    friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        T r(0);
+        for (size_t i = 0; i < lv.size(); i++) {
+            //r = std::fma(lv[i], rv[i], r);
+            r += lv[i] * rv[i];
+        }
+        return r;
+    }
+
+    friend inline constexpr T PURE norm(const VECTOR<T>& lv) {
+        return std::sqrt(dot(lv, lv));
+    }
+
+    friend inline constexpr T PURE length(const VECTOR<T>& lv) {
+        return norm(lv);
+    }
+
+    friend inline constexpr T PURE norm2(const VECTOR<T>& lv) {
+        return dot(lv, lv);
+    }
+
+    friend inline constexpr T PURE length2(const VECTOR<T>& lv) {
+        return norm2(lv);
+    }
+
+    template<typename RT>
+    friend inline constexpr T PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return length(rv - lv);
+    }
+
+    template<typename RT>
+    friend inline constexpr T PURE distance2(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
+        return length2(rv - lv);
+    }
+
+    friend inline constexpr VECTOR<T> PURE normalize(const VECTOR<T>& lv) {
+        return lv * (T(1) / length(lv));
+    }
+
+    friend inline constexpr VECTOR<T> PURE rcp(VECTOR<T> v) {
+        return T(1) / v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE abs(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::abs(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE floor(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::floor(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE ceil(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::ceil(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE round(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::round(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE inversesqrt(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = T(1) / std::sqrt(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE sqrt(VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::sqrt(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE pow(VECTOR<T> v, T p) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::pow(v[i], p);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE saturate(const VECTOR<T>& lv) {
+        return clamp(lv, T(0), T(1));
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) {
+        for (size_t i = 0; i< v.size(); i++) {
+            v[i] = std::min(max, std::max(min, v[i]));
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) {
+        for (size_t i = 0; i<lv.size(); i++) {
+            //a[i] = std::fma(lv[i], rv[i], a[i]);
+            a[i] += (lv[i] * rv[i]);
+        }
+        return a;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::min(u[i], v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = std::max(u[i], v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR T PURE max(const VECTOR<T>& v) {
+        T r(std::numeric_limits<T>::lowest());
+        for (size_t i = 0; i < v.size(); i++) {
+            r = std::max(r, v[i]);
+        }
+        return r;
+    }
+
+    friend inline CONSTEXPR T PURE min(const VECTOR<T>& v) {
+        T r(std::numeric_limits<T>::max());
+        for (size_t i = 0; i < v.size(); i++) {
+            r = std::min(r, v[i]);
+        }
+        return r;
+    }
+
+    friend inline CONSTEXPR VECTOR<T> PURE apply(VECTOR<T> v, const std::function<T(T)>& f) {
+        for (size_t i = 0; i < v.size(); i++) {
+            v[i] = f(v[i]);
+        }
+        return v;
+    }
+
+    friend inline CONSTEXPR bool PURE any(const VECTOR<T>& v) {
+        for (size_t i = 0; i < v.size(); i++) {
+            if (v[i] != T(0)) return true;
+        }
+        return false;
+    }
+
+    friend inline CONSTEXPR bool PURE all(const VECTOR<T>& v) {
+        bool result = true;
+        for (size_t i = 0; i < v.size(); i++) {
+            result &= (v[i] != T(0));
+        }
+        return result;
+    }
+
+    template<typename R>
+    friend inline CONSTEXPR VECTOR<R> PURE map(VECTOR<T> v, const std::function<R(T)>& f) {
+        VECTOR<R> result;
+        for (size_t i = 0; i < v.size(); i++) {
+            result[i] = f(v[i]);
+        }
+        return result;
+    }
+};
+
+/*
+ * TVecDebug implements functions on a vector of type BASE<T>.
+ *
+ * BASE only needs to implement operator[] and size().
+ * By simply inheriting from TVecDebug<BASE, T> BASE will automatically
+ * get all the functionality here.
+ */
+template<template<typename T> class VECTOR, typename T>
+class TVecDebug {
+public:
+    /*
+     * NOTE: the functions below ARE NOT member methods. They are friend functions
+     * with they definition inlined with their declaration. This makes these
+     * template functions available to the compiler when (and only when) this class
+     * is instantiated, at which point they're only templated on the 2nd parameter
+     * (the first one, BASE<T> being known).
+     */
+    friend std::ostream& operator<<(std::ostream& stream, const VECTOR<T>& v) {
+        stream << "< ";
+        for (size_t i = 0; i < v.size() - 1; i++) {
+            stream << T(v[i]) << ", ";
+        }
+        stream << T(v[v.size() - 1]) << " >";
+        return stream;
+    }
+};
+
+#undef CONSTEXPR
+#undef PURE
+
+// -------------------------------------------------------------------------------------
+}  // namespace details
+}  // namespace android
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
new file mode 100644
index 0000000..3ca8bd1
--- /dev/null
+++ b/libs/math/include/math/half.h
@@ -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.
+ */
+
+#pragma once
+
+#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
diff --git a/libs/math/include/math/mat2.h b/libs/math/include/math/mat2.h
new file mode 100644
index 0000000..3e6cd4c
--- /dev/null
+++ b/libs/math/include/math/mat2.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/TMatHelpers.h>
+#include <math/vec2.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+/**
+ * A 2x2 column-major matrix class.
+ *
+ * Conceptually a 2x2 matrix is a an array of 2 column vec2:
+ *
+ * mat2 m =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cc}
+ *      m[0] & m[1] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cc}
+ *      m[0][0] & m[1][0] \\
+ *      m[0][1] & m[1][1] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cc}
+ *      m(0,0) & m(0,1) \\
+ *      m(1,0) & m(1,1) \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2.
+ *
+ */
+template <typename T>
+class TMat22 :  public TVecUnaryOperators<TMat22, T>,
+                public TVecComparisonOperators<TMat22, T>,
+                public TVecAddOperators<TMat22, T>,
+                public TMatProductOperators<TMat22, T>,
+                public TMatSquareFunctions<TMat22, T>,
+                public TMatHelpers<TMat22, T>,
+                public TMatDebug<TMat22, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+    typedef TVec2<T> col_type;
+    typedef TVec2<T> row_type;
+
+    static constexpr size_t COL_SIZE = col_type::SIZE;  // size of a column (i.e.: number of rows)
+    static constexpr size_t ROW_SIZE = row_type::SIZE;  // size of a row (i.e.: number of columns)
+    static constexpr size_t NUM_ROWS = COL_SIZE;
+    static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+    /*
+     *  <--  N columns  -->
+     *
+     *  a[0][0] a[1][0] a[2][0] ... a[N][0]    ^
+     *  a[0][1] a[1][1] a[2][1] ... a[N][1]    |
+     *  a[0][2] a[1][2] a[2][2] ... a[N][2]  M rows
+     *  ...                                    |
+     *  a[0][M] a[1][M] a[2][M] ... a[N][M]    v
+     *
+     *  COL_SIZE = M
+     *  ROW_SIZE = N
+     *  m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+     */
+
+    col_type m_value[NUM_COLS];
+
+public:
+    // array access
+    inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(column < NUM_COLS);
+#endif
+        return m_value[column];
+    }
+
+    inline col_type& operator[](size_t column) {
+        assert(column < NUM_COLS);
+        return m_value[column];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TMat22(const TMat22&) = default;
+    ~TMat22() = default;
+    TMat22& operator = (const TMat22&) = default;
+
+    /**
+     *  constructors
+     */
+
+    /**
+     * leaves object uninitialized. use with caution.
+     */
+    explicit constexpr TMat22(no_init)
+            : m_value{ col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT) } {}
+
+
+    /**
+     * initialize to identity.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      1 & 0 \\
+     *      0 & 1 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    CONSTEXPR TMat22();
+
+    /**
+     * initialize to Identity*scalar.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      v & 0 \\
+     *      0 & v \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template<typename U>
+    explicit CONSTEXPR TMat22(U v);
+
+    /**
+     * sets the diagonal to a vector.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      v[0] & 0 \\
+     *      0 & v[1] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat22(const TVec2<U>& v);
+
+    /**
+     * construct from another matrix of the same size
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat22(const TMat22<U>& rhs);
+
+    /**
+     * construct from 2 column vectors.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      v0 & v1 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename A, typename B>
+    CONSTEXPR TMat22(const TVec2<A>& v0, const TVec2<B>& v1);
+
+    /** construct from 4 elements in column-major form.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cc}
+     *      m[0][0] & m[1][0] \\
+     *      m[0][1] & m[1][1] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <
+        typename A, typename B,
+        typename C, typename D>
+    CONSTEXPR TMat22(A m00, B m01, C m10, D m11);
+
+    /**
+     * construct from a C array in column major form.
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat22(U const* rawArray);
+
+    /**
+     * Rotate by radians in the 2D plane
+     */
+    static CONSTEXPR TMat22<T> rotate(T radian) {
+        TMat22<T> r(TMat22<T>::NO_INIT);
+        T c = std::cos(radian);
+        T s = std::sin(radian);
+        r[0][0] = c;   r[1][1] = c;
+        r[0][1] = s;   r[1][0] = -s;
+        return r;
+    }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat22<T>::TMat22() {
+    m_value[0] = col_type(1, 0);
+    m_value[1] = col_type(0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U v) {
+    m_value[0] = col_type(v, 0);
+    m_value[1] = col_type(0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<U>& v) {
+    m_value[0] = col_type(v.x, 0);
+    m_value[1] = col_type(0, v.y);
+}
+
+// construct from 4 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+    typename A, typename B,
+    typename C, typename D>
+CONSTEXPR TMat22<T>::TMat22( A m00, B m01, C m10, D m11) {
+    m_value[0] = col_type(m00, m01);
+    m_value[1] = col_type(m10, m11);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(const TMat22<U>& rhs) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        m_value[col] = col_type(rhs[col]);
+    }
+}
+
+// Construct from 2 column vectors.
+template <typename T>
+template <typename A, typename B>
+CONSTEXPR TMat22<T>::TMat22(const TVec2<A>& v0, const TVec2<B>& v1) {
+    m_value[0] = v0;
+    m_value[1] = v1;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat22<T>::TMat22(U const* rawArray) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        for (size_t row = 0; row < NUM_ROWS; ++row) {
+            m_value[col][row] = *rawArray++;
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::col_type PURE operator *(const TMat22<T>& lhs, const TVec2<U>& rhs) {
+    // Result is initialized to zero.
+    typename TMat22<U>::col_type result;
+    for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+        result += lhs[col] * rhs[col];
+    }
+    return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat22<U>::row_type PURE operator *(const TVec2<U>& lhs, const TMat22<T>& rhs) {
+    typename TMat22<U>::row_type result(TMat22<U>::row_type::NO_INIT);
+    for (size_t col = 0; col < TMat22<T>::NUM_COLS; ++col) {
+        result[col] = dot(lhs, rhs[col]);
+    }
+    return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(TMat22<T> lhs, U rhs) {
+    return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat22<T>>::type PURE
+operator*(U lhs, const TMat22<T>& rhs) {
+    return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat22<T>::col_type PURE diag(const TMat22<T>& m) {
+    return matrix::diag(m);
+}
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat22<double> mat2d;
+typedef details::TMat22<float> mat2;
+typedef details::TMat22<float> mat2f;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h
new file mode 100644
index 0000000..5c8a9b2
--- /dev/null
+++ b/libs/math/include/math/mat3.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/quat.h>
+#include <math/TMatHelpers.h>
+#include <math/vec3.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 3x3 column-major matrix class.
+ *
+ * Conceptually a 3x3 matrix is a an array of 3 column vec3:
+ *
+ * mat3 m =
+ *      \f$
+ *      \left(
+ *      \begin{array}{ccc}
+ *      m[0] & m[1] & m[2] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{ccc}
+ *      m[0][0] & m[1][0] & m[2][0] \\
+ *      m[0][1] & m[1][1] & m[2][1] \\
+ *      m[0][2] & m[1][2] & m[2][2] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{ccc}
+ *      m(0,0) & m(0,1) & m(0,2) \\
+ *      m(1,0) & m(1,1) & m(1,2) \\
+ *      m(2,0) & m(2,1) & m(2,2) \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3.
+ *
+ */
+template <typename T>
+class TMat33 :  public TVecUnaryOperators<TMat33, T>,
+                public TVecComparisonOperators<TMat33, T>,
+                public TVecAddOperators<TMat33, T>,
+                public TMatProductOperators<TMat33, T>,
+                public TMatSquareFunctions<TMat33, T>,
+                public TMatTransform<TMat33, T>,
+                public TMatHelpers<TMat33, T>,
+                public TMatDebug<TMat33, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+    typedef TVec3<T> col_type;
+    typedef TVec3<T> row_type;
+
+    static constexpr size_t COL_SIZE = col_type::SIZE;  // size of a column (i.e.: number of rows)
+    static constexpr size_t ROW_SIZE = row_type::SIZE;  // size of a row (i.e.: number of columns)
+    static constexpr size_t NUM_ROWS = COL_SIZE;
+    static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+    /*
+     *  <--  N columns  -->
+     *
+     *  a[0][0] a[1][0] a[2][0] ... a[N][0]    ^
+     *  a[0][1] a[1][1] a[2][1] ... a[N][1]    |
+     *  a[0][2] a[1][2] a[2][2] ... a[N][2]  M rows
+     *  ...                                    |
+     *  a[0][M] a[1][M] a[2][M] ... a[N][M]    v
+     *
+     *  COL_SIZE = M
+     *  ROW_SIZE = N
+     *  m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+     */
+
+    col_type m_value[NUM_COLS];
+
+public:
+    // array access
+    inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(column < NUM_COLS);
+#endif
+        return m_value[column];
+    }
+
+    inline col_type& operator[](size_t column) {
+        assert(column < NUM_COLS);
+        return m_value[column];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TMat33(const TMat33&) = default;
+    ~TMat33() = default;
+    TMat33& operator = (const TMat33&) = default;
+
+    /**
+     *  constructors
+     */
+
+    /**
+     * leaves object uninitialized. use with caution.
+     */
+    explicit constexpr TMat33(no_init)
+            : m_value{ col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT) } {}
+
+
+    /**
+     * initialize to identity.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      1 & 0 & 0 \\
+     *      0 & 1 & 0 \\
+     *      0 & 0 & 1 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    CONSTEXPR TMat33();
+
+    /**
+     * initialize to Identity*scalar.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      v & 0 & 0 \\
+     *      0 & v & 0 \\
+     *      0 & 0 & v \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template<typename U>
+    explicit CONSTEXPR TMat33(U v);
+
+    /**
+     * sets the diagonal to a vector.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      v[0] & 0 & 0 \\
+     *      0 & v[1] & 0 \\
+     *      0 & 0 & v[2] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat33(const TVec3<U>& v);
+
+    /**
+     * construct from another matrix of the same size
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat33(const TMat33<U>& rhs);
+
+    /**
+     * construct from 3 column vectors.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      v0 & v1 & v2 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename A, typename B, typename C>
+    CONSTEXPR TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2);
+
+    /** construct from 9 elements in column-major form.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{ccc}
+     *      m[0][0] & m[1][0] & m[2][0] \\
+     *      m[0][1] & m[1][1] & m[2][1] \\
+     *      m[0][2] & m[1][2] & m[2][2] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <
+        typename A, typename B, typename C,
+        typename D, typename E, typename F,
+        typename G, typename H, typename I>
+    CONSTEXPR TMat33(
+           A m00, B m01, C m02,
+           D m10, E m11, F m12,
+           G m20, H m21, I m22);
+
+    /**
+     * construct from a quaternion
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat33(const TQuaternion<U>& q);
+
+    /**
+     * construct from a C array in column major form.
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat33(U const* rawArray);
+
+    /**
+     * orthogonalize only works on matrices of size 3x3
+     */
+    friend inline
+    CONSTEXPR TMat33 orthogonalize(const TMat33& m) {
+        TMat33 ret(TMat33::NO_INIT);
+        ret[0] = normalize(m[0]);
+        ret[2] = normalize(cross(ret[0], m[1]));
+        ret[1] = normalize(cross(ret[2], ret[0]));
+        return ret;
+    }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat33<T>::TMat33() {
+    m_value[0] = col_type(1, 0, 0);
+    m_value[1] = col_type(0, 1, 0);
+    m_value[2] = col_type(0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U v) {
+    m_value[0] = col_type(v, 0, 0);
+    m_value[1] = col_type(0, v, 0);
+    m_value[2] = col_type(0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<U>& v) {
+    m_value[0] = col_type(v.x, 0, 0);
+    m_value[1] = col_type(0, v.y, 0);
+    m_value[2] = col_type(0, 0, v.z);
+}
+
+// construct from 9 scalars. Note that the arrangement
+// of values in the constructor is the transpose of the matrix
+// notation.
+template<typename T>
+template <
+    typename A, typename B, typename C,
+    typename D, typename E, typename F,
+    typename G, typename H, typename I>
+CONSTEXPR TMat33<T>::TMat33(
+        A m00, B m01, C m02,
+        D m10, E m11, F m12,
+        G m20, H m21, I m22) {
+    m_value[0] = col_type(m00, m01, m02);
+    m_value[1] = col_type(m10, m11, m12);
+    m_value[2] = col_type(m20, m21, m22);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TMat33<U>& rhs) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        m_value[col] = col_type(rhs[col]);
+    }
+}
+
+// Construct from 3 column vectors.
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat33<T>::TMat33(const TVec3<A>& v0, const TVec3<B>& v1, const TVec3<C>& v2) {
+    m_value[0] = v0;
+    m_value[1] = v1;
+    m_value[2] = v2;
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(U const* rawArray) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        for (size_t row = 0; row < NUM_ROWS; ++row) {
+            m_value[col][row] = *rawArray++;
+        }
+    }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat33<T>::TMat33(const TQuaternion<U>& q) {
+    const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+    const U s = n > 0 ? 2/n : 0;
+    const U x = s*q.x;
+    const U y = s*q.y;
+    const U z = s*q.z;
+    const U xx = x*q.x;
+    const U xy = x*q.y;
+    const U xz = x*q.z;
+    const U xw = x*q.w;
+    const U yy = y*q.y;
+    const U yz = y*q.z;
+    const U yw = y*q.w;
+    const U zz = z*q.z;
+    const U zw = z*q.w;
+    m_value[0] = col_type(1-yy-zz,    xy+zw,    xz-yw);  // NOLINT
+    m_value[1] = col_type(  xy-zw,  1-xx-zz,    yz+xw);  // NOLINT
+    m_value[2] = col_type(  xz+yw,    yz-xw,  1-xx-yy);  // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::col_type PURE operator *(const TMat33<T>& lhs, const TVec3<U>& rhs) {
+    // Result is initialized to zero.
+    typename TMat33<U>::col_type result;
+    for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+        result += lhs[col] * rhs[col];
+    }
+    return result;
+}
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat33<U>::row_type PURE operator *(const TVec3<U>& lhs, const TMat33<T>& rhs) {
+    typename TMat33<U>::row_type result(TMat33<U>::row_type::NO_INIT);
+    for (size_t col = 0; col < TMat33<T>::NUM_COLS; ++col) {
+        result[col] = dot(lhs, rhs[col]);
+    }
+    return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(TMat33<T> lhs, U rhs) {
+    return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template<typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat33<T>>::type PURE
+operator*(U lhs, const TMat33<T>& rhs) {
+    return rhs * lhs;
+}
+
+//------------------------------------------------------------------------------
+template <typename T>
+CONSTEXPR TMat33<T> orthogonalize(const TMat33<T>& m) {
+    TMat33<T> ret(TMat33<T>::NO_INIT);
+    ret[0] = normalize(m[0]);
+    ret[2] = normalize(cross(ret[0], m[1]));
+    ret[1] = normalize(cross(ret[2], ret[0]));
+    return ret;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+CONSTEXPR typename TMat33<T>::col_type PURE diag(const TMat33<T>& m) {
+    return matrix::diag(m);
+}
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat33<double> mat3d;
+typedef details::TMat33<float> mat3;
+typedef details::TMat33<float> mat3f;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h
new file mode 100644
index 0000000..6119ba7
--- /dev/null
+++ b/libs/math/include/math/mat4.h
@@ -0,0 +1,586 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/mat3.h>
+#include <math/quat.h>
+#include <math/TMatHelpers.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits>
+
+#define PURE __attribute__((pure))
+
+#if __cplusplus >= 201402L
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+namespace android {
+// -------------------------------------------------------------------------------------
+namespace details {
+
+template<typename T>
+class TQuaternion;
+
+/**
+ * A 4x4 column-major matrix class.
+ *
+ * Conceptually a 4x4 matrix is a an array of 4 column double4:
+ *
+ * mat4 m =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cccc}
+ *      m[0] & m[1] & m[2] & m[3] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cccc}
+ *      m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+ *      m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+ *      m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+ *      m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *      =
+ *      \f$
+ *      \left(
+ *      \begin{array}{cccc}
+ *      m(0,0) & m(0,1) & m(0,2) & m(0,3) \\
+ *      m(1,0) & m(1,1) & m(1,2) & m(1,3) \\
+ *      m(2,0) & m(2,1) & m(2,2) & m(2,3) \\
+ *      m(3,0) & m(3,1) & m(3,2) & m(3,3) \\
+ *      \end{array}
+ *      \right)
+ *      \f$
+ *
+ * m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4.
+ *
+ */
+template <typename T>
+class TMat44 :  public TVecUnaryOperators<TMat44, T>,
+                public TVecComparisonOperators<TMat44, T>,
+                public TVecAddOperators<TMat44, T>,
+                public TMatProductOperators<TMat44, T>,
+                public TMatSquareFunctions<TMat44, T>,
+                public TMatTransform<TMat44, T>,
+                public TMatHelpers<TMat44, T>,
+                public TMatDebug<TMat44, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+    typedef TVec4<T> col_type;
+    typedef TVec4<T> row_type;
+
+    static constexpr size_t COL_SIZE = col_type::SIZE;  // size of a column (i.e.: number of rows)
+    static constexpr size_t ROW_SIZE = row_type::SIZE;  // size of a row (i.e.: number of columns)
+    static constexpr size_t NUM_ROWS = COL_SIZE;
+    static constexpr size_t NUM_COLS = ROW_SIZE;
+
+private:
+    /*
+     *  <--  N columns  -->
+     *
+     *  a[0][0] a[1][0] a[2][0] ... a[N][0]    ^
+     *  a[0][1] a[1][1] a[2][1] ... a[N][1]    |
+     *  a[0][2] a[1][2] a[2][2] ... a[N][2]  M rows
+     *  ...                                    |
+     *  a[0][M] a[1][M] a[2][M] ... a[N][M]    v
+     *
+     *  COL_SIZE = M
+     *  ROW_SIZE = N
+     *  m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ]
+     */
+
+    col_type m_value[NUM_COLS];
+
+public:
+    // array access
+    inline constexpr col_type const& operator[](size_t column) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(column < NUM_COLS);
+#endif
+        return m_value[column];
+    }
+
+    inline col_type& operator[](size_t column) {
+        assert(column < NUM_COLS);
+        return m_value[column];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TMat44(const TMat44&) = default;
+    ~TMat44() = default;
+    TMat44& operator = (const TMat44&) = default;
+
+    /*
+     *  constructors
+     */
+
+    // leaves object uninitialized. use with caution.
+    explicit constexpr TMat44(no_init)
+            : m_value{ col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT),
+                       col_type(col_type::NO_INIT) } {}
+
+    /** initialize to identity.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      1 & 0 & 0 & 0 \\
+     *      0 & 1 & 0 & 0 \\
+     *      0 & 0 & 1 & 0 \\
+     *      0 & 0 & 0 & 1 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    CONSTEXPR TMat44();
+
+    /** initialize to Identity*scalar.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      v & 0 & 0 & 0 \\
+     *      0 & v & 0 & 0 \\
+     *      0 & 0 & v & 0 \\
+     *      0 & 0 & 0 & v \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template<typename U>
+    explicit CONSTEXPR TMat44(U v);
+
+    /** sets the diagonal to a vector.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      v[0] & 0 & 0 & 0 \\
+     *      0 & v[1] & 0 & 0 \\
+     *      0 & 0 & v[2] & 0 \\
+     *      0 & 0 & 0 & v[3] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat44(const TVec4<U>& v);
+
+    // construct from another matrix of the same size
+    template <typename U>
+    explicit CONSTEXPR TMat44(const TMat44<U>& rhs);
+
+    /** construct from 4 column vectors.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      v0 & v1 & v2 & v3 \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <typename A, typename B, typename C, typename D>
+    CONSTEXPR TMat44(const TVec4<A>& v0, const TVec4<B>& v1, const TVec4<C>& v2, const TVec4<D>& v3);
+
+    /** construct from 16 elements in column-major form.
+     *
+     *      \f$
+     *      \left(
+     *      \begin{array}{cccc}
+     *      m[0][0] & m[1][0] & m[2][0] & m[3][0] \\
+     *      m[0][1] & m[1][1] & m[2][1] & m[3][1] \\
+     *      m[0][2] & m[1][2] & m[2][2] & m[3][2] \\
+     *      m[0][3] & m[1][3] & m[2][3] & m[3][3] \\
+     *      \end{array}
+     *      \right)
+     *      \f$
+     */
+    template <
+        typename A, typename B, typename C, typename D,
+        typename E, typename F, typename G, typename H,
+        typename I, typename J, typename K, typename L,
+        typename M, typename N, typename O, typename P>
+    CONSTEXPR TMat44(
+            A m00, B m01, C m02, D m03,
+            E m10, F m11, G m12, H m13,
+            I m20, J m21, K m22, L m23,
+            M m30, N m31, O m32, P m33);
+
+    /**
+     * construct from a quaternion
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat44(const TQuaternion<U>& q);
+
+    /**
+     * construct from a C array in column major form.
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat44(U const* rawArray);
+
+    /**
+     * construct from a 3x3 matrix
+     */
+    template <typename U>
+    explicit CONSTEXPR TMat44(const TMat33<U>& matrix);
+
+    /**
+     * construct from a 3x3 matrix and 3d translation
+     */
+    template <typename U, typename V>
+    CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec3<V>& translation);
+
+    /**
+     * construct from a 3x3 matrix and 4d last column.
+     */
+    template <typename U, typename V>
+    CONSTEXPR TMat44(const TMat33<U>& matrix, const TVec4<V>& column3);
+
+    /*
+     *  helpers
+     */
+
+    static CONSTEXPR TMat44 ortho(T left, T right, T bottom, T top, T near, T far);
+
+    static CONSTEXPR TMat44 frustum(T left, T right, T bottom, T top, T near, T far);
+
+    enum class Fov {
+        HORIZONTAL,
+        VERTICAL
+    };
+    static CONSTEXPR TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL);
+
+    template <typename A, typename B, typename C>
+    static CONSTEXPR TMat44 lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up);
+
+    template <typename A>
+    static CONSTEXPR TVec3<A> project(const TMat44& projectionMatrix, TVec3<A> vertice) {
+        TVec4<A> r = projectionMatrix * TVec4<A>{ vertice, 1 };
+        return r.xyz / r.w;
+    }
+
+    template <typename A>
+    static CONSTEXPR TVec4<A> project(const TMat44& projectionMatrix, TVec4<A> vertice) {
+        vertice = projectionMatrix * vertice;
+        return { vertice.xyz / vertice.w, 1 };
+    }
+
+    /**
+     * Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix
+     */
+    inline constexpr TMat33<T> upperLeft() const {
+        return TMat33<T>(m_value[0].xyz, m_value[1].xyz, m_value[2].xyz);
+    }
+};
+
+// ----------------------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------------------
+
+// Since the matrix code could become pretty big quickly, we don't inline most
+// operations.
+
+template <typename T>
+CONSTEXPR TMat44<T>::TMat44() {
+    m_value[0] = col_type(1, 0, 0, 0);
+    m_value[1] = col_type(0, 1, 0, 0);
+    m_value[2] = col_type(0, 0, 1, 0);
+    m_value[3] = col_type(0, 0, 0, 1);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U v) {
+    m_value[0] = col_type(v, 0, 0, 0);
+    m_value[1] = col_type(0, v, 0, 0);
+    m_value[2] = col_type(0, 0, v, 0);
+    m_value[3] = col_type(0, 0, 0, v);
+}
+
+template<typename T>
+template<typename U>
+CONSTEXPR TMat44<T>::TMat44(const TVec4<U>& v) {
+    m_value[0] = col_type(v.x, 0, 0, 0);
+    m_value[1] = col_type(0, v.y, 0, 0);
+    m_value[2] = col_type(0, 0, v.z, 0);
+    m_value[3] = col_type(0, 0, 0, v.w);
+}
+
+// construct from 16 scalars
+template<typename T>
+template <
+    typename A, typename B, typename C, typename D,
+    typename E, typename F, typename G, typename H,
+    typename I, typename J, typename K, typename L,
+    typename M, typename N, typename O, typename P>
+CONSTEXPR TMat44<T>::TMat44(
+        A m00, B m01, C m02, D m03,
+        E m10, F m11, G m12, H m13,
+        I m20, J m21, K m22, L m23,
+        M m30, N m31, O m32, P m33) {
+    m_value[0] = col_type(m00, m01, m02, m03);
+    m_value[1] = col_type(m10, m11, m12, m13);
+    m_value[2] = col_type(m20, m21, m22, m23);
+    m_value[3] = col_type(m30, m31, m32, m33);
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat44<U>& rhs) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        m_value[col] = col_type(rhs[col]);
+    }
+}
+
+// Construct from 4 column vectors.
+template <typename T>
+template <typename A, typename B, typename C, typename D>
+CONSTEXPR TMat44<T>::TMat44(
+        const TVec4<A>& v0, const TVec4<B>& v1,
+        const TVec4<C>& v2, const TVec4<D>& v3) {
+    m_value[0] = col_type(v0);
+    m_value[1] = col_type(v1);
+    m_value[2] = col_type(v2);
+    m_value[3] = col_type(v3);
+}
+
+// Construct from raw array, in column-major form.
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(U const* rawArray) {
+    for (size_t col = 0; col < NUM_COLS; ++col) {
+        for (size_t row = 0; row < NUM_ROWS; ++row) {
+            m_value[col][row] = *rawArray++;
+        }
+    }
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TQuaternion<U>& q) {
+    const U n = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+    const U s = n > 0 ? 2/n : 0;
+    const U x = s*q.x;
+    const U y = s*q.y;
+    const U z = s*q.z;
+    const U xx = x*q.x;
+    const U xy = x*q.y;
+    const U xz = x*q.z;
+    const U xw = x*q.w;
+    const U yy = y*q.y;
+    const U yz = y*q.z;
+    const U yw = y*q.w;
+    const U zz = z*q.z;
+    const U zw = z*q.w;
+    m_value[0] = col_type(1-yy-zz,    xy+zw,    xz-yw,   0);
+    m_value[1] = col_type(  xy-zw,  1-xx-zz,    yz+xw,   0);  // NOLINT
+    m_value[2] = col_type(  xz+yw,    yz-xw,  1-xx-yy,   0);  // NOLINT
+    m_value[3] = col_type(      0,        0,        0,   1);  // NOLINT
+}
+
+template <typename T>
+template <typename U>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m) {
+    m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+    m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+    m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+    m_value[3] = col_type(      0,       0,       0, 1);  // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec3<V>& v) {
+    m_value[0] = col_type(m[0][0], m[0][1], m[0][2], 0);
+    m_value[1] = col_type(m[1][0], m[1][1], m[1][2], 0);
+    m_value[2] = col_type(m[2][0], m[2][1], m[2][2], 0);
+    m_value[3] = col_type(   v[0],    v[1],    v[2], 1);  // NOLINT
+}
+
+template <typename T>
+template <typename U, typename V>
+CONSTEXPR TMat44<T>::TMat44(const TMat33<U>& m, const TVec4<V>& v) {
+    m_value[0] = col_type(m[0][0], m[0][1], m[0][2],    0);  // NOLINT
+    m_value[1] = col_type(m[1][0], m[1][1], m[1][2],    0);  // NOLINT
+    m_value[2] = col_type(m[2][0], m[2][1], m[2][2],    0);  // NOLINT
+    m_value[3] = col_type(   v[0],    v[1],    v[2], v[3]);  // NOLINT
+}
+
+// ----------------------------------------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------------------------------------
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::ortho(T left, T right, T bottom, T top, T near, T far) {
+    TMat44<T> m;
+    m[0][0] =  2 / (right - left);
+    m[1][1] =  2 / (top   - bottom);
+    m[2][2] = -2 / (far   - near);
+    m[3][0] = -(right + left)   / (right - left);
+    m[3][1] = -(top   + bottom) / (top   - bottom);
+    m[3][2] = -(far   + near)   / (far   - near);
+    return m;
+}
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) {
+    TMat44<T> m;
+    m[0][0] =  (2 * near) / (right - left);
+    m[1][1] =  (2 * near) / (top   - bottom);
+    m[2][0] =  (right + left)   / (right - left);
+    m[2][1] =  (top   + bottom) / (top   - bottom);
+    m[2][2] = -(far   + near)   / (far   - near);
+    m[2][3] = -1;
+    m[3][2] = -(2 * far * near) / (far   - near);
+    m[3][3] =  0;
+    return m;
+}
+
+template <typename T>
+CONSTEXPR TMat44<T> TMat44<T>::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) {
+    T h;
+    T w;
+
+    if (direction == TMat44::Fov::VERTICAL) {
+        h = std::tan(fov * M_PI / 360.0f) * near;
+        w = h * aspect;
+    } else {
+        w = std::tan(fov * M_PI / 360.0f) * near;
+        h = w / aspect;
+    }
+    return frustum(-w, w, -h, h, near, far);
+}
+
+/*
+ * Returns a matrix representing the pose of a virtual camera looking towards -Z in its
+ * local Y-up coordinate system. "eye" is where the camera is located, "center" is the points its
+ * looking at and "up" defines where the Y axis of the camera's local coordinate system is.
+ */
+template <typename T>
+template <typename A, typename B, typename C>
+CONSTEXPR TMat44<T> TMat44<T>::lookAt(const TVec3<A>& eye, const TVec3<B>& center, const TVec3<C>& up) {
+    TVec3<T> z_axis(normalize(center - eye));
+    TVec3<T> norm_up(normalize(up));
+    if (std::abs(dot(z_axis, norm_up)) > 0.999) {
+        // Fix up vector if we're degenerate (looking straight up, basically)
+        norm_up = { norm_up.z, norm_up.x, norm_up.y };
+    }
+    TVec3<T> x_axis(normalize(cross(z_axis, norm_up)));
+    TVec3<T> y_axis(cross(x_axis, z_axis));
+    return TMat44<T>(
+            TVec4<T>(x_axis, 0),
+            TVec4<T>(y_axis, 0),
+            TVec4<T>(-z_axis, 0),
+            TVec4<T>(eye, 1));
+}
+
+// ----------------------------------------------------------------------------------------
+// Arithmetic operators outside of class
+// ----------------------------------------------------------------------------------------
+
+/* We use non-friend functions here to prevent the compiler from using
+ * implicit conversions, for instance of a scalar to a vector. The result would
+ * not be what the caller expects.
+ *
+ * Also note that the order of the arguments in the inner loop is important since
+ * it determines the output type (only relevant when T != U).
+ */
+
+// matrix * column-vector, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec4<U>& rhs) {
+    // Result is initialized to zero.
+    typename TMat44<T>::col_type result;
+    for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+        result += lhs[col] * rhs[col];
+    }
+    return result;
+}
+
+// mat44 * vec3, result is vec3( mat44 * {vec3, 1} )
+template <typename T, typename U>
+CONSTEXPR typename TMat44<T>::col_type PURE operator *(const TMat44<T>& lhs, const TVec3<U>& rhs) {
+    return lhs * TVec4<U>{ rhs, 1 };
+}
+
+
+// row-vector * matrix, result is a vector of the same type than the input vector
+template <typename T, typename U>
+CONSTEXPR typename TMat44<U>::row_type PURE operator *(const TVec4<U>& lhs, const TMat44<T>& rhs) {
+    typename TMat44<U>::row_type result(TMat44<U>::row_type::NO_INIT);
+    for (size_t col = 0; col < TMat44<T>::NUM_COLS; ++col) {
+        result[col] = dot(lhs, rhs[col]);
+    }
+    return result;
+}
+
+// matrix * scalar, result is a matrix of the same type than the input matrix
+template <typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(TMat44<T> lhs, U rhs) {
+    return lhs *= rhs;
+}
+
+// scalar * matrix, result is a matrix of the same type than the input matrix
+template <typename T, typename U>
+constexpr typename std::enable_if<std::is_arithmetic<U>::value, TMat44<T>>::type PURE
+operator *(U lhs, const TMat44<T>& rhs) {
+    return rhs * lhs;
+}
+
+// ----------------------------------------------------------------------------------------
+
+/* FIXME: this should go into TMatSquareFunctions<> but for some reason
+ * BASE<T>::col_type is not accessible from there (???)
+ */
+template<typename T>
+typename TMat44<T>::col_type PURE diag(const TMat44<T>& m) {
+    return matrix::diag(m);
+}
+
+} // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TMat44<double> mat4d;
+typedef details::TMat44<float> mat4;
+typedef details::TMat44<float> mat4f;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#undef PURE
+#undef CONSTEXPR
diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h
new file mode 100644
index 0000000..1936a2b
--- /dev/null
+++ b/libs/math/include/math/quat.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/half.h>
+#include <math/TQuatHelpers.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifndef PURE
+#define PURE __attribute__((pure))
+#endif
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TQuaternion : public TVecAddOperators<TQuaternion, T>,
+                    public TVecUnaryOperators<TQuaternion, T>,
+                    public TVecComparisonOperators<TQuaternion, T>,
+                    public TQuatProductOperators<TQuaternion, T>,
+                    public TQuatFunctions<TQuaternion, T>,
+                    public TQuatDebug<TQuaternion, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+
+    /*
+     * quaternion internals stored as:
+     *
+     * q = w + xi + yj + zk
+     *
+     *  q[0] = x;
+     *  q[1] = y;
+     *  q[2] = z;
+     *  q[3] = w;
+     *
+     */
+    union {
+        struct { T x, y, z, w; };
+        TVec4<T> xyzw;
+        TVec3<T> xyz;
+        TVec2<T> xy;
+    };
+
+    enum { SIZE = 4 };
+    inline constexpr static size_type size() { return SIZE; }
+
+    // array access
+    inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(i < SIZE);
+#endif
+        return (&x)[i];
+    }
+
+    inline T& operator[](size_t i) {
+        assert(i < SIZE);
+        return (&x)[i];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TQuaternion(const TQuaternion&) = default;
+    ~TQuaternion() = default;
+    TQuaternion& operator = (const TQuaternion&) = default;
+
+    // constructors
+
+    // leaves object uninitialized. use with caution.
+    explicit
+    constexpr TQuaternion(no_init) : xyzw(TVec4<T>::NO_INIT) {}
+
+    // default constructor. sets all values to zero.
+    constexpr TQuaternion() : x(0), y(0), z(0), w(0) { }
+
+    // handles implicit conversion to a tvec4. must not be explicit.
+    template<typename A>
+    constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {
+        static_assert(std::is_arithmetic<A>::value, "requires arithmetic type");
+    }
+
+    // initialize from 4 values to w + xi + yj + zk
+    template<typename A, typename B, typename C, typename D>
+    constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) { }
+
+    // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w
+    template<typename A, typename B>
+    constexpr TQuaternion(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+    // initialize from a double4
+    template<typename A>
+    constexpr explicit TQuaternion(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+    // initialize from a quaternion of a different type
+    template<typename A>
+    constexpr explicit TQuaternion(const TQuaternion<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+
+    // conjugate operator
+    constexpr TQuaternion operator~() const {
+        return conj(*this);
+    }
+
+    // constructs a quaternion from an axis and angle
+    template <typename A, typename B>
+    constexpr static TQuaternion PURE fromAxisAngle(const TVec3<A>& axis, B angle) {
+        return TQuaternion(std::sin(angle*0.5) * normalize(axis), std::cos(angle*0.5));
+    }
+};
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TQuaternion<double> quatd;
+typedef details::TQuaternion<float> quat;
+typedef details::TQuaternion<float> quatf;
+typedef details::TQuaternion<half> quath;
+
+constexpr inline quat operator"" _i(long double v) {
+    return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(long double v) {
+    return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(long double v) {
+    return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quat operator"" _i(unsigned long long v) {  // NOLINT
+    return quat(0, static_cast<float>(v), 0, 0);
+}
+constexpr inline quat operator"" _j(unsigned long long v) {  // NOLINT
+    return quat(0, 0, static_cast<float>(v), 0);
+}
+constexpr inline quat operator"" _k(unsigned long long v) {  // NOLINT
+    return quat(0, 0, 0, static_cast<float>(v));
+}
+
+constexpr inline quatd operator"" _id(long double v) {
+    return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(long double v) {
+    return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(long double v) {
+    return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+constexpr inline quatd operator"" _id(unsigned long long v) {  // NOLINT
+    return quatd(0, static_cast<double>(v), 0, 0);
+}
+constexpr inline quatd operator"" _jd(unsigned long long v) {  // NOLINT
+    return quatd(0, 0, static_cast<double>(v), 0);
+}
+constexpr inline quatd operator"" _kd(unsigned long long v) {  // NOLINT
+    return quatd(0, 0, 0, static_cast<double>(v));
+}
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#pragma clang diagnostic pop
+
+#undef PURE
diff --git a/libs/math/include/math/scalar.h b/libs/math/include/math/scalar.h
new file mode 100644
index 0000000..2eced92
--- /dev/null
+++ b/libs/math/include/math/scalar.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <cmath>
+
+namespace android {
+
+template<typename T>
+static constexpr T saturate(T v) noexcept {
+    return T(std::min(T(1), std::max(T(0), v)));
+}
+
+template<typename T>
+static constexpr T clamp(T v, T min, T max) noexcept {
+    return T(std::min(max, std::max(min, v)));
+}
+
+template<typename T>
+static constexpr T mix(T x, T y, T a) noexcept {
+    return x * (T(1) - a) + y * a;
+}
+
+template<typename T>
+static constexpr T lerp(T x, T y, T a) noexcept {
+    return mix(x, y, a);
+}
+
+} // namespace std
diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h
new file mode 100644
index 0000000..a347633
--- /dev/null
+++ b/libs/math/include/math/vec2.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/TVecHelpers.h>
+#include <math/half.h>
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <type_traits>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec2 :   public TVecProductOperators<TVec2, T>,
+                public TVecAddOperators<TVec2, T>,
+                public TVecUnaryOperators<TVec2, T>,
+                public TVecComparisonOperators<TVec2, T>,
+                public TVecFunctions<TVec2, T>,
+                public TVecDebug<TVec2, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+
+    union {
+        struct { T x, y; };
+        struct { T s, t; };
+        struct { T r, g; };
+    };
+
+    static constexpr size_t SIZE = 2;
+    inline constexpr size_type size() const { return SIZE; }
+
+    // array access
+    inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(i < SIZE);
+#endif
+        return (&x)[i];
+    }
+
+    inline T& operator[](size_t i) {
+        assert(i < SIZE);
+        return (&x)[i];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TVec2(const TVec2&) = default;
+    ~TVec2() = default;
+    TVec2& operator = (const TVec2&) = default;
+
+    // constructors
+
+    // leaves object uninitialized. use with caution.
+    explicit
+    constexpr TVec2(no_init) { }
+
+    // default constructor
+    constexpr TVec2() : x(0), y(0) { }
+
+    // handles implicit conversion to a tvec4. must not be explicit.
+    template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+    constexpr TVec2(A v) : x(v), y(v) { }
+
+    template<typename A, typename B>
+    constexpr TVec2(A x, B y) : x(x), y(y) { }
+
+    template<typename A>
+    explicit
+    constexpr TVec2(const TVec2<A>& v) : x(v.x), y(v.y) { }
+
+    // cross product works only on vectors of size 2 or 3
+    template<typename RT>
+    friend inline
+    constexpr value_type cross(const TVec2& u, const TVec2<RT>& v) {
+        return value_type(u.x*v.y - u.y*v.x);
+    }
+};
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec2<double> double2;
+typedef details::TVec2<float> float2;
+typedef details::TVec2<float> vec2;
+typedef details::TVec2<half> half2;
+typedef details::TVec2<int32_t> int2;
+typedef details::TVec2<uint32_t> uint2;
+typedef details::TVec2<int16_t> short2;
+typedef details::TVec2<uint16_t> ushort2;
+typedef details::TVec2<int8_t> byte2;
+typedef details::TVec2<uint8_t> ubyte2;
+typedef details::TVec2<bool> bool2;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h
new file mode 100644
index 0000000..009fd84
--- /dev/null
+++ b/libs/math/include/math/vec3.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/vec2.h>
+#include <math/half.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class TVec3 :   public TVecProductOperators<TVec3, T>,
+                public TVecAddOperators<TVec3, T>,
+                public TVecUnaryOperators<TVec3, T>,
+                public TVecComparisonOperators<TVec3, T>,
+                public TVecFunctions<TVec3, T>,
+                public TVecDebug<TVec3, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+
+    union {
+        struct { T x, y, z; };
+        struct { T s, t, p; };
+        struct { T r, g, b; };
+        TVec2<T> xy;
+        TVec2<T> st;
+        TVec2<T> rg;
+    };
+
+    static constexpr size_t SIZE = 3;
+    inline constexpr size_type size() const { return SIZE; }
+
+    // array access
+    inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(i < SIZE);
+#endif
+        return (&x)[i];
+    }
+
+    inline T& operator[](size_t i) {
+        assert(i < SIZE);
+        return (&x)[i];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TVec3(const TVec3&) = default;
+    ~TVec3() = default;
+    TVec3& operator = (const TVec3&) = default;
+
+    // constructors
+    // leaves object uninitialized. use with caution.
+    explicit
+    constexpr TVec3(no_init) { }
+
+    // default constructor
+    constexpr TVec3() : x(0), y(0), z(0) { }
+
+    // handles implicit conversion to a tvec4. must not be explicit.
+    template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+    constexpr TVec3(A v) : x(v), y(v), z(v) { }
+
+    template<typename A, typename B, typename C>
+    constexpr TVec3(A x, B y, C z) : x(x), y(y), z(z) { }
+
+    template<typename A, typename B>
+    constexpr TVec3(const TVec2<A>& v, B z) : x(v.x), y(v.y), z(z) { }
+
+    template<typename A>
+    explicit
+    constexpr TVec3(const TVec3<A>& v) : x(v.x), y(v.y), z(v.z) { }
+
+    // cross product works only on vectors of size 3
+    template <typename RT>
+    friend inline
+    constexpr TVec3 cross(const TVec3& u, const TVec3<RT>& v) {
+        return TVec3(
+                u.y*v.z - u.z*v.y,
+                u.z*v.x - u.x*v.z,
+                u.x*v.y - u.y*v.x);
+    }
+};
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec3<double> double3;
+typedef details::TVec3<float> float3;
+typedef details::TVec3<float> vec3;
+typedef details::TVec3<half> half3;
+typedef details::TVec3<int32_t> int3;
+typedef details::TVec3<uint32_t> uint3;
+typedef details::TVec3<int16_t> short3;
+typedef details::TVec3<uint16_t> ushort3;
+typedef details::TVec3<int8_t> byte3;
+typedef details::TVec3<uint8_t> ubyte3;
+typedef details::TVec3<bool> bool3;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec4.h b/libs/math/include/math/vec4.h
new file mode 100644
index 0000000..1e279fe
--- /dev/null
+++ b/libs/math/include/math/vec4.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/vec3.h>
+#include <math/half.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
+#pragma clang diagnostic ignored "-Wnested-anon-types"
+
+namespace android {
+// -------------------------------------------------------------------------------------
+
+namespace details {
+
+template <typename T>
+class  TVec4 :  public TVecProductOperators<TVec4, T>,
+                public TVecAddOperators<TVec4, T>,
+                public TVecUnaryOperators<TVec4, T>,
+                public TVecComparisonOperators<TVec4, T>,
+                public TVecFunctions<TVec4, T>,
+                public TVecDebug<TVec4, T> {
+public:
+    enum no_init { NO_INIT };
+    typedef T value_type;
+    typedef T& reference;
+    typedef T const& const_reference;
+    typedef size_t size_type;
+
+    union {
+        struct { T x, y, z, w; };
+        struct { T s, t, p, q; };
+        struct { T r, g, b, a; };
+        TVec2<T> xy;
+        TVec2<T> st;
+        TVec2<T> rg;
+        TVec3<T> xyz;
+        TVec3<T> stp;
+        TVec3<T> rgb;
+    };
+
+    static constexpr size_t SIZE = 4;
+    inline constexpr size_type size() const { return SIZE; }
+
+    // array access
+    inline constexpr T const& operator[](size_t i) const {
+#if __cplusplus >= 201402L
+        // only possible in C++0x14 with constexpr
+        assert(i < SIZE);
+#endif
+        return (&x)[i];
+    }
+
+    inline T& operator[](size_t i) {
+        assert(i < SIZE);
+        return (&x)[i];
+    }
+
+    // -----------------------------------------------------------------------
+    // we want the compiler generated versions for these...
+    TVec4(const TVec4&) = default;
+    ~TVec4() = default;
+    TVec4& operator = (const TVec4&) = default;
+
+    // constructors
+
+    // leaves object uninitialized. use with caution.
+    explicit
+    constexpr TVec4(no_init) { }
+
+    // default constructor
+    constexpr TVec4() : x(0), y(0), z(0), w(0) { }
+
+    // handles implicit conversion to a tvec4. must not be explicit.
+    template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type>
+    constexpr TVec4(A v) : x(v), y(v), z(v), w(v) { }
+
+    template<typename A, typename B, typename C, typename D>
+    constexpr TVec4(A x, B y, C z, D w) : x(x), y(y), z(z), w(w) { }
+
+    template<typename A, typename B, typename C>
+    constexpr TVec4(const TVec2<A>& v, B z, C w) : x(v.x), y(v.y), z(z), w(w) { }
+
+    template<typename A, typename B>
+    constexpr TVec4(const TVec3<A>& v, B w) : x(v.x), y(v.y), z(v.z), w(w) { }
+
+    template<typename A>
+    explicit
+    constexpr TVec4(const TVec4<A>& v) : x(v.x), y(v.y), z(v.z), w(v.w) { }
+};
+
+}  // namespace details
+
+// ----------------------------------------------------------------------------------------
+
+typedef details::TVec4<double> double4;
+typedef details::TVec4<float> float4;
+typedef details::TVec4<float> vec4;
+typedef details::TVec4<half> half4;
+typedef details::TVec4<int32_t> int4;
+typedef details::TVec4<uint32_t> uint4;
+typedef details::TVec4<int16_t> short4;
+typedef details::TVec4<uint16_t> ushort4;
+typedef details::TVec4<int8_t> byte4;
+typedef details::TVec4<uint8_t> ubyte4;
+typedef details::TVec4<bool> bool4;
+
+// ----------------------------------------------------------------------------------------
+}  // namespace android
+
+#pragma clang diagnostic pop
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
new file mode 100644
index 0000000..0ed24a2
--- /dev/null
+++ b/libs/math/tests/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "vec_test",
+    srcs: ["vec_test.cpp"],
+    static_libs: ["libmath"],
+}
+
+cc_test {
+    name: "mat_test",
+    srcs: ["mat_test.cpp"],
+    static_libs: ["libmath"],
+}
+
+cc_test {
+    name: "half_test",
+    srcs: ["half_test.cpp"],
+    static_libs: ["libmath"],
+}
+
+cc_test {
+    name: "quat_test",
+    srcs: ["quat_test.cpp"],
+    static_libs: ["libmath"],
+}
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
new file mode 100644
index 0000000..496a7ef
--- /dev/null
+++ b/libs/math/tests/half_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HalfTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/half.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HalfTest : public testing::Test {
+protected:
+};
+
+TEST_F(HalfTest, Basics) {
+
+    EXPECT_EQ(2UL, sizeof(half));
+
+    // test +/- zero
+    EXPECT_EQ(0x0000, half( 0.0f).getBits());
+    EXPECT_EQ(0x8000, half(-0.0f).getBits());
+
+    // test nan
+    EXPECT_EQ(0x7e00, half(NAN).getBits());
+
+    // test +/- infinity
+    EXPECT_EQ(0x7C00, half( std::numeric_limits<float>::infinity()).getBits());
+    EXPECT_EQ(0xFC00, half(-std::numeric_limits<float>::infinity()).getBits());
+
+    // test a few known values
+    EXPECT_EQ(0x3C01, half(1.0009765625).getBits());
+    EXPECT_EQ(0xC000, half(-2).getBits());
+    EXPECT_EQ(0x0400, half(6.10352e-5).getBits());
+    EXPECT_EQ(0x7BFF, half(65504).getBits());
+    EXPECT_EQ(0x3555, half(1.0f/3).getBits());
+
+    // numeric limits
+    EXPECT_EQ(0x7C00, std::numeric_limits<half>::infinity().getBits());
+    EXPECT_EQ(0x0400, std::numeric_limits<half>::min().getBits());
+    EXPECT_EQ(0x7BFF, std::numeric_limits<half>::max().getBits());
+    EXPECT_EQ(0xFBFF, std::numeric_limits<half>::lowest().getBits());
+
+    // denormals (flushed to zero)
+    EXPECT_EQ(0x0000, half( 6.09756e-5).getBits());      // if handled, should be: 0x03FF
+    EXPECT_EQ(0x0000, half( 5.96046e-8).getBits());      // if handled, should be: 0x0001
+    EXPECT_EQ(0x8000, half(-6.09756e-5).getBits());      // if handled, should be: 0x83FF
+    EXPECT_EQ(0x8000, half(-5.96046e-8).getBits());      // if handled, should be: 0x8001
+
+    // test all exactly representable integers
+    for (int i=-2048 ; i<= 2048 ; ++i) {
+        half h = i;
+        EXPECT_EQ(i, float(h));
+    }
+}
+
+TEST_F(HalfTest, Literals) {
+    half one = 1.0_hf;
+    half pi = 3.1415926_hf;
+    half minusTwo = -2.0_hf;
+
+    EXPECT_EQ(half(1.0f), one);
+    EXPECT_EQ(half(3.1415926), pi);
+    EXPECT_EQ(half(-2.0f), minusTwo);
+}
+
+
+TEST_F(HalfTest, Vec) {
+    float4 f4(1,2,3,4);
+    half4 h4(f4);
+    half3 h3(f4.xyz);
+    half2 h2(f4.xy);
+
+    EXPECT_EQ(f4, h4);
+    EXPECT_EQ(f4.xyz, h3);
+    EXPECT_EQ(f4.xy, h2);
+}
+
+}; // namespace android
diff --git a/libs/math/tests/mat_test.cpp b/libs/math/tests/mat_test.cpp
new file mode 100644
index 0000000..c365366
--- /dev/null
+++ b/libs/math/tests/mat_test.cpp
@@ -0,0 +1,692 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MatTest"
+
+#include <stdlib.h>
+
+#include <limits>
+#include <random>
+#include <functional>
+
+#include <gtest/gtest.h>
+
+#include <math/mat2.h>
+#include <math/mat4.h>
+
+namespace android {
+
+class MatTest : public testing::Test {
+protected:
+};
+
+TEST_F(MatTest, Basics) {
+    mat4 m0;
+    EXPECT_EQ(sizeof(mat4), sizeof(float)*16);
+}
+
+TEST_F(MatTest, ComparisonOps) {
+    mat4 m0;
+    mat4 m1(2);
+
+    EXPECT_TRUE(m0 == m0);
+    EXPECT_TRUE(m0 != m1);
+    EXPECT_FALSE(m0 != m0);
+    EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(MatTest, Constructors) {
+    mat4 m0;
+    ASSERT_EQ(m0[0].x, 1);
+    ASSERT_EQ(m0[0].y, 0);
+    ASSERT_EQ(m0[0].z, 0);
+    ASSERT_EQ(m0[0].w, 0);
+    ASSERT_EQ(m0[1].x, 0);
+    ASSERT_EQ(m0[1].y, 1);
+    ASSERT_EQ(m0[1].z, 0);
+    ASSERT_EQ(m0[1].w, 0);
+    ASSERT_EQ(m0[2].x, 0);
+    ASSERT_EQ(m0[2].y, 0);
+    ASSERT_EQ(m0[2].z, 1);
+    ASSERT_EQ(m0[2].w, 0);
+    ASSERT_EQ(m0[3].x, 0);
+    ASSERT_EQ(m0[3].y, 0);
+    ASSERT_EQ(m0[3].z, 0);
+    ASSERT_EQ(m0[3].w, 1);
+
+    mat4 m1(2);
+    mat4 m2(vec4(2));
+    mat4 m3(m2);
+
+    EXPECT_EQ(m1, m2);
+    EXPECT_EQ(m2, m3);
+    EXPECT_EQ(m3, m1);
+
+    mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4));
+}
+
+TEST_F(MatTest, ArithmeticOps) {
+    mat4 m0;
+    mat4 m1(2);
+    mat4 m2(vec4(2));
+
+    m1 += m2;
+    EXPECT_EQ(mat4(4), m1);
+
+    m2 -= m1;
+    EXPECT_EQ(mat4(-2), m2);
+
+    m1 *= 2;
+    EXPECT_EQ(mat4(8), m1);
+
+    m1 /= 2;
+    EXPECT_EQ(mat4(4), m1);
+
+    m0 = -m0;
+    EXPECT_EQ(mat4(-1), m0);
+}
+
+TEST_F(MatTest, UnaryOps) {
+    const mat4 identity;
+    mat4 m0;
+
+    m0 = -m0;
+    EXPECT_EQ(mat4(vec4(-1, 0,  0,  0),
+                   vec4(0, -1,  0,  0),
+                   vec4(0,  0, -1,  0),
+                   vec4(0,  0,  0, -1)), m0);
+
+    m0 = -m0;
+    EXPECT_EQ(identity, m0);
+}
+
+TEST_F(MatTest, MiscOps) {
+    const mat4 identity;
+    mat4 m0;
+    EXPECT_EQ(4, trace(m0));
+
+    mat4 m1(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
+    mat4 m2(vec4(1, 5, 9, 13), vec4(2, 6, 10, 14), vec4(3, 7, 11, 15), vec4(4, 8, 12, 16));
+    EXPECT_EQ(m1, transpose(m2));
+    EXPECT_EQ(m2, transpose(m1));
+    EXPECT_EQ(vec4(1, 6, 11, 16), diag(m1));
+
+    EXPECT_EQ(identity, inverse(identity));
+
+    mat4 m3(vec4(4, 3, 0, 0), vec4(3, 2, 0, 0), vec4(0, 0, 1, 0), vec4(0, 0, 0, 1));
+    mat4 m3i(inverse(m3));
+    EXPECT_FLOAT_EQ(-2, m3i[0][0]);
+    EXPECT_FLOAT_EQ(3,  m3i[0][1]);
+    EXPECT_FLOAT_EQ(3,  m3i[1][0]);
+    EXPECT_FLOAT_EQ(-4, m3i[1][1]);
+
+    mat4 m3ii(inverse(m3i));
+    EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
+    EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
+    EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
+    EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
+
+    EXPECT_EQ(m1, m1*identity);
+
+
+    for (size_t c=0 ; c<4 ; c++) {
+        for (size_t r=0 ; r<4 ; r++) {
+            EXPECT_FLOAT_EQ(m1[c][r], m1(r, c));
+        }
+    }
+}
+
+TEST_F(MatTest, ElementAccess) {
+    mat4 m(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
+    for (size_t c=0 ; c<4 ; c++) {
+        for (size_t r=0 ; r<4 ; r++) {
+            EXPECT_FLOAT_EQ(m[c][r], m(r, c));
+        }
+    }
+
+    m(3,2) = 100;
+    EXPECT_FLOAT_EQ(m[2][3], 100);
+    EXPECT_FLOAT_EQ(m(3, 2), 100);
+}
+
+//------------------------------------------------------------------------------
+// MAT 3
+//------------------------------------------------------------------------------
+
+class Mat3Test : public testing::Test {
+protected:
+};
+
+TEST_F(Mat3Test, Basics) {
+    mat3 m0;
+    EXPECT_EQ(sizeof(mat3), sizeof(float)*9);
+}
+
+TEST_F(Mat3Test, ComparisonOps) {
+    mat3 m0;
+    mat3 m1(2);
+
+    EXPECT_TRUE(m0 == m0);
+    EXPECT_TRUE(m0 != m1);
+    EXPECT_FALSE(m0 != m0);
+    EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(Mat3Test, Constructors) {
+    mat3 m0;
+    ASSERT_EQ(m0[0].x, 1);
+    ASSERT_EQ(m0[0].y, 0);
+    ASSERT_EQ(m0[0].z, 0);
+    ASSERT_EQ(m0[1].x, 0);
+    ASSERT_EQ(m0[1].y, 1);
+    ASSERT_EQ(m0[1].z, 0);
+    ASSERT_EQ(m0[2].x, 0);
+    ASSERT_EQ(m0[2].y, 0);
+    ASSERT_EQ(m0[2].z, 1);
+
+    mat3 m1(2);
+    mat3 m2(vec3(2));
+    mat3 m3(m2);
+
+    EXPECT_EQ(m1, m2);
+    EXPECT_EQ(m2, m3);
+    EXPECT_EQ(m3, m1);
+}
+
+TEST_F(Mat3Test, ArithmeticOps) {
+    mat3 m0;
+    mat3 m1(2);
+    mat3 m2(vec3(2));
+
+    m1 += m2;
+    EXPECT_EQ(mat3(4), m1);
+
+    m2 -= m1;
+    EXPECT_EQ(mat3(-2), m2);
+
+    m1 *= 2;
+    EXPECT_EQ(mat3(8), m1);
+
+    m1 /= 2;
+    EXPECT_EQ(mat3(4), m1);
+
+    m0 = -m0;
+    EXPECT_EQ(mat3(-1), m0);
+}
+
+TEST_F(Mat3Test, UnaryOps) {
+    const mat3 identity;
+    mat3 m0;
+
+    m0 = -m0;
+    EXPECT_EQ(mat3(vec3(-1, 0,  0),
+                   vec3(0, -1,  0),
+                   vec3(0,  0, -1)), m0);
+
+    m0 = -m0;
+    EXPECT_EQ(identity, m0);
+}
+
+TEST_F(Mat3Test, MiscOps) {
+    const mat3 identity;
+    mat3 m0;
+    EXPECT_EQ(3, trace(m0));
+
+    mat3 m1(vec3(1, 2, 3), vec3(4, 5, 6), vec3(7, 8, 9));
+    mat3 m2(vec3(1, 4, 7), vec3(2, 5, 8), vec3(3, 6, 9));
+    EXPECT_EQ(m1, transpose(m2));
+    EXPECT_EQ(m2, transpose(m1));
+    EXPECT_EQ(vec3(1, 5, 9), diag(m1));
+
+    EXPECT_EQ(identity, inverse(identity));
+
+    mat3 m3(vec3(4, 3, 0), vec3(3, 2, 0), vec3(0, 0, 1));
+    mat3 m3i(inverse(m3));
+    EXPECT_FLOAT_EQ(-2, m3i[0][0]);
+    EXPECT_FLOAT_EQ(3,  m3i[0][1]);
+    EXPECT_FLOAT_EQ(3,  m3i[1][0]);
+    EXPECT_FLOAT_EQ(-4, m3i[1][1]);
+
+    mat3 m3ii(inverse(m3i));
+    EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
+    EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
+    EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
+    EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
+
+    EXPECT_EQ(m1, m1*identity);
+}
+
+//------------------------------------------------------------------------------
+// MAT 2
+//------------------------------------------------------------------------------
+
+class Mat2Test : public testing::Test {
+protected:
+};
+
+TEST_F(Mat2Test, Basics) {
+    mat2 m0;
+    EXPECT_EQ(sizeof(mat2), sizeof(float)*4);
+}
+
+TEST_F(Mat2Test, ComparisonOps) {
+    mat2 m0;
+    mat2 m1(2);
+
+    EXPECT_TRUE(m0 == m0);
+    EXPECT_TRUE(m0 != m1);
+    EXPECT_FALSE(m0 != m0);
+    EXPECT_FALSE(m0 == m1);
+}
+
+TEST_F(Mat2Test, Constructors) {
+    mat2 m0;
+    ASSERT_EQ(m0[0].x, 1);
+    ASSERT_EQ(m0[0].y, 0);
+    ASSERT_EQ(m0[1].x, 0);
+    ASSERT_EQ(m0[1].y, 1);
+
+    mat2 m1(2);
+    mat2 m2(vec2(2));
+    mat2 m3(m2);
+
+    EXPECT_EQ(m1, m2);
+    EXPECT_EQ(m2, m3);
+    EXPECT_EQ(m3, m1);
+}
+
+TEST_F(Mat2Test, ArithmeticOps) {
+    mat2 m0;
+    mat2 m1(2);
+    mat2 m2(vec2(2));
+
+    m1 += m2;
+    EXPECT_EQ(mat2(4), m1);
+
+    m2 -= m1;
+    EXPECT_EQ(mat2(-2), m2);
+
+    m1 *= 2;
+    EXPECT_EQ(mat2(8), m1);
+
+    m1 /= 2;
+    EXPECT_EQ(mat2(4), m1);
+
+    m0 = -m0;
+    EXPECT_EQ(mat2(-1), m0);
+}
+
+TEST_F(Mat2Test, UnaryOps) {
+    const mat2 identity;
+    mat2 m0;
+
+    m0 = -m0;
+    EXPECT_EQ(mat2(vec2(-1, 0),
+                   vec2(0, -1)), m0);
+
+    m0 = -m0;
+    EXPECT_EQ(identity, m0);
+}
+
+TEST_F(Mat2Test, MiscOps) {
+    const mat2 identity;
+    mat2 m0;
+    EXPECT_EQ(2, trace(m0));
+
+    mat2 m1(vec2(1, 2), vec2(3, 4));
+    mat2 m2(vec2(1, 3), vec2(2, 4));
+    EXPECT_EQ(m1, transpose(m2));
+    EXPECT_EQ(m2, transpose(m1));
+    EXPECT_EQ(vec2(1, 4), diag(m1));
+
+    EXPECT_EQ(identity, inverse(identity));
+
+    EXPECT_EQ(m1, m1*identity);
+}
+
+//------------------------------------------------------------------------------
+// MORE MATRIX TESTS
+//------------------------------------------------------------------------------
+
+template <typename T>
+class MatTestT : public ::testing::Test {
+public:
+};
+
+typedef ::testing::Types<float,float> TestMatrixValueTypes;
+
+TYPED_TEST_CASE(MatTestT, TestMatrixValueTypes);
+
+#define TEST_MATRIX_INVERSE(MATRIX, EPSILON)                                \
+{                                                                           \
+    typedef decltype(MATRIX) MatrixType;                                    \
+    MatrixType inv1 = inverse(MATRIX);                                      \
+    MatrixType ident1 = MATRIX * inv1;                                      \
+    static const MatrixType IDENTITY;                                       \
+    for (size_t row = 0; row < MatrixType::ROW_SIZE; ++row) {               \
+        for (size_t col = 0; col < MatrixType::COL_SIZE; ++col) {           \
+            EXPECT_NEAR(ident1[row][col], IDENTITY[row][col], EPSILON);     \
+        }                                                                   \
+    }                                                                       \
+}
+
+TYPED_TEST(MatTestT, Inverse4) {
+    typedef ::android::details::TMat44<TypeParam> M44T;
+
+    M44T m1(1,  0,  0,  0,
+            0,  1,  0,  0,
+            0,  0,  1,  0,
+            0,  0,  0,  1);
+
+    M44T m2(0,  -1,  0,  0,
+            1,  0,  0,  0,
+            0,  0,  1,  0,
+            0,  0,  0,  1);
+
+    M44T m3(1,  0,  0,  0,
+            0,  2,  0,  0,
+            0,  0,  0,  1,
+            0,  0,  -1,  0);
+
+    M44T m4(
+            4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00,
+             -8.749647e-01,  1.456563e-01, -4.617587e-01, 3.044795e+00,
+             1.229049e-01,  9.892561e-01, 7.916244e-02, -6.737138e+00,
+             0.000000e+00, 0.000000e+00, 0.000000e+00, 1.000000e+00);
+
+    M44T m5(
+        4.683281e-01, 1.251189e-02, -8.834660e-01, -4.726541e+00,
+        -8.749647e-01,  1.456563e-01, -4.617587e-01, 3.044795e+00,
+        1.229049e-01,  9.892561e-01, 7.916244e-02, -6.737138e+00,
+        1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00);
+
+    TEST_MATRIX_INVERSE(m1, 0);
+    TEST_MATRIX_INVERSE(m2, 0);
+    TEST_MATRIX_INVERSE(m3, 0);
+    TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+    TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+TYPED_TEST(MatTestT, Inverse3) {
+    typedef ::android::details::TMat33<TypeParam> M33T;
+
+    M33T m1(1,  0,  0,
+            0,  1,  0,
+            0,  0,  1);
+
+    M33T m2(0,  -1,  0,
+            1,  0,  0,
+            0,  0,  1);
+
+    M33T m3(2,  0,  0,
+            0,  0,  1,
+            0,  -1,  0);
+
+    M33T m4(
+            4.683281e-01, 1.251189e-02, 0.000000e+00,
+            -8.749647e-01, 1.456563e-01, 0.000000e+00,
+            0.000000e+00, 0.000000e+00, 1.000000e+00);
+
+    M33T m5(
+            4.683281e-01, 1.251189e-02, -8.834660e-01,
+           -8.749647e-01, 1.456563e-01, -4.617587e-01,
+            1.229049e-01, 9.892561e-01, 7.916244e-02);
+
+    TEST_MATRIX_INVERSE(m1, 0);
+    TEST_MATRIX_INVERSE(m2, 0);
+    TEST_MATRIX_INVERSE(m3, 0);
+    TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+    TEST_MATRIX_INVERSE(m5, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+TYPED_TEST(MatTestT, Inverse2) {
+    typedef ::android::details::TMat22<TypeParam> M22T;
+
+    M22T m1(1,  0,
+            0,  1);
+
+    M22T m2(0,  -1,
+            1,  0);
+
+    M22T m3(
+            4.683281e-01, 1.251189e-02,
+            -8.749647e-01, 1.456563e-01);
+
+    M22T m4(
+            4.683281e-01, 1.251189e-02,
+           -8.749647e-01, 1.456563e-01);
+
+    TEST_MATRIX_INVERSE(m1, 0);
+    TEST_MATRIX_INVERSE(m2, 0);
+    TEST_MATRIX_INVERSE(m3, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+    TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits<TypeParam>::epsilon());
+}
+
+//------------------------------------------------------------------------------
+// A macro to help with vector comparisons within floating point range.
+#define EXPECT_VEC_EQ(VEC1, VEC2)                               \
+do {                                                            \
+    const decltype(VEC1) v1 = VEC1;                             \
+    const decltype(VEC2) v2 = VEC2;                             \
+    if (std::is_same<TypeParam,float>::value) {                 \
+        for (size_t i = 0; i < v1.size(); ++i) {                \
+            EXPECT_FLOAT_EQ(v1[i], v2[i]);                      \
+        }                                                       \
+    } else if (std::is_same<TypeParam,float>::value) {          \
+        for (size_t i = 0; i < v1.size(); ++i) {                \
+            EXPECT_DOUBLE_EQ(v1[i], v2[i]);                     \
+        }                                                       \
+    } else {                                                    \
+        for (size_t i = 0; i < v1.size(); ++i) {                \
+            EXPECT_EQ(v1[i], v2[i]);                            \
+        }                                                       \
+    }                                                           \
+} while(0)
+
+//------------------------------------------------------------------------------
+// A macro to help with type comparisons within floating point range.
+#define ASSERT_TYPE_EQ(T1, T2)                                  \
+do {                                                            \
+    const decltype(T1) t1 = T1;                                 \
+    const decltype(T2) t2 = T2;                                 \
+    if (std::is_same<TypeParam,float>::value) {                 \
+        ASSERT_FLOAT_EQ(t1, t2);                                \
+    } else if (std::is_same<TypeParam,float>::value) {         \
+        ASSERT_DOUBLE_EQ(t1, t2);                               \
+    } else {                                                    \
+        ASSERT_EQ(t1, t2);                                      \
+    }                                                           \
+} while(0)
+
+//------------------------------------------------------------------------------
+// Test some translation stuff.
+TYPED_TEST(MatTestT, Translation4) {
+    typedef ::android::details::TMat44<TypeParam> M44T;
+    typedef ::android::details::TVec4<TypeParam> V4T;
+
+    V4T translateBy(-7.3, 1.1, 14.4, 0.0);
+    V4T translation(translateBy[0], translateBy[1], translateBy[2], 1.0);
+    M44T translation_matrix = M44T::translate(translation);
+
+    V4T p1(9.9, 3.1, 41.1, 1.0);
+    V4T p2(-18.0, 0.0, 1.77, 1.0);
+    V4T p3(0, 0, 0, 1);
+    V4T p4(-1000, -1000, 1000, 1.0);
+
+    EXPECT_VEC_EQ(translation_matrix * p1, translateBy + p1);
+    EXPECT_VEC_EQ(translation_matrix * p2, translateBy + p2);
+    EXPECT_VEC_EQ(translation_matrix * p3, translateBy + p3);
+    EXPECT_VEC_EQ(translation_matrix * p4, translateBy + p4);
+}
+
+//------------------------------------------------------------------------------
+template <typename MATRIX>
+static void verifyOrthonormal(const MATRIX& A) {
+    typedef typename MATRIX::value_type T;
+
+    static constexpr T value_eps = T(100) * std::numeric_limits<T>::epsilon();
+
+    const MATRIX prod = A * transpose(A);
+    for (size_t i = 0; i < MATRIX::NUM_COLS; ++i) {
+        for (size_t j = 0; j < MATRIX::NUM_ROWS; ++j) {
+            if (i == j) {
+                ASSERT_NEAR(prod[i][j], T(1), value_eps);
+            } else {
+                ASSERT_NEAR(prod[i][j], T(0), value_eps);
+            }
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+// Test euler code.
+TYPED_TEST(MatTestT, EulerZYX_44) {
+    typedef ::android::details::TMat44<TypeParam> M44T;
+
+    std::default_random_engine generator(82828);
+    std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M44T m = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        verifyOrthonormal(m);
+    }
+
+    M44T m = M44T::eulerZYX(1, 2, 3);
+    verifyOrthonormal(m);
+}
+
+//------------------------------------------------------------------------------
+// Test euler code.
+TYPED_TEST(MatTestT, EulerZYX_33) {
+
+    typedef ::android::details::TMat33<TypeParam> M33T;
+
+    std::default_random_engine generator(112233);
+    std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M33T m = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        verifyOrthonormal(m);
+    }
+
+    M33T m = M33T::eulerZYX(1, 2, 3);
+    verifyOrthonormal(m);
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPostTranslation) {
+
+    typedef ::android::details::TMat44<TypeParam> M44T;
+    typedef ::android::details::TVec4<TypeParam> V4T;
+    typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+    std::default_random_engine generator(112233);
+    std::uniform_real_distribution<float> distribution(-6.0 * 2.0*M_PI, 6.0 * 2.0*M_PI);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        M44T t = M44T::translate(V4T(rand_gen(), rand_gen(), rand_gen(), 1));
+        QuatT qr = r.toQuaternion();
+        M44T tr = t * r;
+        QuatT qtr = tr.toQuaternion();
+
+        ASSERT_TYPE_EQ(qr.x, qtr.x);
+        ASSERT_TYPE_EQ(qr.y, qtr.y);
+        ASSERT_TYPE_EQ(qr.z, qtr.z);
+        ASSERT_TYPE_EQ(qr.w, qtr.w);
+    }
+
+    M44T r = M44T::eulerZYX(1, 2, 3);
+    M44T t = M44T::translate(V4T(20, -15, 2, 1));
+    QuatT qr = r.toQuaternion();
+    M44T tr = t * r;
+    QuatT qtr = tr.toQuaternion();
+
+    ASSERT_TYPE_EQ(qr.x, qtr.x);
+    ASSERT_TYPE_EQ(qr.y, qtr.y);
+    ASSERT_TYPE_EQ(qr.z, qtr.z);
+    ASSERT_TYPE_EQ(qr.w, qtr.w);
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPointTransformation33) {
+    static constexpr TypeParam value_eps =
+            TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon();
+
+    typedef ::android::details::TMat33<TypeParam> M33T;
+    typedef ::android::details::TVec3<TypeParam> V3T;
+    typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+    std::default_random_engine generator(112233);
+    std::uniform_real_distribution<float> distribution(-100.0, 100.0);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M33T r = M33T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        QuatT qr = r.toQuaternion();
+        V3T p(rand_gen(), rand_gen(), rand_gen());
+
+        V3T pr = r * p;
+        V3T pq = qr * p;
+
+        ASSERT_NEAR(pr.x, pq.x, value_eps);
+        ASSERT_NEAR(pr.y, pq.y, value_eps);
+        ASSERT_NEAR(pr.z, pq.z, value_eps);
+    }
+}
+
+//------------------------------------------------------------------------------
+// Test to quaternion with post translation.
+TYPED_TEST(MatTestT, ToQuaternionPointTransformation44) {
+    static constexpr TypeParam value_eps =
+            TypeParam(1000) * std::numeric_limits<TypeParam>::epsilon();
+
+    typedef ::android::details::TMat44<TypeParam> M44T;
+    typedef ::android::details::TVec4<TypeParam> V4T;
+    typedef ::android::details::TVec3<TypeParam> V3T;
+    typedef ::android::details::TQuaternion<TypeParam> QuatT;
+
+    std::default_random_engine generator(992626);
+    std::uniform_real_distribution<float> distribution(-100.0, 100.0);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < 100; ++i) {
+        M44T r = M44T::eulerZYX(rand_gen(), rand_gen(), rand_gen());
+        QuatT qr = r.toQuaternion();
+        V3T p(rand_gen(), rand_gen(), rand_gen());
+
+        V4T pr = r * V4T(p.x, p.y, p.z, 1);
+        pr.x /= pr.w;
+        pr.y /= pr.w;
+        pr.z /= pr.w;
+        V3T pq = qr * p;
+
+        ASSERT_NEAR(pr.x, pq.x, value_eps);
+        ASSERT_NEAR(pr.y, pq.y, value_eps);
+        ASSERT_NEAR(pr.z, pq.z, value_eps);
+    }
+}
+
+#undef TEST_MATRIX_INVERSE
+
+}; // namespace android
diff --git a/libs/math/tests/quat_test.cpp b/libs/math/tests/quat_test.cpp
new file mode 100644
index 0000000..c20771e
--- /dev/null
+++ b/libs/math/tests/quat_test.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "QuatTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <random>
+#include <functional>
+
+#include <math/quat.h>
+#include <math/mat4.h>
+#include <math/vec3.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class QuatTest : public testing::Test {
+protected:
+};
+
+TEST_F(QuatTest, Basics) {
+    quatd q;
+    double4& v(q.xyzw);
+
+    EXPECT_EQ(sizeof(quatd), sizeof(double)*4);
+    EXPECT_EQ(reinterpret_cast<void*>(&q), reinterpret_cast<void*>(&v));
+}
+
+TEST_F(QuatTest, Constructors) {
+    quatd q0;
+    EXPECT_EQ(q0.x, 0);
+    EXPECT_EQ(q0.y, 0);
+    EXPECT_EQ(q0.z, 0);
+    EXPECT_EQ(q0.w, 0);
+
+    quatd q1(1);
+    EXPECT_EQ(q1.x, 0);
+    EXPECT_EQ(q1.y, 0);
+    EXPECT_EQ(q1.z, 0);
+    EXPECT_EQ(q1.w, 1);
+
+    quatd q2(1, 2, 3, 4);
+    EXPECT_EQ(q2.x, 2);
+    EXPECT_EQ(q2.y, 3);
+    EXPECT_EQ(q2.z, 4);
+    EXPECT_EQ(q2.w, 1);
+
+    quatd q3(q2);
+    EXPECT_EQ(q3.x, 2);
+    EXPECT_EQ(q3.y, 3);
+    EXPECT_EQ(q3.z, 4);
+    EXPECT_EQ(q3.w, 1);
+
+    quatd q4(q3.xyz, 42);
+    EXPECT_EQ(q4.x, 2);
+    EXPECT_EQ(q4.y, 3);
+    EXPECT_EQ(q4.z, 4);
+    EXPECT_EQ(q4.w, 42);
+
+    quatd q5(double3(q2.xy, 42), 24);
+    EXPECT_EQ(q5.x, 2);
+    EXPECT_EQ(q5.y, 3);
+    EXPECT_EQ(q5.z, 42);
+    EXPECT_EQ(q5.w, 24);
+
+    quatd q6;
+    q6 = 12;
+    EXPECT_EQ(q6.x, 0);
+    EXPECT_EQ(q6.y, 0);
+    EXPECT_EQ(q6.z, 0);
+    EXPECT_EQ(q6.w, 12);
+
+    quatd q7 = 1 + 2_id + 3_jd + 4_kd;
+    EXPECT_EQ(q7.x, 2);
+    EXPECT_EQ(q7.y, 3);
+    EXPECT_EQ(q7.z, 4);
+    EXPECT_EQ(q7.w, 1);
+
+    quatf qf(2);
+    EXPECT_EQ(qf.x, 0);
+    EXPECT_EQ(qf.y, 0);
+    EXPECT_EQ(qf.z, 0);
+    EXPECT_EQ(qf.w, 2);
+}
+
+TEST_F(QuatTest, Access) {
+    quatd q0(1, 2, 3, 4);
+    q0.x = 10;
+    q0.y = 20;
+    q0.z = 30;
+    q0.w = 40;
+    EXPECT_EQ(q0.x, 10);
+    EXPECT_EQ(q0.y, 20);
+    EXPECT_EQ(q0.z, 30);
+    EXPECT_EQ(q0.w, 40);
+
+    q0[0] = 100;
+    q0[1] = 200;
+    q0[2] = 300;
+    q0[3] = 400;
+    EXPECT_EQ(q0.x, 100);
+    EXPECT_EQ(q0.y, 200);
+    EXPECT_EQ(q0.z, 300);
+    EXPECT_EQ(q0.w, 400);
+
+    q0.xyz = double3(1, 2, 3);
+    EXPECT_EQ(q0.x, 1);
+    EXPECT_EQ(q0.y, 2);
+    EXPECT_EQ(q0.z, 3);
+    EXPECT_EQ(q0.w, 400);
+}
+
+TEST_F(QuatTest, UnaryOps) {
+    quatd q0(1, 2, 3, 4);
+
+    q0 += 1;
+    EXPECT_EQ(q0.x, 2);
+    EXPECT_EQ(q0.y, 3);
+    EXPECT_EQ(q0.z, 4);
+    EXPECT_EQ(q0.w, 2);
+
+    q0 -= 1;
+    EXPECT_EQ(q0.x, 2);
+    EXPECT_EQ(q0.y, 3);
+    EXPECT_EQ(q0.z, 4);
+    EXPECT_EQ(q0.w, 1);
+
+    q0 *= 2;
+    EXPECT_EQ(q0.x, 4);
+    EXPECT_EQ(q0.y, 6);
+    EXPECT_EQ(q0.z, 8);
+    EXPECT_EQ(q0.w, 2);
+
+    q0 /= 2;
+    EXPECT_EQ(q0.x, 2);
+    EXPECT_EQ(q0.y, 3);
+    EXPECT_EQ(q0.z, 4);
+    EXPECT_EQ(q0.w, 1);
+
+    quatd q1(10, 20, 30, 40);
+
+    q0 += q1;
+    EXPECT_EQ(q0.x, 22);
+    EXPECT_EQ(q0.y, 33);
+    EXPECT_EQ(q0.z, 44);
+    EXPECT_EQ(q0.w, 11);
+
+    q0 -= q1;
+    EXPECT_EQ(q0.x, 2);
+    EXPECT_EQ(q0.y, 3);
+    EXPECT_EQ(q0.z, 4);
+    EXPECT_EQ(q0.w, 1);
+
+    q1 = -q1;
+    EXPECT_EQ(q1.x, -20);
+    EXPECT_EQ(q1.y, -30);
+    EXPECT_EQ(q1.z, -40);
+    EXPECT_EQ(q1.w, -10);
+
+    // TODO(mathias): multiplies
+}
+
+TEST_F(QuatTest, ComparisonOps) {
+    quatd q0(1, 2, 3, 4);
+    quatd q1(10, 20, 30, 40);
+
+    EXPECT_TRUE(q0 == q0);
+    EXPECT_TRUE(q0 != q1);
+    EXPECT_FALSE(q0 != q0);
+    EXPECT_FALSE(q0 == q1);
+}
+
+TEST_F(QuatTest, ArithmeticOps) {
+    quatd q0(1, 2, 3, 4);
+    quatd q1(10, 20, 30, 40);
+
+    quatd q2(q0 + q1);
+    EXPECT_EQ(q2.x, 22);
+    EXPECT_EQ(q2.y, 33);
+    EXPECT_EQ(q2.z, 44);
+    EXPECT_EQ(q2.w, 11);
+
+    q0 = q1 * 2;
+    EXPECT_EQ(q0.x, 40);
+    EXPECT_EQ(q0.y, 60);
+    EXPECT_EQ(q0.z, 80);
+    EXPECT_EQ(q0.w, 20);
+
+    q0 = 2 * q1;
+    EXPECT_EQ(q0.x, 40);
+    EXPECT_EQ(q0.y, 60);
+    EXPECT_EQ(q0.z, 80);
+    EXPECT_EQ(q0.w, 20);
+
+    quatf qf(2);
+    q0 = q1 * qf;
+    EXPECT_EQ(q0.x, 40);
+    EXPECT_EQ(q0.y, 60);
+    EXPECT_EQ(q0.z, 80);
+    EXPECT_EQ(q0.w, 20);
+
+    EXPECT_EQ(1_id * 1_id, quat(-1));
+    EXPECT_EQ(1_jd * 1_jd, quat(-1));
+    EXPECT_EQ(1_kd * 1_kd, quat(-1));
+    EXPECT_EQ(1_id * 1_jd * 1_kd, quat(-1));
+}
+
+TEST_F(QuatTest, ArithmeticFunc) {
+    quatd q(1, 2, 3, 4);
+    quatd qc(conj(q));
+    __attribute__((unused)) quatd qi(inverse(q));
+    quatd qn(normalize(q));
+
+    EXPECT_EQ(qc.x, -2);
+    EXPECT_EQ(qc.y, -3);
+    EXPECT_EQ(qc.z, -4);
+    EXPECT_EQ(qc.w,  1);
+
+    EXPECT_EQ(~q, qc);
+    EXPECT_EQ(length(q), length(qc));
+    EXPECT_EQ(sqrt(30), length(q));
+    EXPECT_FLOAT_EQ(1, length(qn));
+    EXPECT_FLOAT_EQ(1, dot(qn, qn));
+
+    quatd qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2);
+    EXPECT_EQ(mat4d(qr).toQuaternion(), qr);
+    EXPECT_EQ(1_id, mat4d(1_id).toQuaternion());
+    EXPECT_EQ(1_jd, mat4d(1_jd).toQuaternion());
+    EXPECT_EQ(1_kd, mat4d(1_kd).toQuaternion());
+
+
+    EXPECT_EQ(qr, log(exp(qr)));
+
+    quatd qq = qr * qr;
+    quatd q2 = pow(qr, 2);
+    EXPECT_NEAR(qq.x, q2.x, 1e-15);
+    EXPECT_NEAR(qq.y, q2.y, 1e-15);
+    EXPECT_NEAR(qq.z, q2.z, 1e-15);
+    EXPECT_NEAR(qq.w, q2.w, 1e-15);
+
+    quatd qa = quatd::fromAxisAngle(double3(0, 0, 1), 0);
+    quatd qb = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 2);
+    quatd qs = slerp(qa, qb, 0.5);
+    qr = quatd::fromAxisAngle(double3(0, 0, 1), M_PI / 4);
+    EXPECT_FLOAT_EQ(qr.x, qs.x);
+    EXPECT_FLOAT_EQ(qr.y, qs.y);
+    EXPECT_FLOAT_EQ(qr.z, qs.z);
+    EXPECT_FLOAT_EQ(qr.w, qs.w);
+
+    qs = nlerp(qa, qb, 0.5);
+    EXPECT_FLOAT_EQ(qr.x, qs.x);
+    EXPECT_FLOAT_EQ(qr.y, qs.y);
+    EXPECT_FLOAT_EQ(qr.z, qs.z);
+    EXPECT_FLOAT_EQ(qr.w, qs.w);
+}
+
+TEST_F(QuatTest, MultiplicationExhaustive) {
+    static constexpr double value_eps = double(1000) * std::numeric_limits<double>::epsilon();
+
+    std::default_random_engine generator(171717);
+    std::uniform_real_distribution<double> distribution(-10.0, 10.0);
+    auto rand_gen = std::bind(distribution, generator);
+
+    for (size_t i = 0; i < (1024 * 1024); ++i) {
+        double3 axis_a = normalize(double3(rand_gen(), rand_gen(), rand_gen()));
+        double angle_a = rand_gen();
+        quatd a = quatd::fromAxisAngle(axis_a, angle_a);
+
+        double3 axis_b = normalize(double3(rand_gen(), rand_gen(), rand_gen()));
+        double angle_b = rand_gen();
+        quatd b = quatd::fromAxisAngle(axis_b, angle_b);
+
+        quatd ab = a * b;
+        quatd ab_other(a.w * b.xyz + b.w * a.xyz + cross(a.xyz, b.xyz),
+            (a.w * b.w) - dot(a.xyz, b.xyz));
+
+        ASSERT_NEAR(ab.x, ab_other.x, value_eps);
+        ASSERT_NEAR(ab.y, ab_other.y, value_eps);
+        ASSERT_NEAR(ab.z, ab_other.z, value_eps);
+        ASSERT_NEAR(ab.w, ab_other.w, value_eps);
+    }
+}
+
+}; // namespace android
diff --git a/libs/math/tests/vec_test.cpp b/libs/math/tests/vec_test.cpp
new file mode 100644
index 0000000..79ae2e4
--- /dev/null
+++ b/libs/math/tests/vec_test.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VecTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class VecTest : public testing::Test {
+};
+
+TEST_F(VecTest, Basics) {
+    vec4 v4;
+    vec3& v3(v4.xyz);
+
+    EXPECT_EQ(sizeof(vec4), sizeof(float)*4);
+    EXPECT_EQ(sizeof(vec3), sizeof(float)*3);
+    EXPECT_EQ(sizeof(vec2), sizeof(float)*2);
+    EXPECT_EQ(reinterpret_cast<void*>(&v3), reinterpret_cast<void*>(&v4));
+}
+
+TEST_F(VecTest, Constructors) {
+    vec4 v0;
+    EXPECT_EQ(v0.x, 0);
+    EXPECT_EQ(v0.y, 0);
+    EXPECT_EQ(v0.z, 0);
+    EXPECT_EQ(v0.w, 0);
+
+    vec4 v1(1);
+    EXPECT_EQ(v1.x, 1);
+    EXPECT_EQ(v1.y, 1);
+    EXPECT_EQ(v1.z, 1);
+    EXPECT_EQ(v1.w, 1);
+
+    vec4 v2(1, 2, 3, 4);
+    EXPECT_EQ(v2.x, 1);
+    EXPECT_EQ(v2.y, 2);
+    EXPECT_EQ(v2.z, 3);
+    EXPECT_EQ(v2.w, 4);
+
+    vec4 v3(v2);
+    EXPECT_EQ(v3.x, 1);
+    EXPECT_EQ(v3.y, 2);
+    EXPECT_EQ(v3.z, 3);
+    EXPECT_EQ(v3.w, 4);
+
+    vec4 v4(v3.xyz, 42);
+    EXPECT_EQ(v4.x, 1);
+    EXPECT_EQ(v4.y, 2);
+    EXPECT_EQ(v4.z, 3);
+    EXPECT_EQ(v4.w, 42);
+
+    vec4 v5(vec3(v2.xy, 42), 24);
+    EXPECT_EQ(v5.x, 1);
+    EXPECT_EQ(v5.y, 2);
+    EXPECT_EQ(v5.z, 42);
+    EXPECT_EQ(v5.w, 24);
+
+    float4 vf(2);
+    EXPECT_EQ(vf.x, 2);
+    EXPECT_EQ(vf.y, 2);
+    EXPECT_EQ(vf.z, 2);
+    EXPECT_EQ(vf.w, 2);
+}
+
+TEST_F(VecTest, Access) {
+    vec4 v0(1, 2, 3, 4);
+
+    v0.x = 10;
+    v0.y = 20;
+    v0.z = 30;
+    v0.w = 40;
+    EXPECT_EQ(v0.x, 10);
+    EXPECT_EQ(v0.y, 20);
+    EXPECT_EQ(v0.z, 30);
+    EXPECT_EQ(v0.w, 40);
+
+    v0[0] = 100;
+    v0[1] = 200;
+    v0[2] = 300;
+    v0[3] = 400;
+    EXPECT_EQ(v0.x, 100);
+    EXPECT_EQ(v0.y, 200);
+    EXPECT_EQ(v0.z, 300);
+    EXPECT_EQ(v0.w, 400);
+
+    v0.xyz = vec3(1, 2, 3);
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 400);
+}
+
+TEST_F(VecTest, UnaryOps) {
+    vec4 v0(1, 2, 3, 4);
+
+    v0 += 1;
+    EXPECT_EQ(v0.x, 2);
+    EXPECT_EQ(v0.y, 3);
+    EXPECT_EQ(v0.z, 4);
+    EXPECT_EQ(v0.w, 5);
+
+    v0 -= 1;
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 4);
+
+    v0 *= 2;
+    EXPECT_EQ(v0.x, 2);
+    EXPECT_EQ(v0.y, 4);
+    EXPECT_EQ(v0.z, 6);
+    EXPECT_EQ(v0.w, 8);
+
+    v0 /= 2;
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 4);
+
+    vec4 v1(10, 20, 30, 40);
+
+    v0 += v1;
+    EXPECT_EQ(v0.x, 11);
+    EXPECT_EQ(v0.y, 22);
+    EXPECT_EQ(v0.z, 33);
+    EXPECT_EQ(v0.w, 44);
+
+    v0 -= v1;
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 4);
+
+    v0 *= v1;
+    EXPECT_EQ(v0.x, 10);
+    EXPECT_EQ(v0.y, 40);
+    EXPECT_EQ(v0.z, 90);
+    EXPECT_EQ(v0.w, 160);
+
+    v0 /= v1;
+    EXPECT_EQ(v0.x, 1);
+    EXPECT_EQ(v0.y, 2);
+    EXPECT_EQ(v0.z, 3);
+    EXPECT_EQ(v0.w, 4);
+
+    v1 = -v1;
+    EXPECT_EQ(v1.x, -10);
+    EXPECT_EQ(v1.y, -20);
+    EXPECT_EQ(v1.z, -30);
+    EXPECT_EQ(v1.w, -40);
+
+    float4 fv(1, 2, 3, 4);
+    v1 += fv;
+    EXPECT_EQ(v1.x,  -9);
+    EXPECT_EQ(v1.y, -18);
+    EXPECT_EQ(v1.z, -27);
+    EXPECT_EQ(v1.w, -36);
+}
+
+TEST_F(VecTest, ComparisonOps) {
+    vec4 v0(1, 2, 3, 4);
+    vec4 v1(10, 20, 30, 40);
+
+    EXPECT_TRUE(v0 == v0);
+    EXPECT_TRUE(v0 != v1);
+    EXPECT_FALSE(v0 != v0);
+    EXPECT_FALSE(v0 == v1);
+}
+
+TEST_F(VecTest, ComparisonFunctions) {
+    vec4 v0(1, 2, 3, 4);
+    vec4 v1(10, 20, 30, 40);
+
+    EXPECT_TRUE(all(equal(v0, v0)));
+    EXPECT_TRUE(all(notEqual(v0, v1)));
+    EXPECT_FALSE(any(notEqual(v0, v0)));
+    EXPECT_FALSE(any(equal(v0, v1)));
+
+    EXPECT_FALSE(all(lessThan(v0, v0)));
+    EXPECT_TRUE(all(lessThanEqual(v0, v0)));
+    EXPECT_FALSE(all(greaterThan(v0, v0)));
+    EXPECT_TRUE(all(greaterThanEqual(v0, v0)));
+    EXPECT_TRUE(all(lessThan(v0, v1)));
+    EXPECT_TRUE(all(greaterThan(v1, v0)));
+}
+
+TEST_F(VecTest, ArithmeticOps) {
+    vec4 v0(1, 2, 3, 4);
+    vec4 v1(10, 20, 30, 40);
+
+    vec4 v2(v0 + v1);
+    EXPECT_EQ(v2.x, 11);
+    EXPECT_EQ(v2.y, 22);
+    EXPECT_EQ(v2.z, 33);
+    EXPECT_EQ(v2.w, 44);
+
+    v0 = v1 * 2;
+    EXPECT_EQ(v0.x, 20);
+    EXPECT_EQ(v0.y, 40);
+    EXPECT_EQ(v0.z, 60);
+    EXPECT_EQ(v0.w, 80);
+
+    v0 = 2 * v1;
+    EXPECT_EQ(v0.x, 20);
+    EXPECT_EQ(v0.y, 40);
+    EXPECT_EQ(v0.z, 60);
+    EXPECT_EQ(v0.w, 80);
+
+    float4 vf(2);
+    v0 = v1 * vf;
+    EXPECT_EQ(v0.x, 20);
+    EXPECT_EQ(v0.y, 40);
+    EXPECT_EQ(v0.z, 60);
+    EXPECT_EQ(v0.w, 80);
+}
+
+TEST_F(VecTest, ArithmeticFunc) {
+    vec3 east(1, 0, 0);
+    vec3 north(0, 1, 0);
+    vec3 up(cross(east, north));
+    EXPECT_EQ(up, vec3(0, 0, 1));
+    EXPECT_EQ(dot(east, north), 0);
+    EXPECT_EQ(length(east), 1);
+    EXPECT_EQ(distance(east, north), sqrtf(2));
+
+    vec3 v0(1, 2, 3);
+    vec3 vn(normalize(v0));
+    EXPECT_FLOAT_EQ(1, length(vn));
+    EXPECT_FLOAT_EQ(length(v0), dot(v0, vn));
+
+    float3 vf(east);
+    EXPECT_EQ(length(vf), 1);
+
+    EXPECT_TRUE(any(vec3(0, 0, 1)));
+    EXPECT_FALSE(any(vec3(0, 0, 0)));
+
+    EXPECT_TRUE(all(vec3(1, 1, 1)));
+    EXPECT_FALSE(all(vec3(0, 0, 1)));
+
+    EXPECT_TRUE(any(bool3(false, false, true)));
+    EXPECT_FALSE(any(bool3(false)));
+
+    EXPECT_TRUE(all(bool3(true)));
+    EXPECT_FALSE(all(bool3(false, false, true)));
+
+    std::function<bool(float)> p = [](auto v) -> bool { return v > 0.0f; };
+    EXPECT_TRUE(all(map(vec3(1, 2, 3), p)));
+}
+
+}; // namespace android
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
new file mode 100644
index 0000000..3fbab17
--- /dev/null
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHardwareBuffer"
+
+#include <android/hardware_buffer.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <memory>
+
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+#include <ui/GraphicBuffer.h>
+#include <system/graphics.h>
+#include <hardware/gralloc1.h>
+
+#include <private/android/AHardwareBufferHelpers.h>
+
+
+static constexpr int kDataBufferSize = 64 * sizeof(int);  // 64 ints
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+// Public functions
+// ----------------------------------------------------------------------------
+
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) {
+    if (!outBuffer || !desc)
+        return BAD_VALUE;
+
+    int format = AHardwareBuffer_convertToPixelFormat(desc->format);
+    if (format == 0) {
+        ALOGE("Invalid pixel format %u", desc->format);
+        return BAD_VALUE;
+    }
+
+    if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
+        ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format");
+        return BAD_VALUE;
+    }
+
+    uint64_t producerUsage = 0;
+    uint64_t consumerUsage = 0;
+    AHardwareBuffer_convertToGrallocUsageBits(&producerUsage, &consumerUsage, desc->usage0,
+            desc->usage1);
+
+    sp<GraphicBuffer> gbuffer(new GraphicBuffer(
+            desc->width, desc->height, format, desc->layers, producerUsage, consumerUsage,
+            std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
+
+    status_t err = gbuffer->initCheck();
+    if (err != 0 || gbuffer->handle == 0) {
+        if (err == NO_MEMORY) {
+            GraphicBuffer::dumpAllocationsToSystemLog();
+        }
+        ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%u, h=%u, lc=%u) failed (%s), handle=%p",
+                desc->width, desc->height, desc->layers, strerror(-err), gbuffer->handle);
+        return err;
+    }
+
+    *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get());
+
+    // Ensure the buffer doesn't get destroyed when the sp<> goes away.
+    AHardwareBuffer_acquire(*outBuffer);
+    return NO_ERROR;
+}
+
+void AHardwareBuffer_acquire(AHardwareBuffer* buffer) {
+    AHardwareBuffer_to_GraphicBuffer(buffer)->incStrong((void*)AHardwareBuffer_acquire);
+}
+
+void AHardwareBuffer_release(AHardwareBuffer* buffer) {
+    AHardwareBuffer_to_GraphicBuffer(buffer)->decStrong((void*)AHardwareBuffer_release);
+}
+
+void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
+        AHardwareBuffer_Desc* outDesc) {
+    if (!buffer || !outDesc) return;
+
+    const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+    outDesc->width = gbuffer->getWidth();
+    outDesc->height = gbuffer->getHeight();
+    outDesc->layers = gbuffer->getLayerCount();
+    AHardwareBuffer_convertFromGrallocUsageBits(&outDesc->usage0, &outDesc->usage1,
+            gbuffer->getUsage(), gbuffer->getUsage());
+    outDesc->format = AHardwareBuffer_convertFromPixelFormat(
+            static_cast<uint32_t>(gbuffer->getPixelFormat()));
+}
+
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage0,
+        int32_t fence, const ARect* rect, void** outVirtualAddress) {
+    if (!buffer) return BAD_VALUE;
+
+    if (usage0 & ~(AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN |
+                   AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN)) {
+        ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+                " AHARDWAREBUFFER_USAGE0_CPU_* flags are allowed");
+        return BAD_VALUE;
+    }
+
+    uint64_t producerUsage = 0;
+    uint64_t consumerUsage = 0;
+    AHardwareBuffer_convertToGrallocUsageBits(&producerUsage, &consumerUsage, usage0, 0);
+    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    Rect bounds;
+    if (!rect) {
+        bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
+    } else {
+        bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
+    }
+    return gBuffer->lockAsync(producerUsage, consumerUsage, bounds,
+            outVirtualAddress, fence);
+}
+
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
+    if (!buffer) return BAD_VALUE;
+
+    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    return gBuffer->unlockAsync(fence);
+}
+
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer,
+        int socketFd) {
+    if (!buffer) return BAD_VALUE;
+    const GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+    size_t flattenedSize = gBuffer->getFlattenedSize();
+    size_t fdCount = gBuffer->getFdCount();
+
+    std::unique_ptr<uint8_t[]> data(new uint8_t[flattenedSize]);
+    std::unique_ptr<int[]> fds(new int[fdCount]);
+
+    // Make copies of needed items since flatten modifies them, and we don't
+    // want to send anything if there's an error during flatten.
+    size_t flattenedSizeCopy = flattenedSize;
+    size_t fdCountCopy = fdCount;
+    void* dataStart = data.get();
+    int* fdsStart = fds.get();
+    status_t err = gBuffer->flatten(dataStart, flattenedSizeCopy, fdsStart,
+            fdCountCopy);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    struct iovec iov[1];
+    iov[0].iov_base = data.get();
+    iov[0].iov_len = flattenedSize;
+
+    char buf[CMSG_SPACE(kDataBufferSize)];
+    struct msghdr msg = {
+            .msg_control = buf,
+            .msg_controllen = sizeof(buf),
+            .msg_iov = &iov[0],
+            .msg_iovlen = 1,
+    };
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount);
+    int* fdData = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    memcpy(fdData, fds.get(), sizeof(int) * fdCount);
+    msg.msg_controllen = cmsg->cmsg_len;
+
+    int result = sendmsg(socketFd, &msg, 0);
+    if (result <= 0) {
+        ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)",
+                result, strerror(errno));
+        return result;
+    }
+    return NO_ERROR;
+}
+
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd,
+        AHardwareBuffer** outBuffer) {
+    if (!outBuffer) return BAD_VALUE;
+
+    char dataBuf[CMSG_SPACE(kDataBufferSize)];
+    char fdBuf[CMSG_SPACE(kDataBufferSize)];
+    struct iovec iov[1];
+    iov[0].iov_base = dataBuf;
+    iov[0].iov_len = sizeof(dataBuf);
+
+    struct msghdr msg = {
+            .msg_control = fdBuf,
+            .msg_controllen = sizeof(fdBuf),
+            .msg_iov = &iov[0],
+            .msg_iovlen = 1,
+    };
+
+    int result = recvmsg(socketFd, &msg, 0);
+    if (result <= 0) {
+        ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)",
+                result, strerror(errno));
+        return result;
+    }
+
+    if (msg.msg_iovlen != 1) {
+        ALOGE("Error reading AHardwareBuffer from socket: bad data length");
+        return INVALID_OPERATION;
+    }
+
+    if (msg.msg_controllen % sizeof(int) != 0) {
+        ALOGE("Error reading AHardwareBuffer from socket: bad fd length");
+        return INVALID_OPERATION;
+    }
+
+    size_t dataLen = msg.msg_iov[0].iov_len;
+    const void* data = static_cast<const void*>(msg.msg_iov[0].iov_base);
+    if (!data) {
+        ALOGE("Error reading AHardwareBuffer from socket: no buffer data");
+        return INVALID_OPERATION;
+    }
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    if (!cmsg) {
+        ALOGE("Error reading AHardwareBuffer from socket: no fd header");
+        return INVALID_OPERATION;
+    }
+
+    size_t fdCount = msg.msg_controllen >> 2;
+    const int* fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+    if (!fdData) {
+        ALOGE("Error reading AHardwareBuffer from socket: no fd data");
+        return INVALID_OPERATION;
+    }
+
+    GraphicBuffer* gBuffer = new GraphicBuffer();
+    status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    *outBuffer = AHardwareBuffer_from_GraphicBuffer(gBuffer);
+    // Ensure the buffer has a positive ref-count.
+    AHardwareBuffer_acquire(*outBuffer);
+
+    return NO_ERROR;
+}
+
+const struct native_handle* AHardwareBuffer_getNativeHandle(
+        const AHardwareBuffer* buffer) {
+    if (!buffer) return nullptr;
+    const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    return gbuffer->handle;
+}
+
+
+// ----------------------------------------------------------------------------
+// Helpers implementation
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks.
+struct UsageMaskMapping {
+    uint64_t hardwareBufferMask;
+    uint64_t grallocMask;
+};
+
+static constexpr UsageMaskMapping kUsage0ProducerMapping[] = {
+    { AHARDWAREBUFFER_USAGE0_CPU_WRITE, GRALLOC1_PRODUCER_USAGE_CPU_WRITE },
+    { AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN, GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN },
+    { AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT, GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET },
+    { AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT, GRALLOC1_PRODUCER_USAGE_PROTECTED },
+    { AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA, GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA },
+};
+
+static constexpr UsageMaskMapping kUsage1ProducerMapping[] = {
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_0, GRALLOC1_PRODUCER_USAGE_PRIVATE_0 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_1, GRALLOC1_PRODUCER_USAGE_PRIVATE_1 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_2, GRALLOC1_PRODUCER_USAGE_PRIVATE_2 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_3, GRALLOC1_PRODUCER_USAGE_PRIVATE_3 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_4, GRALLOC1_PRODUCER_USAGE_PRIVATE_4 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_5, GRALLOC1_PRODUCER_USAGE_PRIVATE_5 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_6, GRALLOC1_PRODUCER_USAGE_PRIVATE_6 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_7, GRALLOC1_PRODUCER_USAGE_PRIVATE_7 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_8, GRALLOC1_PRODUCER_USAGE_PRIVATE_8 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_9, GRALLOC1_PRODUCER_USAGE_PRIVATE_9 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_10, GRALLOC1_PRODUCER_USAGE_PRIVATE_10 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_11, GRALLOC1_PRODUCER_USAGE_PRIVATE_11 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_12, GRALLOC1_PRODUCER_USAGE_PRIVATE_12 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_13, GRALLOC1_PRODUCER_USAGE_PRIVATE_13 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_14, GRALLOC1_PRODUCER_USAGE_PRIVATE_14 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_15, GRALLOC1_PRODUCER_USAGE_PRIVATE_15 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_16, GRALLOC1_PRODUCER_USAGE_PRIVATE_16 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_17, GRALLOC1_PRODUCER_USAGE_PRIVATE_17 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_18, GRALLOC1_PRODUCER_USAGE_PRIVATE_18 },
+    { AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_19, GRALLOC1_PRODUCER_USAGE_PRIVATE_19 },
+};
+
+static constexpr UsageMaskMapping kUsage0ConsumerMapping[] = {
+    { AHARDWAREBUFFER_USAGE0_CPU_READ, GRALLOC1_CONSUMER_USAGE_CPU_READ },
+    { AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN, GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN },
+    { AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE, GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE },
+    { AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER, GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER },
+    { AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE, GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER },
+};
+
+static constexpr UsageMaskMapping kUsage1ConsumerMapping[] = {
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_0, GRALLOC1_CONSUMER_USAGE_PRIVATE_0 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_1, GRALLOC1_CONSUMER_USAGE_PRIVATE_1 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_2, GRALLOC1_CONSUMER_USAGE_PRIVATE_2 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_3, GRALLOC1_CONSUMER_USAGE_PRIVATE_3 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_4, GRALLOC1_CONSUMER_USAGE_PRIVATE_4 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_5, GRALLOC1_CONSUMER_USAGE_PRIVATE_5 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_6, GRALLOC1_CONSUMER_USAGE_PRIVATE_6 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_7, GRALLOC1_CONSUMER_USAGE_PRIVATE_7 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_8, GRALLOC1_CONSUMER_USAGE_PRIVATE_8 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_9, GRALLOC1_CONSUMER_USAGE_PRIVATE_9 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_10, GRALLOC1_CONSUMER_USAGE_PRIVATE_10 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_11, GRALLOC1_CONSUMER_USAGE_PRIVATE_11 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_12, GRALLOC1_CONSUMER_USAGE_PRIVATE_12 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_13, GRALLOC1_CONSUMER_USAGE_PRIVATE_13 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_14, GRALLOC1_CONSUMER_USAGE_PRIVATE_14 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_15, GRALLOC1_CONSUMER_USAGE_PRIVATE_15 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_16, GRALLOC1_CONSUMER_USAGE_PRIVATE_16 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_17, GRALLOC1_CONSUMER_USAGE_PRIVATE_17 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_18, GRALLOC1_CONSUMER_USAGE_PRIVATE_18 },
+    { AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_19, GRALLOC1_CONSUMER_USAGE_PRIVATE_19 },
+};
+
+static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
+    return (mask & bitsToCheck) == bitsToCheck && bitsToCheck;
+}
+
+uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format) {
+    switch (format) {
+        case HAL_PIXEL_FORMAT_RGBA_8888:    return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+        case HAL_PIXEL_FORMAT_RGBX_8888:    return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
+        case HAL_PIXEL_FORMAT_RGB_565:      return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
+        case HAL_PIXEL_FORMAT_RGB_888:      return AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM;
+        case HAL_PIXEL_FORMAT_RGBA_FP16:    return AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT;
+        case HAL_PIXEL_FORMAT_RGBA_1010102: return AHARDWAREBUFFER_FORMAT_A2R10G10B10_UNORM_PACK32;
+        case HAL_PIXEL_FORMAT_BLOB:         return AHARDWAREBUFFER_FORMAT_BLOB;
+        default:ALOGE("Unknown pixel format %u", format);
+            return 0;
+    }
+}
+
+uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format) {
+    switch (format) {
+        case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:         return HAL_PIXEL_FORMAT_RGBA_8888;
+        case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:         return HAL_PIXEL_FORMAT_RGBX_8888;
+        case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:           return HAL_PIXEL_FORMAT_RGB_565;
+        case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:           return HAL_PIXEL_FORMAT_RGB_888;
+        case AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT:    return HAL_PIXEL_FORMAT_RGBA_FP16;
+        case AHARDWAREBUFFER_FORMAT_A2R10G10B10_UNORM_PACK32: return HAL_PIXEL_FORMAT_RGBA_1010102;
+        case AHARDWAREBUFFER_FORMAT_BLOB:                   return HAL_PIXEL_FORMAT_BLOB;
+        default:ALOGE("Unknown AHardwareBuffer format %u", format);
+            return 0;
+    }
+}
+
+void AHardwareBuffer_convertToGrallocUsageBits(uint64_t* outProducerUsage,
+    uint64_t* outConsumerUsage, uint64_t usage0, uint64_t usage1) {
+    *outProducerUsage = 0;
+    *outConsumerUsage = 0;
+    for (const UsageMaskMapping& mapping : kUsage0ProducerMapping) {
+        if (containsBits(usage0, mapping.hardwareBufferMask)) {
+            *outProducerUsage |= mapping.grallocMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage1ProducerMapping) {
+        if (containsBits(usage1, mapping.hardwareBufferMask)) {
+            *outProducerUsage |= mapping.grallocMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage0ConsumerMapping) {
+        if (containsBits(usage0, mapping.hardwareBufferMask)) {
+            *outConsumerUsage |= mapping.grallocMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage1ConsumerMapping) {
+        if (containsBits(usage1, mapping.hardwareBufferMask)) {
+            *outConsumerUsage |= mapping.grallocMask;
+        }
+    }
+}
+
+void AHardwareBuffer_convertFromGrallocUsageBits(uint64_t* outUsage0, uint64_t* outUsage1,
+        uint64_t producerUsage, uint64_t consumerUsage) {
+    *outUsage0 = 0;
+    *outUsage1 = 0;
+    for (const UsageMaskMapping& mapping : kUsage0ProducerMapping) {
+        if (containsBits(producerUsage, mapping.grallocMask)) {
+            *outUsage0 |= mapping.hardwareBufferMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage1ProducerMapping) {
+        if (containsBits(producerUsage, mapping.grallocMask)) {
+            *outUsage1 |= mapping.hardwareBufferMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage0ConsumerMapping) {
+        if (containsBits(consumerUsage, mapping.grallocMask)) {
+            *outUsage0 |= mapping.hardwareBufferMask;
+        }
+    }
+    for (const UsageMaskMapping& mapping : kUsage1ConsumerMapping) {
+        if (containsBits(consumerUsage, mapping.grallocMask)) {
+            *outUsage1 |= mapping.hardwareBufferMask;
+        }
+    }
+}
+
+const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) {
+    return reinterpret_cast<const GraphicBuffer*>(buffer);
+}
+
+GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) {
+    return reinterpret_cast<GraphicBuffer*>(buffer);
+}
+
+AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) {
+    return reinterpret_cast<AHardwareBuffer*>(buffer);
+}
+
+} // namespace android
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
new file mode 100644
index 0000000..34c136a
--- /dev/null
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ANativeWindow"
+
+#include <android/native_window.h>
+#include <system/window.h>
+
+void ANativeWindow_acquire(ANativeWindow* window) {
+    window->incStrong((void*)ANativeWindow_acquire);
+}
+
+void ANativeWindow_release(ANativeWindow* window) {
+    window->decStrong((void*)ANativeWindow_release);
+}
+
+static int32_t getWindowProp(ANativeWindow* window, int what) {
+    int value;
+    int res = window->query(window, what, &value);
+    return res < 0 ? res : value;
+}
+
+int32_t ANativeWindow_getWidth(ANativeWindow* window) {
+    return getWindowProp(window, NATIVE_WINDOW_WIDTH);
+}
+
+int32_t ANativeWindow_getHeight(ANativeWindow* window) {
+    return getWindowProp(window, NATIVE_WINDOW_HEIGHT);
+}
+
+int32_t ANativeWindow_getFormat(ANativeWindow* window) {
+    return getWindowProp(window, NATIVE_WINDOW_FORMAT);
+}
+
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
+        int32_t height, int32_t format) {
+    int32_t err = native_window_set_buffers_format(window, format);
+    if (!err) {
+        err = native_window_set_buffers_user_dimensions(window, width, height);
+        if (!err) {
+            int mode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
+            if (width && height) {
+                mode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+            }
+            err = native_window_set_scaling_mode(window, mode);
+        }
+    }
+    return err;
+}
+
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+        ARect* inOutDirtyBounds) {
+    return window->perform(window, NATIVE_WINDOW_LOCK, outBuffer, inOutDirtyBounds);
+}
+
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) {
+    return window->perform(window, NATIVE_WINDOW_UNLOCK_AND_POST);
+}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
new file mode 100644
index 0000000..9c5f096
--- /dev/null
+++ b/libs/nativewindow/Android.bp
@@ -0,0 +1,60 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+ndk_headers {
+    name: "libnativewindow_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/*.h"],
+    license: "NOTICE",
+}
+
+ndk_library {
+    name: "libnativewindow.ndk",
+    symbol_file: "libnativewindow.map.txt",
+
+    // Android O
+    first_version: "26",
+}
+
+cc_library {
+    name: "libnativewindow",
+    export_include_dirs: ["include"],
+
+    clang: true,
+
+    srcs: [
+        "AHardwareBuffer.cpp",
+        "ANativeWindow.cpp",
+    ],
+
+    shared_libs: [
+        "libhardware",
+        "libcutils",
+        "liblog",
+        "libutils",
+        "libui",
+    ],
+
+    static_libs: [
+        "libarect",
+    ],
+
+    // headers we include in our public headers
+    export_static_lib_headers: [
+        "libarect",
+    ],
+}
+
+subdirs = ["tests"]
diff --git a/libs/nativewindow/MODULE_LICENSE_APACHE2 b/libs/nativewindow/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/nativewindow/MODULE_LICENSE_APACHE2
diff --git a/libs/nativewindow/NOTICE b/libs/nativewindow/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/nativewindow/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
new file mode 100644
index 0000000..ef49995
--- /dev/null
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -0,0 +1,270 @@
+/*
+ * 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,
+
+    /**
+     * Corresponding formats:
+     *   Vulkan: VK_FORMAT_A2R10G10B10_UNORM_PACK32
+     *   OpenGL ES: GL_RGB10_A2
+     */
+    AHARDWAREBUFFER_FORMAT_A2R10G10B10_UNORM_PACK32 = 0x2b,
+
+    /**
+     * An opaque binary blob format that must have height 1, with width equal to
+     * the buffer size in bytes.
+     */
+    AHARDWAREBUFFER_FORMAT_BLOB                     = 0x21,
+};
+
+enum {
+    /* The buffer will sometimes be read by the CPU */
+    AHARDWAREBUFFER_USAGE0_CPU_READ               = 1ULL << 1,
+    /* The buffer will often be read by the CPU*/
+    AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN         = 1ULL << 2 |
+            AHARDWAREBUFFER_USAGE0_CPU_READ,
+    /* The buffer will sometimes be written to by the CPU */
+    AHARDWAREBUFFER_USAGE0_CPU_WRITE              = 1ULL << 5,
+    /* The buffer will often be written to by the CPU */
+    AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN        = 1ULL << 6 |
+            AHARDWAREBUFFER_USAGE0_CPU_WRITE,
+    /* The buffer will be read from by the GPU */
+    AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE      = 1ULL << 10,
+    /* The buffer will be written to by the GPU */
+    AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT       = 1ULL << 11,
+    /* The buffer will be used as a 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,
+};
+
+/* These flags are intended only for use by device-specific graphics drivers. */
+enum {
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_19 = 1ULL << 24,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_19 = 1ULL << 25,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_18 = 1ULL << 26,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_18 = 1ULL << 27,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_17 = 1ULL << 28,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_17 = 1ULL << 29,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_16 = 1ULL << 30,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_16 = 1ULL << 31,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_15 = 1ULL << 32,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_15 = 1ULL << 33,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_14 = 1ULL << 34,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_14 = 1ULL << 35,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_13 = 1ULL << 36,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_13 = 1ULL << 37,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_12 = 1ULL << 38,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_12 = 1ULL << 39,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_11 = 1ULL << 40,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_11 = 1ULL << 41,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_10 = 1ULL << 42,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_10 = 1ULL << 43,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_9 = 1ULL << 44,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_9 = 1ULL << 45,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_8 = 1ULL << 46,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_8 = 1ULL << 47,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_7 = 1ULL << 48,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_7 = 1ULL << 49,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_6 = 1ULL << 50,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_6 = 1ULL << 51,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_5 = 1ULL << 52,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_5 = 1ULL << 53,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_4 = 1ULL << 54,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_4 = 1ULL << 55,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_3 = 1ULL << 56,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_3 = 1ULL << 57,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_2 = 1ULL << 58,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_2 = 1ULL << 59,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_1 = 1ULL << 60,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_1 = 1ULL << 61,
+    AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_0 = 1ULL << 62,
+    AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_0 = 1ULL << 63,
+};
+
+typedef struct AHardwareBuffer_Desc {
+    uint32_t    width;
+    uint32_t    height;
+    uint32_t    layers;
+    uint32_t    format;     // One of AHARDWAREBUFFER_FORMAT_*
+    uint64_t    usage0;     // Combination of AHARDWAREBUFFER_USAGE0_*
+    uint64_t    usage1;     // Initialize to zero, reserved for future use
+} AHardwareBuffer_Desc;
+
+typedef struct AHardwareBuffer AHardwareBuffer;
+
+/**
+ * Allocates a buffer that backs an AHardwareBuffer using the passed
+ * AHardwareBuffer_Desc.
+ *
+ * Returns NO_ERROR on success, or an error number of the allocation fails for
+ * any reason.
+ */
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
+        AHardwareBuffer** outBuffer);
+/**
+ * Acquire a reference on the given AHardwareBuffer object.  This prevents the
+ * object from being deleted until the last reference is removed.
+ */
+void AHardwareBuffer_acquire(AHardwareBuffer* buffer);
+
+/**
+ * Remove a reference that was previously acquired with
+ * AHardwareBuffer_acquire().
+ */
+void AHardwareBuffer_release(AHardwareBuffer* buffer);
+
+/**
+ * Return a description of the AHardwareBuffer in the passed
+ * AHardwareBuffer_Desc struct.
+ */
+void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
+        AHardwareBuffer_Desc* outDesc);
+
+/*
+ * Lock the AHardwareBuffer for reading or writing, depending on the usage flags
+ * passed.  This call may block if the hardware needs to finish rendering or if
+ * CPU caches need to be synchronized, or possibly for other implementation-
+ * specific reasons.  If fence is not negative, then it specifies a fence file
+ * descriptor that will be signaled when the buffer is locked, otherwise the
+ * caller will block until the buffer is available.
+ *
+ * If rect is not NULL, the caller promises to modify only data in the area
+ * specified by rect. If rect is NULL, the caller may modify the contents of the
+ * entire buffer.
+ *
+ * The content of the buffer outside of the specified rect is NOT modified
+ * by this call.
+ *
+ * The buffer usage may only specify AHARDWAREBUFFER_USAGE0_CPU_*. If set, then
+ * outVirtualAddress is filled with the address of the buffer in virtual memory,
+ * otherwise this function will fail.
+ *
+ * THREADING CONSIDERATIONS:
+ *
+ * It is legal for several different threads to lock a buffer for read access;
+ * none of the threads are blocked.
+ *
+ * Locking a buffer simultaneously for write or read/write is undefined, but
+ * will neither terminate the process nor block the caller; AHardwareBuffer_lock
+ * may return an error or leave the buffer's content into an indeterminate
+ * state.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL or if the usage0
+ * flags are not a combination of AHARDWAREBUFFER_USAGE0_CPU_*, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage0,
+        int32_t fence, const ARect* rect, void** outVirtualAddress);
+
+/*
+ * Unlock the AHardwareBuffer; must be called after all changes to the buffer
+ * are completed by the caller. If fence is not NULL then it will be set to a
+ * file descriptor that is signaled when all pending work on the buffer is
+ * completed. The caller is responsible for closing the fence when it is no
+ * longer needed.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence);
+
+/*
+ * Send the AHardwareBuffer to an AF_UNIX socket.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer,
+        int socketFd);
+
+/*
+ * Receive the AHardwareBuffer from an AF_UNIX socket.
+ *
+ * Returns NO_ERROR on success, BAD_VALUE if the buffer is NULL, or an error
+ * number of the lock fails for any reason.
+ */
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd,
+        AHardwareBuffer** outBuffer);
+
+// ----------------------------------------------------------------------------
+// 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/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
new file mode 100644
index 0000000..6a46d7f
--- /dev/null
+++ b/libs/nativewindow/include/android/native_window.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file native_window.h
+ */
+
+#ifndef ANDROID_NATIVE_WINDOW_H
+#define ANDROID_NATIVE_WINDOW_H
+
+#include <sys/cdefs.h>
+
+#include <android/hardware_buffer.h>
+#include <android/rect.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Legacy window pixel format names, kept for backwards compatibility.
+ * New code and APIs should use AHARDWAREBUFFER_FORMAT_*.
+ */
+enum {
+    // NOTE: these values must match the values from graphics/common/x.x/types.hal
+
+    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
+    WINDOW_FORMAT_RGBA_8888          = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+    /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Unused: 8 bits. **/
+    WINDOW_FORMAT_RGBX_8888          = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
+    /** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
+    WINDOW_FORMAT_RGB_565            = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
+};
+
+struct ANativeWindow;
+/**
+ * {@link ANativeWindow} is opaque type that provides access to a native window.
+ *
+ * A pointer can be obtained using ANativeWindow_fromSurface().
+ */
+typedef struct ANativeWindow ANativeWindow;
+
+/**
+ * {@link ANativeWindow} is a struct that represents a windows buffer.
+ *
+ * A pointer can be obtained using ANativeWindow_lock().
+ */
+typedef struct ANativeWindow_Buffer {
+    // The number of pixels that are show horizontally.
+    int32_t width;
+
+    // The number of pixels that are shown vertically.
+    int32_t height;
+
+    // The number of *pixels* that a line in the buffer takes in
+    // memory.  This may be >= width.
+    int32_t stride;
+
+    // The format of the buffer.  One of WINDOW_FORMAT_*
+    int32_t format;
+
+    // The actual bits.
+    void* bits;
+
+    // Do not touch.
+    uint32_t reserved[6];
+} ANativeWindow_Buffer;
+
+/**
+ * Acquire a reference on the given ANativeWindow object.  This prevents the object
+ * from being deleted until the reference is removed.
+ */
+void ANativeWindow_acquire(ANativeWindow* window);
+
+/**
+ * Remove a reference that was previously acquired with ANativeWindow_acquire().
+ */
+void ANativeWindow_release(ANativeWindow* window);
+
+/**
+ * Return the current width in pixels of the window surface.  Returns a
+ * negative value on error.
+ */
+int32_t ANativeWindow_getWidth(ANativeWindow* window);
+
+/**
+ * Return the current height in pixels of the window surface.  Returns a
+ * negative value on error.
+ */
+int32_t ANativeWindow_getHeight(ANativeWindow* window);
+
+/**
+ * Return the current pixel format of the window surface.  Returns a
+ * negative value on error.
+ */
+int32_t ANativeWindow_getFormat(ANativeWindow* window);
+
+/**
+ * Change the format and size of the window buffers.
+ *
+ * The width and height control the number of pixels in the buffers, not the
+ * dimensions of the window on screen.  If these are different than the
+ * window's physical size, then it buffer will be scaled to match that size
+ * when compositing it to the screen.
+ *
+ * For all of these parameters, if 0 is supplied then the window's base
+ * value will come back in force.
+ *
+ * width and height must be either both zero or both non-zero.
+ *
+ */
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
+        int32_t width, int32_t height, int32_t format);
+
+/**
+ * Lock the window's next drawing surface for writing.
+ * inOutDirtyBounds is used as an in/out parameter, upon entering the
+ * function, it contains the dirty region, that is, the region the caller
+ * intends to redraw. When the function returns, inOutDirtyBounds is updated
+ * with the actual area the caller needs to redraw -- this region is often
+ * extended by ANativeWindow_lock.
+ */
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+        ARect* inOutDirtyBounds);
+
+/**
+ * Unlock the window's drawing surface after previously locking it,
+ * posting the new buffer to the display.
+ */
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_NATIVE_WINDOW_H
+
+/** @} */
diff --git a/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
new file mode 100644
index 0000000..612ee80
--- /dev/null
+++ b/libs/nativewindow/include/private/android/AHardwareBufferHelpers.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
+#define ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
+
+/*
+ * This file contains utility functions related to AHardwareBuffer, mostly to
+ * convert to/from HAL formats.
+ *
+ * These are PRIVATE methods, so this file can NEVER appear in a public NDK
+ * header. They are used by higher level libraries such as core/jni.
+ */
+
+#include <stdint.h>
+
+struct AHardwareBuffer;
+
+namespace android {
+
+uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
+
+uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
+
+void AHardwareBuffer_convertToGrallocUsageBits(uint64_t* outProducerUsage,
+    uint64_t* outConsumerUsage, uint64_t usage0, uint64_t usage1);
+
+void AHardwareBuffer_convertFromGrallocUsageBits(uint64_t* outUsage0, uint64_t* outUsage1,
+    uint64_t producerUsage, uint64_t consumerUsage);
+
+class GraphicBuffer;
+const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer);
+GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer);
+AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer);
+
+} // namespace android
+
+#endif // ANDROID_PRIVATE_NATIVE_AHARDWARE_BUFFER_HELPERS_H
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
new file mode 100644
index 0000000..dcfabac
--- /dev/null
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -0,0 +1,26 @@
+LIBNATIVEWINDOW {
+  global:
+    AHardwareBuffer_acquire;
+    AHardwareBuffer_allocate;
+    AHardwareBuffer_describe;
+    AHardwareBuffer_fromHardwareBuffer;
+    AHardwareBuffer_getNativeHandle;
+    AHardwareBuffer_lock;
+    AHardwareBuffer_recvHandleFromUnixSocket;
+    AHardwareBuffer_release;
+    AHardwareBuffer_sendHandleToUnixSocket;
+    AHardwareBuffer_toHardwareBuffer;
+    AHardwareBuffer_unlock;
+    ANativeWindow_acquire;
+    ANativeWindow_fromSurface;
+    ANativeWindow_fromSurfaceTexture;
+    ANativeWindow_getFormat;
+    ANativeWindow_getHeight;
+    ANativeWindow_getWidth;
+    ANativeWindow_lock;
+    ANativeWindow_release;
+    ANativeWindow_setBuffersGeometry;
+    ANativeWindow_unlockAndPost;
+  local:
+    *;
+};
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
new file mode 100644
index 0000000..1099043
--- /dev/null
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHardwareBuffer_test"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware_buffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <hardware/gralloc1.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected,
+        uint64_t actual, const char* type) {
+    std::ostringstream ss;
+    ss << type << " 0x" << std::hex << actual
+            << " does not match expected " << type << " 0x" << std::hex
+            << expected;
+    return ::testing::AssertionFailure() << ss.str();
+}
+
+static ::testing::AssertionResult TestUsageConversion(
+        uint64_t grallocProducerUsage, uint64_t grallocConsumerUsage,
+        uint64_t hardwareBufferUsage0, uint64_t hardwareBufferUsage1) {
+    uint64_t producerUsage = 0;
+    uint64_t consumerUsage = 0;
+    uint64_t usage0 = 0;
+    uint64_t usage1 = 0;
+
+    AHardwareBuffer_convertToGrallocUsageBits(
+            &producerUsage, &consumerUsage, hardwareBufferUsage0, hardwareBufferUsage1);
+    if (producerUsage != grallocProducerUsage)
+        return BuildHexFailureMessage(grallocProducerUsage, producerUsage,
+                "producer");
+    if (consumerUsage != grallocConsumerUsage)
+        return BuildHexFailureMessage(grallocConsumerUsage, consumerUsage,
+                "consumer");
+
+    AHardwareBuffer_convertFromGrallocUsageBits(
+            &usage0, &usage1, grallocProducerUsage, grallocConsumerUsage);
+    if (usage0 != hardwareBufferUsage0)
+        return BuildHexFailureMessage(hardwareBufferUsage0, usage0, "usage0");
+    if (usage1 != hardwareBufferUsage1)
+        return BuildHexFailureMessage(hardwareBufferUsage1, usage1, "usage1");
+
+    return testing::AssertionSuccess();
+}
+
+// This is a unit test rather than going through AHardwareBuffer because not
+// all flags may be supported by the host device.
+TEST(AHardwareBufferTest, ConvertToAndFromGrallocBits) {
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_CPU_READ,
+            AHARDWAREBUFFER_USAGE0_CPU_READ, 0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN,
+            AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_CPU_WRITE, 0,
+            AHARDWAREBUFFER_USAGE0_CPU_WRITE, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN, 0,
+            AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN, 0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE,
+            AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET,
+            0, AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT, 0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER,
+            AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PROTECTED, 0,
+            AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA,
+            0, AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA, 0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER,
+            AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE, 0));
+
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_0, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_1, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_1));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_2, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_2));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_3, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_3));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_4, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_4));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_5, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_5));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_6, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_6));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_7, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_7));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_8, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_8));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_9, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_9));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_10, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_10));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_11, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_11));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_12, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_12));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_13, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_13));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_14, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_14));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_15, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_15));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_16, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_16));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_17, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_17));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_18, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_18));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_PRIVATE_19, 0,
+            0, AHARDWAREBUFFER_USAGE1_PRODUCER_PRIVATE_19));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_0,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_0));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_1,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_1));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_2,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_2));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_3,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_3));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_4,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_4));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_5,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_5));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_6,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_6));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_7,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_7));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_8,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_8));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_9,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_9));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_10,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_10));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_11,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_11));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_12,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_12));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_13,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_13));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_14,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_14));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_15,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_15));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_16,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_16));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_17,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_17));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_18,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_18));
+    EXPECT_TRUE(TestUsageConversion(0, GRALLOC1_CONSUMER_USAGE_PRIVATE_19,
+            0, AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_19));
+
+    // Test some more complex flag combinations.
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_CPU_WRITE,
+            GRALLOC1_CONSUMER_USAGE_CPU_READ,
+            AHARDWAREBUFFER_USAGE0_CPU_READ | AHARDWAREBUFFER_USAGE0_CPU_WRITE,
+            0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN, 0,
+            AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN, 0));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET,
+            GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE |
+                    GRALLOC1_CONSUMER_USAGE_PRIVATE_17,
+            AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT |
+                    AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE,
+            AHARDWAREBUFFER_USAGE1_CONSUMER_PRIVATE_17));
+    EXPECT_TRUE(TestUsageConversion(GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA,
+            GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER,
+            AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER |
+                    AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA, 0));
+}
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
new file mode 100644
index 0000000..437ab75
--- /dev/null
+++ b/libs/nativewindow/tests/Android.bp
@@ -0,0 +1,21 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "AHardwareBufferTest",
+    shared_libs: ["libnativewindow"],
+    srcs: ["AHardwareBufferTest.cpp"],
+}
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
new file mode 100644
index 0000000..171a627
--- /dev/null
+++ b/libs/sensor/Android.bp
@@ -0,0 +1,61 @@
+// Copyright 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    name: "libsensor",
+
+    clang: true,
+    cppflags: [
+        "-Weverything",
+        "-Werror",
+
+        // The static constructors and destructors in this library have not been noted to
+        // introduce significant overheads
+        "-Wno-exit-time-destructors",
+        "-Wno-global-constructors",
+
+        // We only care about compiling as C++14
+        "-Wno-c++98-compat-pedantic",
+
+        // android/sensors.h uses nested anonymous unions and anonymous structs
+        "-Wno-nested-anon-types",
+        "-Wno-gnu-anonymous-struct",
+
+        // Don't warn about struct padding
+        "-Wno-padded",
+    ],
+
+    srcs: [
+        "BitTube.cpp",
+        "ISensorEventConnection.cpp",
+        "ISensorServer.cpp",
+        "Sensor.cpp",
+        "SensorEventQueue.cpp",
+        "SensorManager.cpp",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "libutils",
+        "liblog",
+        "libhardware",
+    ],
+
+    export_include_dirs: ["include"],
+
+    export_shared_lib_headers: ["libbinder", "libhardware"],
+}
+
+subdirs = ["tests"]
diff --git a/libs/sensor/BitTube.cpp b/libs/sensor/BitTube.cpp
new file mode 100644
index 0000000..93555c8
--- /dev/null
+++ b/libs/sensor/BitTube.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sensor/BitTube.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+// Socket buffer size.  The default is typically about 128KB, which is much larger than
+// we really need.  So we make it smaller.
+static const size_t DEFAULT_SOCKET_BUFFER_SIZE = 4 * 1024;
+
+
+BitTube::BitTube()
+    : mSendFd(-1), mReceiveFd(-1)
+{
+    init(DEFAULT_SOCKET_BUFFER_SIZE, DEFAULT_SOCKET_BUFFER_SIZE);
+}
+
+BitTube::BitTube(size_t bufsize)
+    : mSendFd(-1), mReceiveFd(-1)
+{
+    init(bufsize, bufsize);
+}
+
+BitTube::BitTube(const Parcel& data)
+    : mSendFd(-1), mReceiveFd(-1)
+{
+    mReceiveFd = dup(data.readFileDescriptor());
+    if (mReceiveFd < 0) {
+        mReceiveFd = -errno;
+        ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)",
+                strerror(-mReceiveFd));
+    }
+}
+
+BitTube::~BitTube()
+{
+    if (mSendFd >= 0)
+        close(mSendFd);
+
+    if (mReceiveFd >= 0)
+        close(mReceiveFd);
+}
+
+void BitTube::init(size_t rcvbuf, size_t sndbuf) {
+    int sockets[2];
+    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
+        size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
+        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
+        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
+        // sine we don't use the "return channel", we keep it small...
+        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
+        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
+        fcntl(sockets[0], F_SETFL, O_NONBLOCK);
+        fcntl(sockets[1], F_SETFL, O_NONBLOCK);
+        mReceiveFd = sockets[0];
+        mSendFd = sockets[1];
+    } else {
+        mReceiveFd = -errno;
+        ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd));
+    }
+}
+
+status_t BitTube::initCheck() const
+{
+    if (mReceiveFd < 0) {
+        return status_t(mReceiveFd);
+    }
+    return NO_ERROR;
+}
+
+int BitTube::getFd() const
+{
+    return mReceiveFd;
+}
+
+int BitTube::getSendFd() const
+{
+    return mSendFd;
+}
+
+ssize_t BitTube::write(void const* vaddr, size_t size)
+{
+    ssize_t err, len;
+    do {
+        len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
+        // cannot return less than size, since we're using SOCK_SEQPACKET
+        err = len < 0 ? errno : 0;
+    } while (err == EINTR);
+    return err == 0 ? len : -err;
+}
+
+ssize_t BitTube::read(void* vaddr, size_t size)
+{
+    ssize_t err, len;
+    do {
+        len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
+        err = len < 0 ? errno : 0;
+    } while (err == EINTR);
+    if (err == EAGAIN || err == EWOULDBLOCK) {
+        // EAGAIN means that we have non-blocking I/O but there was
+        // no data to be read. Nothing the client should care about.
+        return 0;
+    }
+    return err == 0 ? len : -err;
+}
+
+status_t BitTube::writeToParcel(Parcel* reply) const
+{
+    if (mReceiveFd < 0)
+        return -EINVAL;
+
+    status_t result = reply->writeDupFileDescriptor(mReceiveFd);
+    close(mReceiveFd);
+    mReceiveFd = -1;
+    return result;
+}
+
+
+ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
+        void const* events, size_t count, size_t objSize)
+{
+    const char* vaddr = reinterpret_cast<const char*>(events);
+    ssize_t size = tube->write(vaddr, count*objSize);
+
+    // should never happen because of SOCK_SEQPACKET
+    LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
+            "BitTube::sendObjects(count=%zu, size=%zu), res=%zd (partial events were sent!)",
+            count, objSize, size);
+
+    //ALOGE_IF(size<0, "error %d sending %d events", size, count);
+    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
+}
+
+ssize_t BitTube::recvObjects(const sp<BitTube>& tube,
+        void* events, size_t count, size_t objSize)
+{
+    char* vaddr = reinterpret_cast<char*>(events);
+    ssize_t size = tube->read(vaddr, count*objSize);
+
+    // should never happen because of SOCK_SEQPACKET
+    LOG_ALWAYS_FATAL_IF((size >= 0) && (size % static_cast<ssize_t>(objSize)),
+            "BitTube::recvObjects(count=%zu, size=%zu), res=%zd (partial events were received!)",
+            count, objSize, size);
+
+    //ALOGE_IF(size<0, "error %d receiving %d events", size, count);
+    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/ISensorEventConnection.cpp b/libs/sensor/ISensorEventConnection.cpp
new file mode 100644
index 0000000..8a3a623
--- /dev/null
+++ b/libs/sensor/ISensorEventConnection.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sensor/ISensorEventConnection.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <sensor/BitTube.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+    GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+    ENABLE_DISABLE,
+    SET_EVENT_RATE,
+    FLUSH_SENSOR,
+    CONFIGURE_CHANNEL
+};
+
+class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
+{
+public:
+    explicit BpSensorEventConnection(const sp<IBinder>& impl)
+        : BpInterface<ISensorEventConnection>(impl)
+    {
+    }
+
+    virtual ~BpSensorEventConnection();
+
+    virtual sp<BitTube> getSensorChannel() const
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
+        return new BitTube(reply);
+    }
+
+    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
+                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        data.writeInt32(enabled);
+        data.writeInt64(samplingPeriodNs);
+        data.writeInt64(maxBatchReportLatencyNs);
+        data.writeInt32(reservedFlags);
+        remote()->transact(ENABLE_DISABLE, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t setEventRate(int handle, nsecs_t ns)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        data.writeInt64(ns);
+        remote()->transact(SET_EVENT_RATE, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t flush() {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        remote()->transact(FLUSH_SENSOR, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+        data.writeInt32(handle);
+        data.writeInt32(rateLevel);
+        remote()->transact(CONFIGURE_CHANNEL, data, &reply);
+        return reply.readInt32();
+    }
+};
+
+// Out-of-line virtual method definition to trigger vtable emission in this
+// translation unit (see clang warning -Wweak-vtables)
+BpSensorEventConnection::~BpSensorEventConnection() {}
+
+IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection");
+
+// ----------------------------------------------------------------------------
+
+status_t BnSensorEventConnection::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case GET_SENSOR_CHANNEL: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            sp<BitTube> channel(getSensorChannel());
+            channel->writeToParcel(reply);
+            return NO_ERROR;
+        }
+        case ENABLE_DISABLE: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            int handle = data.readInt32();
+            int enabled = data.readInt32();
+            nsecs_t samplingPeriodNs = data.readInt64();
+            nsecs_t maxBatchReportLatencyNs = data.readInt64();
+            int reservedFlags = data.readInt32();
+            status_t result = enableDisable(handle, enabled, samplingPeriodNs,
+                                            maxBatchReportLatencyNs, reservedFlags);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+        case SET_EVENT_RATE: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            int handle = data.readInt32();
+            nsecs_t ns = data.readInt64();
+            status_t result = setEventRate(handle, ns);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+        case FLUSH_SENSOR: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            status_t result = flush();
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+        case CONFIGURE_CHANNEL: {
+            CHECK_INTERFACE(ISensorEventConnection, data, reply);
+            int handle = data.readInt32();
+            int rateLevel = data.readInt32();
+            status_t result = configureChannel(handle, rateLevel);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+
+    }
+    return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
new file mode 100644
index 0000000..f41f187
--- /dev/null
+++ b/libs/sensor/ISensorServer.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sensor/ISensorServer.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/native_handle.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <sensor/Sensor.h>
+#include <sensor/ISensorEventConnection.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+    GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION,
+    CREATE_SENSOR_EVENT_CONNECTION,
+    ENABLE_DATA_INJECTION,
+    GET_DYNAMIC_SENSOR_LIST,
+    CREATE_SENSOR_DIRECT_CONNECTION,
+};
+
+class BpSensorServer : public BpInterface<ISensorServer>
+{
+public:
+    explicit BpSensorServer(const sp<IBinder>& impl)
+        : BpInterface<ISensorServer>(impl)
+    {
+    }
+
+    virtual ~BpSensorServer();
+
+    virtual Vector<Sensor> getSensorList(const String16& opPackageName)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
+        remote()->transact(GET_SENSOR_LIST, data, &reply);
+        Sensor s;
+        Vector<Sensor> v;
+        uint32_t n = reply.readUint32();
+        v.setCapacity(n);
+        while (n--) {
+            reply.read(s);
+            v.add(s);
+        }
+        return v;
+    }
+
+    virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
+        remote()->transact(GET_DYNAMIC_SENSOR_LIST, data, &reply);
+        Sensor s;
+        Vector<Sensor> v;
+        uint32_t n = reply.readUint32();
+        v.setCapacity(n);
+        while (n--) {
+            reply.read(s);
+            v.add(s);
+        }
+        return v;
+    }
+
+    virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
+             int mode, const String16& opPackageName)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString8(packageName);
+        data.writeInt32(mode);
+        data.writeString16(opPackageName);
+        remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
+        return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
+    }
+
+    virtual int isDataInjectionEnabled() {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        remote()->transact(ENABLE_DATA_INJECTION, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+            uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+        data.writeString16(opPackageName);
+        data.writeUint32(size);
+        data.writeInt32(type);
+        data.writeInt32(format);
+        data.writeNativeHandle(resource);
+        remote()->transact(CREATE_SENSOR_DIRECT_CONNECTION, data, &reply);
+        return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
+    }
+};
+
+// Out-of-line virtual method definition to trigger vtable emission in this
+// translation unit (see clang warning -Wweak-vtables)
+BpSensorServer::~BpSensorServer() {}
+
+IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer");
+
+// ----------------------------------------------------------------------
+
+status_t BnSensorServer::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case GET_SENSOR_LIST: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            const String16& opPackageName = data.readString16();
+            Vector<Sensor> v(getSensorList(opPackageName));
+            size_t n = v.size();
+            reply->writeUint32(static_cast<uint32_t>(n));
+            for (size_t i = 0; i < n; i++) {
+                reply->write(v[i]);
+            }
+            return NO_ERROR;
+        }
+        case CREATE_SENSOR_EVENT_CONNECTION: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            String8 packageName = data.readString8();
+            int32_t mode = data.readInt32();
+            const String16& opPackageName = data.readString16();
+            sp<ISensorEventConnection> connection(createSensorEventConnection(packageName, mode,
+                    opPackageName));
+            reply->writeStrongBinder(IInterface::asBinder(connection));
+            return NO_ERROR;
+        }
+        case ENABLE_DATA_INJECTION: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            int32_t ret = isDataInjectionEnabled();
+            reply->writeInt32(static_cast<int32_t>(ret));
+            return NO_ERROR;
+        }
+        case GET_DYNAMIC_SENSOR_LIST: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            const String16& opPackageName = data.readString16();
+            Vector<Sensor> v(getDynamicSensorList(opPackageName));
+            size_t n = v.size();
+            reply->writeUint32(static_cast<uint32_t>(n));
+            for (size_t i = 0; i < n; i++) {
+                reply->write(v[i]);
+            }
+            return NO_ERROR;
+        }
+        case CREATE_SENSOR_DIRECT_CONNECTION: {
+            CHECK_INTERFACE(ISensorServer, data, reply);
+            const String16& opPackageName = data.readString16();
+            uint32_t size = data.readUint32();
+            int32_t type = data.readInt32();
+            int32_t format = data.readInt32();
+            native_handle_t *resource = data.readNativeHandle();
+            sp<ISensorEventConnection> ch =
+                    createSensorDirectConnection(opPackageName, size, type, format, resource);
+            native_handle_close(resource);
+            native_handle_delete(resource);
+            reply->writeStrongBinder(IInterface::asBinder(ch));
+            return NO_ERROR;
+        }
+    }
+    return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
new file mode 100644
index 0000000..c2d477e
--- /dev/null
+++ b/libs/sensor/Sensor.cpp
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sensor/Sensor.h>
+
+#include <inttypes.h>
+
+#include <binder/AppOpsManager.h>
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+Sensor::Sensor(const char * name) :
+        mName(name), mHandle(0), mType(0),
+        mMinValue(0), mMaxValue(0), mResolution(0),
+        mPower(0), mMinDelay(0), mVersion(0), mFifoReservedEventCount(0),
+        mFifoMaxEventCount(0), mRequiredAppOp(0),
+        mMaxDelay(0), mFlags(0) {
+}
+
+Sensor::Sensor(struct sensor_t const* hwSensor, int halVersion) :
+        Sensor(*hwSensor, uuid_t(), halVersion) {
+}
+
+Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion) {
+    mName = hwSensor.name;
+    mVendor = hwSensor.vendor;
+    mVersion = hwSensor.version;
+    mHandle = hwSensor.handle;
+    mType = hwSensor.type;
+    mMinValue = 0;                      // FIXME: minValue
+    mMaxValue = hwSensor.maxRange;      // FIXME: maxValue
+    mResolution = hwSensor.resolution;
+    mPower = hwSensor.power;
+    mMinDelay = hwSensor.minDelay;
+    mFlags = 0;
+    mUuid = uuid;
+
+    // Set fifo event count zero for older devices which do not support batching. Fused
+    // sensors also have their fifo counts set to zero.
+    if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
+        mFifoReservedEventCount = hwSensor.fifoReservedEventCount;
+        mFifoMaxEventCount = hwSensor.fifoMaxEventCount;
+    } else {
+        mFifoReservedEventCount = 0;
+        mFifoMaxEventCount = 0;
+    }
+
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        if (hwSensor.maxDelay > INT_MAX) {
+            // Max delay is declared as a 64 bit integer for 64 bit architectures. But it should
+            // always fit in a 32 bit integer, log error and cap it to INT_MAX.
+            ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.string(),
+                  static_cast<int64_t>(hwSensor.maxDelay));
+            mMaxDelay = INT_MAX;
+        } else {
+            mMaxDelay = static_cast<int32_t>(hwSensor.maxDelay);
+        }
+    } else {
+        // For older hals set maxDelay to 0.
+        mMaxDelay = 0;
+    }
+
+    // Ensure existing sensors have correct string type, required permissions and reporting mode.
+    // Set reportingMode for all android defined sensor types, set wake-up flag only for proximity
+    // sensor, significant motion, tilt, pick_up gesture, wake gesture and glance gesture on older
+    // HALs. Newer HALs can define both wake-up and non wake-up proximity sensors.
+    // All the OEM defined defined sensors have flags set to whatever is provided by the HAL.
+    switch (mType) {
+    case SENSOR_TYPE_ACCELEROMETER:
+        mStringType = SENSOR_STRING_TYPE_ACCELEROMETER;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_AMBIENT_TEMPERATURE:
+        mStringType = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_GAME_ROTATION_VECTOR:
+        mStringType = SENSOR_STRING_TYPE_GAME_ROTATION_VECTOR;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+        mStringType = SENSOR_STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_GRAVITY:
+        mStringType = SENSOR_STRING_TYPE_GRAVITY;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_GYROSCOPE:
+        mStringType = SENSOR_STRING_TYPE_GYROSCOPE;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
+        mStringType = SENSOR_STRING_TYPE_GYROSCOPE_UNCALIBRATED;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_HEART_RATE: {
+        mStringType = SENSOR_STRING_TYPE_HEART_RATE;
+        mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
+        AppOpsManager appOps;
+        mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        } break;
+    case SENSOR_TYPE_LIGHT:
+        mStringType = SENSOR_STRING_TYPE_LIGHT;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_LINEAR_ACCELERATION:
+        mStringType = SENSOR_STRING_TYPE_LINEAR_ACCELERATION;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_MAGNETIC_FIELD:
+        mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+        mStringType = SENSOR_STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_ORIENTATION:
+        mStringType = SENSOR_STRING_TYPE_ORIENTATION;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_PRESSURE:
+        mStringType = SENSOR_STRING_TYPE_PRESSURE;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_PROXIMITY:
+        mStringType = SENSOR_STRING_TYPE_PROXIMITY;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_RELATIVE_HUMIDITY:
+        mStringType = SENSOR_STRING_TYPE_RELATIVE_HUMIDITY;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_ROTATION_VECTOR:
+        mStringType = SENSOR_STRING_TYPE_ROTATION_VECTOR;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_SIGNIFICANT_MOTION:
+        mStringType = SENSOR_STRING_TYPE_SIGNIFICANT_MOTION;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_STEP_COUNTER:
+        mStringType = SENSOR_STRING_TYPE_STEP_COUNTER;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_STEP_DETECTOR:
+        mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+        break;
+    case SENSOR_TYPE_TEMPERATURE:
+        mStringType = SENSOR_STRING_TYPE_TEMPERATURE;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_TILT_DETECTOR:
+        mStringType = SENSOR_STRING_TYPE_TILT_DETECTOR;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_WAKE_GESTURE:
+        mStringType = SENSOR_STRING_TYPE_WAKE_GESTURE;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_GLANCE_GESTURE:
+        mStringType = SENSOR_STRING_TYPE_GLANCE_GESTURE;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_PICK_UP_GESTURE:
+        mStringType = SENSOR_STRING_TYPE_PICK_UP_GESTURE;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT:
+        mStringType = SENSOR_STRING_TYPE_LOW_LATENCY_OFFBODY_DETECT;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
+    case SENSOR_TYPE_WRIST_TILT_GESTURE:
+        mStringType = SENSOR_STRING_TYPE_WRIST_TILT_GESTURE;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_DYNAMIC_SENSOR_META:
+        mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_POSE_6DOF:
+        mStringType = SENSOR_STRING_TYPE_POSE_6DOF;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    case SENSOR_TYPE_STATIONARY_DETECT:
+        mStringType = SENSOR_STRING_TYPE_STATIONARY_DETECT;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_MOTION_DETECT:
+        mStringType = SENSOR_STRING_TYPE_MOTION_DETECT;
+        mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+        if (halVersion < SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags |= SENSOR_FLAG_WAKE_UP;
+        }
+        break;
+    case SENSOR_TYPE_HEART_BEAT:
+        mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
+        mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+        break;
+
+    // TODO:  Placeholder for LLOB sensor type
+
+
+    case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED:
+        mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
+        mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+        break;
+    default:
+        // Only pipe the stringType, requiredPermission and flags for custom sensors.
+        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
+            mStringType = hwSensor.stringType;
+        }
+        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) {
+            mRequiredPermission = hwSensor.requiredPermission;
+            if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) {
+                AppOpsManager appOps;
+                mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS));
+            }
+        }
+
+        if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+            mFlags = static_cast<uint32_t>(hwSensor.flags);
+        } else {
+            // This is an OEM defined sensor on an older HAL. Use minDelay to determine the
+            // reporting mode of the sensor.
+            if (mMinDelay > 0) {
+                mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
+            } else if (mMinDelay == 0) {
+                mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+            } else if (mMinDelay < 0) {
+                mFlags |= SENSOR_FLAG_ONE_SHOT_MODE;
+            }
+        }
+        break;
+    }
+
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        // Wake-up flag of HAL 1.3 and above is set here
+        mFlags |= (hwSensor.flags & SENSOR_FLAG_WAKE_UP);
+
+        // Log error if the reporting mode is not as expected, but respect HAL setting.
+        int actualReportingMode = (hwSensor.flags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
+        int expectedReportingMode = (mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT;
+        if (actualReportingMode != expectedReportingMode) {
+            ALOGE("Reporting Mode incorrect: sensor %s handle=%#010" PRIx32 " type=%" PRId32 " "
+                   "actual=%d expected=%d",
+                   mName.string(), mHandle, mType, actualReportingMode, expectedReportingMode);
+        }
+    }
+
+    // Feature flags
+    // Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3.
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        mFlags |= hwSensor.flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK);
+    }
+    // Set DIRECT_REPORT_MASK and DIRECT_CHANNEL_MASK flags. Compatible with HAL 1_3.
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        // only on continuous sensors direct report mode is defined
+        if ((mFlags & REPORTING_MODE_MASK) == SENSOR_FLAG_CONTINUOUS_MODE) {
+            mFlags |= hwSensor.flags
+                & (SENSOR_FLAG_MASK_DIRECT_REPORT | SENSOR_FLAG_MASK_DIRECT_CHANNEL);
+        }
+    }
+    // Set DATA_INJECTION flag here. Defined in HAL 1_4.
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) {
+        mFlags |= (hwSensor.flags & DATA_INJECTION_MASK);
+    }
+
+    if (mRequiredPermission.length() > 0) {
+        // If the sensor is protected by a permission we need to know if it is
+        // a runtime one to determine whether we can use the permission cache.
+        sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+        if (binder != 0) {
+            sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
+            mRequiredPermissionRuntime = permCtrl->isRuntimePermission(
+                    String16(mRequiredPermission));
+        }
+    }
+}
+
+Sensor::~Sensor() {
+}
+
+const String8& Sensor::getName() const {
+    return mName;
+}
+
+const String8& Sensor::getVendor() const {
+    return mVendor;
+}
+
+int32_t Sensor::getHandle() const {
+    return mHandle;
+}
+
+int32_t Sensor::getType() const {
+    return mType;
+}
+
+float Sensor::getMinValue() const {
+    return mMinValue;
+}
+
+float Sensor::getMaxValue() const {
+    return mMaxValue;
+}
+
+float Sensor::getResolution() const {
+    return mResolution;
+}
+
+float Sensor::getPowerUsage() const {
+    return mPower;
+}
+
+int32_t Sensor::getMinDelay() const {
+    return mMinDelay;
+}
+
+nsecs_t Sensor::getMinDelayNs() const {
+    return getMinDelay() * 1000;
+}
+
+int32_t Sensor::getVersion() const {
+    return mVersion;
+}
+
+uint32_t Sensor::getFifoReservedEventCount() const {
+    return mFifoReservedEventCount;
+}
+
+uint32_t Sensor::getFifoMaxEventCount() const {
+    return mFifoMaxEventCount;
+}
+
+const String8& Sensor::getStringType() const {
+    return mStringType;
+}
+
+const String8& Sensor::getRequiredPermission() const {
+    return mRequiredPermission;
+}
+
+bool Sensor::isRequiredPermissionRuntime() const {
+    return mRequiredPermissionRuntime;
+}
+
+int32_t Sensor::getRequiredAppOp() const {
+    return mRequiredAppOp;
+}
+
+int32_t Sensor::getMaxDelay() const {
+    return mMaxDelay;
+}
+
+uint32_t Sensor::getFlags() const {
+    return mFlags;
+}
+
+bool Sensor::isWakeUpSensor() const {
+    return (mFlags & SENSOR_FLAG_WAKE_UP) != 0;
+}
+
+bool Sensor::isDynamicSensor() const {
+    return (mFlags & SENSOR_FLAG_DYNAMIC_SENSOR) != 0;
+}
+
+bool Sensor::hasAdditionalInfo() const {
+    return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0;
+}
+
+int32_t Sensor::getHighestDirectReportRateLevel() const {
+    return ((mFlags & SENSOR_FLAG_MASK_DIRECT_REPORT) >> SENSOR_FLAG_SHIFT_DIRECT_REPORT);
+}
+
+bool Sensor::isDirectChannelTypeSupported(int32_t sharedMemType) const {
+    switch (sharedMemType) {
+        case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+            return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM;
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            return mFlags & SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC;
+        default:
+            return false;
+    }
+}
+
+int32_t Sensor::getReportingMode() const {
+    return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
+}
+
+const Sensor::uuid_t& Sensor::getUuid() const {
+    return mUuid;
+}
+
+void Sensor::setId(int32_t id) {
+    mUuid.i64[0] = id;
+    mUuid.i64[1] = 0;
+}
+
+int32_t Sensor::getId() const {
+    return int32_t(mUuid.i64[0]);
+}
+
+size_t Sensor::getFlattenedSize() const {
+    size_t fixedSize =
+            sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) +
+            sizeof(mMinValue) + sizeof(mMaxValue) + sizeof(mResolution) +
+            sizeof(mPower) + sizeof(mMinDelay) + sizeof(mFifoMaxEventCount) +
+            sizeof(mFifoMaxEventCount) + sizeof(mRequiredPermissionRuntime) +
+            sizeof(mRequiredAppOp) + sizeof(mMaxDelay) + sizeof(mFlags) + sizeof(mUuid);
+
+    size_t variableSize =
+            sizeof(uint32_t) + FlattenableUtils::align<4>(mName.length()) +
+            sizeof(uint32_t) + FlattenableUtils::align<4>(mVendor.length()) +
+            sizeof(uint32_t) + FlattenableUtils::align<4>(mStringType.length()) +
+            sizeof(uint32_t) + FlattenableUtils::align<4>(mRequiredPermission.length());
+
+    return fixedSize + variableSize;
+}
+
+status_t Sensor::flatten(void* buffer, size_t size) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    flattenString8(buffer, size, mName);
+    flattenString8(buffer, size, mVendor);
+    FlattenableUtils::write(buffer, size, mVersion);
+    FlattenableUtils::write(buffer, size, mHandle);
+    FlattenableUtils::write(buffer, size, mType);
+    FlattenableUtils::write(buffer, size, mMinValue);
+    FlattenableUtils::write(buffer, size, mMaxValue);
+    FlattenableUtils::write(buffer, size, mResolution);
+    FlattenableUtils::write(buffer, size, mPower);
+    FlattenableUtils::write(buffer, size, mMinDelay);
+    FlattenableUtils::write(buffer, size, mFifoReservedEventCount);
+    FlattenableUtils::write(buffer, size, mFifoMaxEventCount);
+    flattenString8(buffer, size, mStringType);
+    flattenString8(buffer, size, mRequiredPermission);
+    FlattenableUtils::write(buffer, size, mRequiredPermissionRuntime);
+    FlattenableUtils::write(buffer, size, mRequiredAppOp);
+    FlattenableUtils::write(buffer, size, mMaxDelay);
+    FlattenableUtils::write(buffer, size, mFlags);
+    if (mUuid.i64[1] != 0) {
+        // We should never hit this case with our current API, but we
+        // could via a careless API change.  If that happens,
+        // this code will keep us from leaking our UUID (while probably
+        // breaking dynamic sensors).  See b/29547335.
+        ALOGW("Sensor with UUID being flattened; sending 0.  Expect "
+              "bad dynamic sensor behavior");
+        uuid_t tmpUuid;  // default constructor makes this 0.
+        FlattenableUtils::write(buffer, size, tmpUuid);
+    } else {
+        FlattenableUtils::write(buffer, size, mUuid);
+    }
+    return NO_ERROR;
+}
+
+status_t Sensor::unflatten(void const* buffer, size_t size) {
+    if (!unflattenString8(buffer, size, mName)) {
+        return NO_MEMORY;
+    }
+    if (!unflattenString8(buffer, size, mVendor)) {
+        return NO_MEMORY;
+    }
+
+    size_t fixedSize1 =
+            sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) + sizeof(mMinValue) +
+            sizeof(mMaxValue) + sizeof(mResolution) + sizeof(mPower) + sizeof(mMinDelay) +
+            sizeof(mFifoMaxEventCount) + sizeof(mFifoMaxEventCount);
+    if (size < fixedSize1) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, mVersion);
+    FlattenableUtils::read(buffer, size, mHandle);
+    FlattenableUtils::read(buffer, size, mType);
+    FlattenableUtils::read(buffer, size, mMinValue);
+    FlattenableUtils::read(buffer, size, mMaxValue);
+    FlattenableUtils::read(buffer, size, mResolution);
+    FlattenableUtils::read(buffer, size, mPower);
+    FlattenableUtils::read(buffer, size, mMinDelay);
+    FlattenableUtils::read(buffer, size, mFifoReservedEventCount);
+    FlattenableUtils::read(buffer, size, mFifoMaxEventCount);
+
+    if (!unflattenString8(buffer, size, mStringType)) {
+        return NO_MEMORY;
+    }
+    if (!unflattenString8(buffer, size, mRequiredPermission)) {
+        return NO_MEMORY;
+    }
+
+    size_t fixedSize2 =
+            sizeof(mRequiredPermissionRuntime) + sizeof(mRequiredAppOp) + sizeof(mMaxDelay) +
+            sizeof(mFlags) + sizeof(mUuid);
+    if (size < fixedSize2) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, mRequiredPermissionRuntime);
+    FlattenableUtils::read(buffer, size, mRequiredAppOp);
+    FlattenableUtils::read(buffer, size, mMaxDelay);
+    FlattenableUtils::read(buffer, size, mFlags);
+    FlattenableUtils::read(buffer, size, mUuid);
+    return NO_ERROR;
+}
+
+void Sensor::flattenString8(void*& buffer, size_t& size,
+        const String8& string8) {
+    uint32_t len = static_cast<uint32_t>(string8.length());
+    FlattenableUtils::write(buffer, size, len);
+    memcpy(static_cast<char*>(buffer), string8.string(), len);
+    FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+}
+
+bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) {
+    uint32_t len;
+    if (size < sizeof(len)) {
+        return false;
+    }
+    FlattenableUtils::read(buffer, size, len);
+    if (size < len) {
+        return false;
+    }
+    outputString8.setTo(static_cast<char const*>(buffer), len);
+    FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
new file mode 100644
index 0000000..6f68fb5
--- /dev/null
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Sensors"
+
+#include <sensor/SensorEventQueue.h>
+
+#include <algorithm>
+#include <sys/socket.h>
+
+#include <utils/RefBase.h>
+#include <utils/Looper.h>
+
+#include <sensor/Sensor.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorEventConnection.h>
+
+#include <android/sensor.h>
+
+using std::min;
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
+    : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0),
+      mNumAcksToSend(0) {
+    mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT];
+}
+
+SensorEventQueue::~SensorEventQueue() {
+    delete [] mRecBuffer;
+}
+
+void SensorEventQueue::onFirstRef()
+{
+    mSensorChannel = mSensorEventConnection->getSensorChannel();
+}
+
+int SensorEventQueue::getFd() const
+{
+    return mSensorChannel->getFd();
+}
+
+
+ssize_t SensorEventQueue::write(const sp<BitTube>& tube,
+        ASensorEvent const* events, size_t numEvents) {
+    return BitTube::sendObjects(tube, events, numEvents);
+}
+
+ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) {
+    if (mAvailable == 0) {
+        ssize_t err = BitTube::recvObjects(mSensorChannel,
+                mRecBuffer, MAX_RECEIVE_BUFFER_EVENT_COUNT);
+        if (err < 0) {
+            return err;
+        }
+        mAvailable = static_cast<size_t>(err);
+        mConsumed = 0;
+    }
+    size_t count = min(numEvents, mAvailable);
+    memcpy(events, mRecBuffer + mConsumed, count * sizeof(ASensorEvent));
+    mAvailable -= count;
+    mConsumed += count;
+    return static_cast<ssize_t>(count);
+}
+
+sp<Looper> SensorEventQueue::getLooper() const
+{
+    Mutex::Autolock _l(mLock);
+    if (mLooper == 0) {
+        mLooper = new Looper(true);
+        mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
+    }
+    return mLooper;
+}
+
+status_t SensorEventQueue::waitForEvent() const
+{
+    const int fd = getFd();
+    sp<Looper> looper(getLooper());
+
+    int events;
+    int32_t result;
+    do {
+        result = looper->pollOnce(-1, NULL, &events, NULL);
+        if (result == ALOOPER_POLL_ERROR) {
+            ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
+            result = -EPIPE; // unknown error, so we make up one
+            break;
+        }
+        if (events & ALOOPER_EVENT_HANGUP) {
+            // the other-side has died
+            ALOGE("SensorEventQueue::waitForEvent error HANGUP");
+            result = -EPIPE; // unknown error, so we make up one
+            break;
+        }
+    } while (result != fd);
+
+    return  (result == fd) ? status_t(NO_ERROR) : result;
+}
+
+status_t SensorEventQueue::wake() const
+{
+    sp<Looper> looper(getLooper());
+    looper->wake();
+    return NO_ERROR;
+}
+
+status_t SensorEventQueue::enableSensor(Sensor const* sensor) const {
+    return enableSensor(sensor, SENSOR_DELAY_NORMAL);
+}
+
+status_t SensorEventQueue::enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const {
+    return mSensorEventConnection->enableDisable(sensor->getHandle(), true,
+                                                 us2ns(samplingPeriodUs), 0, 0);
+}
+
+status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
+    return mSensorEventConnection->enableDisable(sensor->getHandle(), false, 0, 0, 0);
+}
+
+status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs,
+                                        int64_t maxBatchReportLatencyUs, int reservedFlags) const {
+    return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs),
+                                                 us2ns(maxBatchReportLatencyUs), reservedFlags);
+}
+
+status_t SensorEventQueue::flush() const {
+    return mSensorEventConnection->flush();
+}
+
+status_t SensorEventQueue::disableSensor(int32_t handle) const {
+    return mSensorEventConnection->enableDisable(handle, false, 0, 0, false);
+}
+
+status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const {
+    return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
+}
+
+status_t SensorEventQueue::injectSensorEvent(const ASensorEvent& event) {
+    do {
+        // Blocking call.
+        ssize_t size = ::send(mSensorChannel->getFd(), &event, sizeof(event), MSG_NOSIGNAL);
+        if (size >= 0) {
+            return NO_ERROR;
+        } else if (size < 0 && errno == EAGAIN) {
+            // If send is returning a "Try again" error, sleep for 100ms and try again. In all
+            // other cases log a failure and exit.
+            usleep(100000);
+        } else {
+            ALOGE("injectSensorEvent failure %s %zd", strerror(errno), size);
+            return INVALID_OPERATION;
+        }
+    } while (true);
+}
+
+void SensorEventQueue::sendAck(const ASensorEvent* events, int count) {
+    for (int i = 0; i < count; ++i) {
+        if (events[i].flags & WAKE_UP_SENSOR_EVENT_NEEDS_ACK) {
+            ++mNumAcksToSend;
+        }
+    }
+    // Send mNumAcksToSend to acknowledge for the wake up sensor events received.
+    if (mNumAcksToSend > 0) {
+        ssize_t size = ::send(mSensorChannel->getFd(), &mNumAcksToSend, sizeof(mNumAcksToSend),
+                MSG_DONTWAIT | MSG_NOSIGNAL);
+        if (size < 0) {
+            ALOGE("sendAck failure %zd %d", size, mNumAcksToSend);
+        } else {
+            mNumAcksToSend = 0;
+        }
+    }
+    return;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
new file mode 100644
index 0000000..9309275
--- /dev/null
+++ b/libs/sensor/SensorManager.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Sensors"
+
+#include <sensor/SensorManager.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/native_handle.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+android::Mutex android::SensorManager::sLock;
+std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
+
+SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) {
+    Mutex::Autolock _l(sLock);
+    SensorManager* sensorManager;
+    std::map<String16, SensorManager*>::iterator iterator =
+        sPackageInstances.find(packageName);
+
+    if (iterator != sPackageInstances.end()) {
+        sensorManager = iterator->second;
+    } else {
+        String16 opPackageName = packageName;
+
+        // It is possible that the calling code has no access to the package name.
+        // In this case we will get the packages for the calling UID and pick the
+        // first one for attributing the app op. This will work correctly for
+        // runtime permissions as for legacy apps we will toggle the app op for
+        // all packages in the UID. The caveat is that the operation may be attributed
+        // to the wrong package and stats based on app ops may be slightly off.
+        if (opPackageName.size() <= 0) {
+            sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+            if (binder != 0) {
+                const uid_t uid = IPCThreadState::self()->getCallingUid();
+                Vector<String16> packages;
+                interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
+                if (!packages.isEmpty()) {
+                    opPackageName = packages[0];
+                } else {
+                    ALOGE("No packages for calling UID");
+                }
+            } else {
+                ALOGE("Cannot get permission service");
+            }
+        }
+
+        sensorManager = new SensorManager(opPackageName);
+
+        // If we had no package name, we looked it up from the UID and the sensor
+        // manager instance we created should also be mapped to the empty package
+        // name, to avoid looking up the packages for a UID and get the same result.
+        if (packageName.size() <= 0) {
+            sPackageInstances.insert(std::make_pair(String16(), sensorManager));
+        }
+
+        // Stash the per package sensor manager.
+        sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
+    }
+
+    return *sensorManager;
+}
+
+SensorManager::SensorManager(const String16& opPackageName)
+    : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
+    // okay we're not locked here, but it's not needed during construction
+    assertStateLocked();
+}
+
+SensorManager::~SensorManager() {
+    free(mSensorList);
+}
+
+void SensorManager::sensorManagerDied() {
+    Mutex::Autolock _l(mLock);
+    mSensorServer.clear();
+    free(mSensorList);
+    mSensorList = NULL;
+    mSensors.clear();
+}
+
+status_t SensorManager::assertStateLocked() {
+    bool initSensorManager = false;
+    if (mSensorServer == NULL) {
+        initSensorManager = true;
+    } else {
+        // Ping binder to check if sensorservice is alive.
+        status_t err = IInterface::asBinder(mSensorServer)->pingBinder();
+        if (err != NO_ERROR) {
+            initSensorManager = true;
+        }
+    }
+    if (initSensorManager) {
+        // try for 300 seconds (60*5(getService() tries for 5 seconds)) before giving up ...
+        const String16 name("sensorservice");
+        for (int i = 0; i < 60; i++) {
+            status_t err = getService(name, &mSensorServer);
+            if (err == NAME_NOT_FOUND) {
+                sleep(1);
+                continue;
+            }
+            if (err != NO_ERROR) {
+                return err;
+            }
+            break;
+        }
+
+        class DeathObserver : public IBinder::DeathRecipient {
+            SensorManager& mSensorManager;
+            virtual void binderDied(const wp<IBinder>& who) {
+                ALOGW("sensorservice died [%p]", who.unsafe_get());
+                mSensorManager.sensorManagerDied();
+            }
+        public:
+            explicit DeathObserver(SensorManager& mgr) : mSensorManager(mgr) { }
+        };
+
+        LOG_ALWAYS_FATAL_IF(mSensorServer.get() == NULL, "getService(SensorService) NULL");
+
+        mDeathObserver = new DeathObserver(*const_cast<SensorManager *>(this));
+        IInterface::asBinder(mSensorServer)->linkToDeath(mDeathObserver);
+
+        mSensors = mSensorServer->getSensorList(mOpPackageName);
+        size_t count = mSensors.size();
+        mSensorList =
+                static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
+        LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL");
+
+        for (size_t i=0 ; i<count ; i++) {
+            mSensorList[i] = mSensors.array() + i;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+ssize_t SensorManager::getSensorList(Sensor const* const** list) {
+    Mutex::Autolock _l(mLock);
+    status_t err = assertStateLocked();
+    if (err < 0) {
+        return static_cast<ssize_t>(err);
+    }
+    *list = mSensorList;
+    return static_cast<ssize_t>(mSensors.size());
+}
+
+ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) {
+    Mutex::Autolock _l(mLock);
+    status_t err = assertStateLocked();
+    if (err < 0) {
+        return static_cast<ssize_t>(err);
+    }
+
+    dynamicSensors = mSensorServer->getDynamicSensorList(mOpPackageName);
+    size_t count = dynamicSensors.size();
+
+    return static_cast<ssize_t>(count);
+}
+
+Sensor const* SensorManager::getDefaultSensor(int type)
+{
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() == NO_ERROR) {
+        bool wakeUpSensor = false;
+        // For the following sensor types, return a wake-up sensor. These types are by default
+        // defined as wake-up sensors. For the rest of the sensor types defined in sensors.h return
+        // a non_wake-up version.
+        if (type == SENSOR_TYPE_PROXIMITY || type == SENSOR_TYPE_SIGNIFICANT_MOTION ||
+            type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
+            type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
+            type == SENSOR_TYPE_WRIST_TILT_GESTURE ||
+            type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) {
+            wakeUpSensor = true;
+        }
+        // For now we just return the first sensor of that type we find.
+        // in the future it will make sense to let the SensorService make
+        // that decision.
+        for (size_t i=0 ; i<mSensors.size() ; i++) {
+            if (mSensorList[i]->getType() == type &&
+                mSensorList[i]->isWakeUpSensor() == wakeUpSensor) {
+                return mSensorList[i];
+            }
+        }
+    }
+    return NULL;
+}
+
+sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) {
+    sp<SensorEventQueue> queue;
+
+    Mutex::Autolock _l(mLock);
+    while (assertStateLocked() == NO_ERROR) {
+        sp<ISensorEventConnection> connection =
+                mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName);
+        if (connection == NULL) {
+            // SensorService just died or the app doesn't have required permissions.
+            ALOGE("createEventQueue: connection is NULL.");
+            return NULL;
+        }
+        queue = new SensorEventQueue(connection);
+        break;
+    }
+    return queue;
+}
+
+bool SensorManager::isDataInjectionEnabled() {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() == NO_ERROR) {
+        return mSensorServer->isDataInjectionEnabled();
+    }
+    return false;
+}
+
+int SensorManager::createDirectChannel(
+        size_t size, int channelType, const native_handle_t *resourceHandle) {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() != NO_ERROR) {
+        return NO_INIT;
+    }
+
+    if (channelType != SENSOR_DIRECT_MEM_TYPE_ASHMEM
+            && channelType != SENSOR_DIRECT_MEM_TYPE_GRALLOC) {
+        ALOGE("Bad channel shared memory type %d", channelType);
+        return BAD_VALUE;
+    }
+
+    sp<ISensorEventConnection> conn =
+              mSensorServer->createSensorDirectConnection(mOpPackageName,
+                  static_cast<uint32_t>(size),
+                  static_cast<int32_t>(channelType),
+                  SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle);
+    if (conn == nullptr) {
+        return NO_MEMORY;
+    }
+
+    int nativeHandle = mDirectConnectionHandle++;
+    mDirectConnection.emplace(nativeHandle, conn);
+    return nativeHandle;
+}
+
+void SensorManager::destroyDirectChannel(int channelNativeHandle) {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() == NO_ERROR) {
+        mDirectConnection.erase(channelNativeHandle);
+    }
+}
+
+int SensorManager::configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel) {
+    Mutex::Autolock _l(mLock);
+    if (assertStateLocked() != NO_ERROR) {
+        return NO_INIT;
+    }
+
+    auto i = mDirectConnection.find(channelNativeHandle);
+    if (i == mDirectConnection.end()) {
+        ALOGE("Cannot find the handle in client direct connection table");
+        return BAD_VALUE;
+    }
+
+    int ret;
+    ret = i->second->configureChannel(sensorHandle, rateLevel);
+    ALOGE_IF(ret < 0, "SensorManager::configureChannel (%d, %d) returns %d",
+            static_cast<int>(sensorHandle), static_cast<int>(rateLevel),
+            static_cast<int>(ret));
+    return ret;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/BitTube.h b/libs/sensor/include/sensor/BitTube.h
new file mode 100644
index 0000000..c1fabe8
--- /dev/null
+++ b/libs/sensor/include/sensor/BitTube.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+class Parcel;
+
+class BitTube : public RefBase
+{
+public:
+
+    // creates a BitTube with a default (4KB) send buffer
+    BitTube();
+
+    // creates a BitTube with a a specified send and receive buffer size
+    explicit BitTube(size_t bufsize);
+
+    explicit BitTube(const Parcel& data);
+    virtual ~BitTube();
+
+    // check state after construction
+    status_t initCheck() const;
+
+    // get receive file-descriptor
+    int getFd() const;
+
+    // get the send file-descriptor.
+    int getSendFd() const;
+
+    // send objects (sized blobs). All objects are guaranteed to be written or the call fails.
+    template <typename T>
+    static ssize_t sendObjects(const sp<BitTube>& tube,
+            T const* events, size_t count) {
+        return sendObjects(tube, events, count, sizeof(T));
+    }
+
+    // receive objects (sized blobs). If the receiving buffer isn't large enough,
+    // excess messages are silently discarded.
+    template <typename T>
+    static ssize_t recvObjects(const sp<BitTube>& tube,
+            T* events, size_t count) {
+        return recvObjects(tube, events, count, sizeof(T));
+    }
+
+    // parcels this BitTube
+    status_t writeToParcel(Parcel* reply) const;
+
+private:
+    void init(size_t rcvbuf, size_t sndbuf);
+
+    // send a message. The write is guaranteed to send the whole message or fail.
+    ssize_t write(void const* vaddr, size_t size);
+
+    // receive a message. the passed buffer must be at least as large as the
+    // write call used to send the message, excess data is silently discarded.
+    ssize_t read(void* vaddr, size_t size);
+
+    int mSendFd;
+    mutable int mReceiveFd;
+
+    static ssize_t sendObjects(const sp<BitTube>& tube,
+            void const* events, size_t count, size_t objSize);
+
+    static ssize_t recvObjects(const sp<BitTube>& tube,
+            void* events, size_t count, size_t objSize);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/ISensorEventConnection.h b/libs/sensor/include/sensor/ISensorEventConnection.h
new file mode 100644
index 0000000..07cc7e8
--- /dev/null
+++ b/libs/sensor/include/sensor/ISensorEventConnection.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class BitTube;
+class Parcel;
+
+class ISensorEventConnection : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(SensorEventConnection)
+
+    virtual sp<BitTube> getSensorChannel() const = 0;
+    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
+                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags) = 0;
+    virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
+    virtual status_t flush() = 0;
+    virtual int32_t configureChannel(int32_t handle, int32_t rateLevel) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorEventConnection : public BnInterface<ISensorEventConnection>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
new file mode 100644
index 0000000..f922307
--- /dev/null
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include <binder/IInterface.h>
+
+struct native_handle;
+typedef struct native_handle native_handle_t;
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorEventConnection;
+class Parcel;
+class Sensor;
+class String8;
+class String16;
+
+class ISensorServer : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(SensorServer)
+
+    virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
+    virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
+
+    virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
+             int mode, const String16& opPackageName) = 0;
+    virtual int32_t isDataInjectionEnabled() = 0;
+
+    virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
+            uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorServer : public BnInterface<ISensorServer>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h
new file mode 100644
index 0000000..043e635
--- /dev/null
+++ b/libs/sensor/include/sensor/Sensor.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Flattenable.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+// FIXME: including from android/ breaks layering, as libandroid ultimately depends on libsensors
+#include <android/sensor.h>
+
+#include <hardware/sensors.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensor { };
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class Parcel;
+
+// ----------------------------------------------------------------------------
+
+class Sensor : public ASensor, public LightFlattenable<Sensor>
+{
+public:
+    enum {
+        TYPE_ACCELEROMETER  = ASENSOR_TYPE_ACCELEROMETER,
+        TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
+        TYPE_GYROSCOPE      = ASENSOR_TYPE_GYROSCOPE,
+        TYPE_LIGHT          = ASENSOR_TYPE_LIGHT,
+        TYPE_PROXIMITY      = ASENSOR_TYPE_PROXIMITY
+    };
+
+    struct uuid_t{
+        union {
+            uint8_t b[16];
+            int64_t i64[2];
+        };
+        uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));}
+        uuid_t() : b{0} {}
+    };
+
+    Sensor(const Sensor&) = default;
+    Sensor& operator=(const Sensor&) = default;
+
+    Sensor(const char * name = "");
+    Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
+    Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0);
+    ~Sensor();
+
+    const String8& getName() const;
+    const String8& getVendor() const;
+    int32_t getHandle() const;
+    int32_t getType() const;
+    float getMinValue() const;
+    float getMaxValue() const;
+    float getResolution() const;
+    float getPowerUsage() const;
+    int32_t getMinDelay() const;
+    nsecs_t getMinDelayNs() const;
+    int32_t getVersion() const;
+    uint32_t getFifoReservedEventCount() const;
+    uint32_t getFifoMaxEventCount() const;
+    const String8& getStringType() const;
+    const String8& getRequiredPermission() const;
+    bool isRequiredPermissionRuntime() const;
+    int32_t getRequiredAppOp() const;
+    int32_t getMaxDelay() const;
+    uint32_t getFlags() const;
+    bool isWakeUpSensor() const;
+    bool isDynamicSensor() const;
+    bool hasAdditionalInfo() const;
+    int32_t getHighestDirectReportRateLevel() const;
+    bool isDirectChannelTypeSupported(int32_t sharedMemType) const;
+    int32_t getReportingMode() const;
+
+    // Note that after setId() has been called, getUuid() no longer
+    // returns the UUID.
+    // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
+    //     make sure setId() doesn't change the UuidIndex.
+    const uuid_t& getUuid() const;
+    int32_t getId() const;
+    void setId(int32_t id);
+
+    // LightFlattenable protocol
+    inline bool isFixedSize() const { return false; }
+    size_t getFlattenedSize() const;
+    status_t flatten(void* buffer, size_t size) const;
+    status_t unflatten(void const* buffer, size_t size);
+
+private:
+    String8 mName;
+    String8 mVendor;
+    int32_t mHandle;
+    int32_t mType;
+    float   mMinValue;
+    float   mMaxValue;
+    float   mResolution;
+    float   mPower;
+    int32_t mMinDelay;
+    int32_t mVersion;
+    uint32_t mFifoReservedEventCount;
+    uint32_t mFifoMaxEventCount;
+    String8 mStringType;
+    String8 mRequiredPermission;
+    bool mRequiredPermissionRuntime = false;
+    int32_t mRequiredAppOp;
+    int32_t mMaxDelay;
+    uint32_t mFlags;
+    // TODO(b/29547335): Get rid of this field and replace with an index.
+    //     The index will be into a separate global vector of UUIDs.
+    //     Also add an mId field (and change flatten/unflatten appropriately).
+    uuid_t  mUuid;
+    static void flattenString8(void*& buffer, size_t& size, const String8& string8);
+    static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
new file mode 100644
index 0000000..a03c7ee
--- /dev/null
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Mutex.h>
+
+#include <sensor/BitTube.h>
+
+// ----------------------------------------------------------------------------
+#define WAKE_UP_SENSOR_EVENT_NEEDS_ACK (1U << 31)
+struct ALooper;
+struct ASensorEvent;
+
+// Concrete types for the NDK
+struct ASensorEventQueue {
+    ALooper* looper;
+};
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorEventConnection;
+class Sensor;
+class Looper;
+
+// ----------------------------------------------------------------------------
+
+class SensorEventQueue : public ASensorEventQueue, public RefBase
+{
+public:
+
+    enum { MAX_RECEIVE_BUFFER_EVENT_COUNT = 256 };
+
+    /**
+     * Typical sensor delay (sample period) in microseconds.
+     */
+    // Fastest sampling, system will bound it to minDelay
+    static constexpr int32_t SENSOR_DELAY_FASTEST = 0;
+    // Typical sample period for game, 50Hz;
+    static constexpr int32_t SENSOR_DELAY_GAME = 20000;
+    // Typical sample period for UI, 15Hz
+    static constexpr int32_t SENSOR_DELAY_UI = 66667;
+    // Default sensor sample period
+    static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
+
+    SensorEventQueue(const sp<ISensorEventConnection>& connection);
+    virtual ~SensorEventQueue();
+    virtual void onFirstRef();
+
+    int getFd() const;
+
+    static ssize_t write(const sp<BitTube>& tube,
+            ASensorEvent const* events, size_t numEvents);
+
+    ssize_t read(ASensorEvent* events, size_t numEvents);
+
+    status_t waitForEvent() const;
+    status_t wake() const;
+
+    status_t enableSensor(Sensor const* sensor) const;
+    status_t enableSensor(Sensor const* sensor, int32_t samplingPeriodUs) const;
+    status_t disableSensor(Sensor const* sensor) const;
+    status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
+
+    // these are here only to support SensorManager.java
+    status_t enableSensor(int32_t handle, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs,
+                          int reservedFlags) const;
+    status_t disableSensor(int32_t handle) const;
+    status_t flush() const;
+    // Send an ack for every wake_up sensor event that is set to WAKE_UP_SENSOR_EVENT_NEEDS_ACK.
+    void sendAck(const ASensorEvent* events, int count);
+
+    status_t injectSensorEvent(const ASensorEvent& event);
+private:
+    sp<Looper> getLooper() const;
+    sp<ISensorEventConnection> mSensorEventConnection;
+    sp<BitTube> mSensorChannel;
+    mutable Mutex mLock;
+    mutable sp<Looper> mLooper;
+    ASensorEvent* mRecBuffer;
+    size_t mAvailable;
+    size_t mConsumed;
+    uint32_t mNumAcksToSend;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
new file mode 100644
index 0000000..a3d9741
--- /dev/null
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_SENSOR_MANAGER_H
+#define ANDROID_GUI_SENSOR_MANAGER_H
+
+#include <map>
+#include <unordered_map>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+#include <utils/String8.h>
+
+#include <sensor/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensorManager { };
+
+struct native_handle;
+typedef struct native_handle native_handle_t;
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorServer;
+class Sensor;
+class SensorEventQueue;
+// ----------------------------------------------------------------------------
+
+class SensorManager : public ASensorManager
+{
+public:
+    static SensorManager& getInstanceForPackage(const String16& packageName);
+    ~SensorManager();
+
+    ssize_t getSensorList(Sensor const* const** list);
+    ssize_t getDynamicSensorList(Vector<Sensor>& list);
+    Sensor const* getDefaultSensor(int type);
+    sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0);
+    bool isDataInjectionEnabled();
+    int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
+    void destroyDirectChannel(int channelNativeHandle);
+    int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel);
+
+private:
+    // DeathRecipient interface
+    void sensorManagerDied();
+
+    SensorManager(const String16& opPackageName);
+    status_t assertStateLocked();
+
+private:
+    static Mutex sLock;
+    static std::map<String16, SensorManager*> sPackageInstances;
+
+    Mutex mLock;
+    sp<ISensorServer> mSensorServer;
+    Sensor const** mSensorList;
+    Vector<Sensor> mSensors;
+    sp<IBinder::DeathRecipient> mDeathObserver;
+    const String16 mOpPackageName;
+    std::unordered_map<int, sp<ISensorEventConnection>> mDirectConnection;
+    int32_t mDirectConnectionHandle;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SENSOR_MANAGER_H
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
new file mode 100644
index 0000000..9d530fc
--- /dev/null
+++ b/libs/sensor/tests/Android.bp
@@ -0,0 +1,29 @@
+// Copyright 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "libsensor_test",
+
+    clang: true,
+
+    srcs: [
+        "Sensor_test.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libsensor",
+        "libutils",
+    ],
+}
diff --git a/libs/sensor/tests/Sensor_test.cpp b/libs/sensor/tests/Sensor_test.cpp
new file mode 100644
index 0000000..ede20c9
--- /dev/null
+++ b/libs/sensor/tests/Sensor_test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Sensor_test"
+
+#include <sensor/Sensor.h>
+#include <hardware/sensors.h>
+#include <utils/Errors.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+// Returns true if the two sensors have the same attributes. Does not compare
+// UUID since that should not be transmitted via flatten/unflatten.
+static bool sensorsMatch(const Sensor& a, const Sensor& b) {
+    return a.getName() == b.getName () &&
+        a.getVendor() == b.getVendor () &&
+        a.getHandle() == b.getHandle () &&
+        a.getType() == b.getType () &&
+        a.getMinValue() == b.getMinValue () &&
+        a.getMaxValue() == b.getMaxValue () &&
+        a.getResolution() == b.getResolution () &&
+        a.getPowerUsage() == b.getPowerUsage () &&
+        a.getMinDelay() == b.getMinDelay () &&
+        a.getMinDelayNs() == b.getMinDelayNs () &&
+        a.getVersion() == b.getVersion () &&
+        a.getFifoReservedEventCount() == b.getFifoReservedEventCount () &&
+        a.getFifoMaxEventCount() == b.getFifoMaxEventCount () &&
+        a.getStringType() == b.getStringType () &&
+        a.getRequiredPermission() == b.getRequiredPermission () &&
+        a.isRequiredPermissionRuntime() == b.isRequiredPermissionRuntime () &&
+        a.getRequiredAppOp() == b.getRequiredAppOp () &&
+        a.getMaxDelay() == b.getMaxDelay () &&
+        a.getFlags() == b.getFlags () &&
+        a.isWakeUpSensor() == b.isWakeUpSensor () &&
+        a.isDynamicSensor() == b.isDynamicSensor () &&
+        a.hasAdditionalInfo() == b.hasAdditionalInfo () &&
+        a.getReportingMode() == b.getReportingMode();
+}
+
+// Creates and returns a sensor_t struct with some default values filled in.
+static sensor_t getTestSensorT() {
+    sensor_t hwSensor = {};
+    hwSensor.name = "Test Sensor";
+    hwSensor.vendor = "Test Vendor";
+    hwSensor.version = 1;
+    hwSensor.handle = 2;
+    hwSensor.type = SENSOR_TYPE_ACCELEROMETER;
+    hwSensor.maxRange = 10.f;
+    hwSensor.resolution = 1.f;
+    hwSensor.power = 5.f;
+    hwSensor.minDelay = 1000;
+    hwSensor.fifoReservedEventCount = 50;
+    hwSensor.fifoMaxEventCount = 100;
+    hwSensor.stringType = SENSOR_STRING_TYPE_ACCELEROMETER;
+    hwSensor.requiredPermission = "";
+    hwSensor.maxDelay = 5000;
+    hwSensor.flags = SENSOR_FLAG_CONTINUOUS_MODE;
+    return hwSensor;
+}
+
+TEST(SensorTest, FlattenAndUnflatten) {
+    sensor_t hwSensor = getTestSensorT();
+    Sensor sensor1(&hwSensor, SENSORS_DEVICE_API_VERSION_1_4);
+    Sensor sensor2;
+
+    std::vector<uint8_t> buffer(sensor1.getFlattenedSize());
+    ASSERT_EQ(OK, sensor1.flatten(buffer.data(), buffer.size()));
+    ASSERT_EQ(OK, sensor2.unflatten(buffer.data(), buffer.size()));
+
+    EXPECT_TRUE(sensorsMatch(sensor1, sensor2));
+}
+
+} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d5ff753..d1bfa18 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -28,6 +28,9 @@
         // We only care about compiling as C++14
         "-Wno-c++98-compat-pedantic",
 
+        // We are aware of the risks inherent in comparing floats for equality
+        "-Wno-float-equal",
+
         // We use four-character constants for the GraphicBuffer header, and don't care
         // that they're non-portable as long as they're consistent within one execution
         "-Wno-four-char-constants",
@@ -41,13 +44,18 @@
     },
 
     srcs: [
+        "ColorSpace.cpp",
         "Fence.cpp",
+        "FenceTime.cpp",
         "FrameStats.cpp",
         "Gralloc1.cpp",
         "Gralloc1On0Adapter.cpp",
+        "GrallocAllocator.cpp",
+        "GrallocMapper.cpp",
         "GraphicBuffer.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicBufferMapper.cpp",
+        "GraphicsEnv.cpp",
         "HdrCapabilities.cpp",
         "PixelFormat.cpp",
         "Rect.cpp",
@@ -56,13 +64,27 @@
     ],
 
     shared_libs: [
-        "libbinder",
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.mapper@2.0",
+        "libnativeloader",
         "libcutils",
         "libhardware",
+        "libhidlbase",
+        "libhidltransport",
         "libsync",
         "libutils",
         "liblog",
     ],
+
+    static_libs: [
+        "libarect",
+        "libmath",
+    ],
+
+    export_static_lib_headers: [
+        "libarect",
+        "libmath",
+    ],
 }
 
 subdirs = ["tests"]
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
new file mode 100644
index 0000000..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..1414766
--- /dev/null
+++ b/libs/ui/FenceTime.cpp
@@ -0,0 +1,367 @@
+/*
+* Copyright 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <ui/FenceTime.h>
+
+#define LOG_TAG "FenceTime"
+
+#include <cutils/compiler.h>  // For CC_[UN]LIKELY
+#include <utils/Log.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <memory>
+
+namespace android {
+
+// ============================================================================
+// FenceTime
+// ============================================================================
+
+const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE);
+
+void* FenceTime::operator new(size_t byteCount) noexcept {
+    void *p = nullptr;
+    if (posix_memalign(&p, alignof(FenceTime), byteCount)) {
+        return nullptr;
+    }
+    return p;
+}
+
+void FenceTime::operator delete(void *p) {
+    free(p);
+}
+
+FenceTime::FenceTime(const sp<Fence>& fence)
+  : mState(((fence.get() != nullptr) && fence->isValid()) ?
+            State::VALID : State::INVALID),
+    mFence(fence),
+    mSignalTime(mState == State::INVALID ?
+            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(sp<Fence>&& fence)
+  : mState(((fence.get() != nullptr) && fence->isValid()) ?
+            State::VALID : State::INVALID),
+    mFence(std::move(fence)),
+    mSignalTime(mState == State::INVALID ?
+            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+FenceTime::FenceTime(nsecs_t signalTime)
+  : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID),
+    mFence(nullptr),
+    mSignalTime(signalTime) {
+    if (CC_UNLIKELY(mSignalTime == Fence::SIGNAL_TIME_PENDING)) {
+        ALOGE("Pending signal time not allowed after signal.");
+        mSignalTime = Fence::SIGNAL_TIME_INVALID;
+    }
+}
+
+void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
+    if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
+        // Applying Snapshot::State::FENCE, could change the valid state of the
+        // FenceTime, which is not allowed. Callers should create a new
+        // FenceTime from the snapshot instead.
+        ALOGE("applyTrustedSnapshot: Unexpected fence.");
+        return;
+    }
+
+    if (src.state == Snapshot::State::EMPTY) {
+        return;
+    }
+
+    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        // We should always get the same signalTime here that we did in
+        // getSignalTime(). This check races with getSignalTime(), but it is
+        // only a sanity check so that's okay.
+        if (CC_UNLIKELY(signalTime != src.signalTime)) {
+            ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. "
+                    "(%" PRId64 " (old) != %" PRId64 " (new))",
+                    signalTime, src.signalTime);
+        }
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mFence.clear();
+    mSignalTime.store(src.signalTime, std::memory_order_relaxed);
+}
+
+bool FenceTime::isValid() const {
+    // We store the valid state in the constructors and return it here.
+    // This lets release code remember the valid state even after the
+    // underlying fence is destroyed.
+    return mState != State::INVALID;
+}
+
+nsecs_t FenceTime::getSignalTime() {
+    // See if we already have a cached value we can return.
+    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        return signalTime;
+    }
+
+    // Hold a reference to the fence on the stack in case the class'
+    // reference is removed by another thread. This prevents the
+    // fence from being destroyed until the end of this method, where
+    // we conveniently do not have the lock held.
+    sp<Fence> fence;
+    {
+        // With the lock acquired this time, see if we have the cached
+        // value or if we need to poll the fence.
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (!mFence.get()) {
+            // Another thread set the signal time just before we added the
+            // reference to mFence.
+            return mSignalTime.load(std::memory_order_relaxed);
+        }
+        fence = mFence;
+    }
+
+    // Make the system call without the lock held.
+    signalTime = fence->getSignalTime();
+
+    // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests
+    // use invalid underlying Fences without real file descriptors.
+    if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) {
+        if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+            signalTime = Fence::SIGNAL_TIME_PENDING;
+        }
+    }
+
+    // Make the signal time visible to everyone if it is no longer pending
+    // and remove the class' reference to the fence.
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mFence.clear();
+        mSignalTime.store(signalTime, std::memory_order_relaxed);
+    }
+
+    return signalTime;
+}
+
+nsecs_t FenceTime::getCachedSignalTime() const {
+    // memory_order_acquire since we don't have a lock fallback path
+    // that will do an acquire.
+    return mSignalTime.load(std::memory_order_acquire);
+}
+
+FenceTime::Snapshot FenceTime::getSnapshot() const {
+    // Quick check without the lock.
+    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        return Snapshot(signalTime);
+    }
+
+    // Do the full check with the lock.
+    std::lock_guard<std::mutex> lock(mMutex);
+    signalTime = mSignalTime.load(std::memory_order_relaxed);
+    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+        return Snapshot(signalTime);
+    }
+    return Snapshot(mFence);
+}
+
+// For tests only. If forceValidForTest is true, then getSignalTime will
+// never return SIGNAL_TIME_INVALID and isValid will always return true.
+FenceTime::FenceTime(const sp<Fence>& fence, bool forceValidForTest)
+  : mState(forceValidForTest ?
+            State::FORCED_VALID_FOR_TEST : State::INVALID),
+    mFence(fence),
+    mSignalTime(mState == State::INVALID ?
+            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
+}
+
+void FenceTime::signalForTest(nsecs_t signalTime) {
+    // To be realistic, this should really set a hidden value that
+    // gets picked up in the next call to getSignalTime, but this should
+    // be good enough.
+    std::lock_guard<std::mutex> lock(mMutex);
+    mFence.clear();
+    mSignalTime.store(signalTime, std::memory_order_relaxed);
+}
+
+// ============================================================================
+// FenceTime::Snapshot
+// ============================================================================
+FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence)
+    : state(State::FENCE), fence(srcFence) {
+}
+
+FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime)
+    : state(State::SIGNAL_TIME), signalTime(srcSignalTime) {
+}
+
+size_t FenceTime::Snapshot::getFlattenedSize() const {
+    constexpr size_t min = sizeof(state);
+    switch (state) {
+        case State::EMPTY:
+            return min;
+        case State::FENCE:
+            return min + fence->getFlattenedSize();
+        case State::SIGNAL_TIME:
+            return min + sizeof(signalTime);
+    }
+    return 0;
+}
+
+size_t FenceTime::Snapshot::getFdCount() const {
+    return state == State::FENCE ? fence->getFdCount() : 0u;
+}
+
+status_t FenceTime::Snapshot::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const {
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, state);
+    switch (state) {
+        case State::EMPTY:
+            return NO_ERROR;
+        case State::FENCE:
+            return fence->flatten(buffer, size, fds, count);
+        case State::SIGNAL_TIME:
+            FlattenableUtils::write(buffer, size, signalTime);
+            return NO_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+status_t FenceTime::Snapshot::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+    if (size < sizeof(state)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, state);
+    switch (state) {
+        case State::EMPTY:
+            return NO_ERROR;
+        case State::FENCE:
+            fence = new Fence;
+            return fence->unflatten(buffer, size, fds, count);
+        case State::SIGNAL_TIME:
+            if (size < sizeof(signalTime)) {
+                return NO_MEMORY;
+            }
+            FlattenableUtils::read(buffer, size, signalTime);
+            return NO_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+// ============================================================================
+// FenceTimeline
+// ============================================================================
+void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    while (mQueue.size() >= MAX_ENTRIES) {
+        // This is a sanity check to make sure the queue doesn't grow unbounded.
+        // MAX_ENTRIES should be big enough not to trigger this path.
+        // In case this path is taken though, users of FenceTime must make sure
+        // not to rely solely on FenceTimeline to get the final timestamp and
+        // should eventually call Fence::getSignalTime on their own.
+        std::shared_ptr<FenceTime> front = mQueue.front().lock();
+        if (front) {
+            // Make a last ditch effort to get the signalTime here since
+            // we are removing it from the timeline.
+            front->getSignalTime();
+        }
+        mQueue.pop();
+    }
+    mQueue.push(fence);
+}
+
+void FenceTimeline::updateSignalTimes() {
+    while (!mQueue.empty()) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        std::shared_ptr<FenceTime> fence = mQueue.front().lock();
+        if (!fence) {
+            // The shared_ptr no longer exists and no one cares about the
+            // timestamp anymore.
+            mQueue.pop();
+            continue;
+        } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+            // The fence has signaled and we've removed the sp<Fence> ref.
+            mQueue.pop();
+            continue;
+        } else {
+            // The fence didn't signal yet. Break since the later ones
+            // shouldn't have signaled either.
+            break;
+        }
+    }
+}
+
+// ============================================================================
+// FenceToFenceTimeMap
+// ============================================================================
+std::shared_ptr<FenceTime> FenceToFenceTimeMap::createFenceTimeForTest(
+        const sp<Fence>& fence) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    // Always garbage collecting isn't efficient, but this is only for testing.
+    garbageCollectLocked();
+    std::shared_ptr<FenceTime> fenceTime(new FenceTime(fence, true));
+    mMap[fence.get()].push_back(fenceTime);
+    return fenceTime;
+}
+
+void FenceToFenceTimeMap::signalAllForTest(
+        const sp<Fence>& fence, nsecs_t signalTime) {
+    bool signaled = false;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    auto it = mMap.find(fence.get());
+    if (it != mMap.end()) {
+        for (auto& weakFenceTime : it->second) {
+            std::shared_ptr<FenceTime> fenceTime = weakFenceTime.lock();
+            if (!fenceTime) {
+                continue;
+            }
+            ALOGE_IF(!fenceTime->isValid(),
+                    "signalAllForTest: Signaling invalid fence.");
+            fenceTime->signalForTest(signalTime);
+            signaled = true;
+        }
+    }
+
+    ALOGE_IF(!signaled, "signalAllForTest: Nothing to signal.");
+}
+
+void FenceToFenceTimeMap::garbageCollectLocked() {
+    for (auto& it : mMap) {
+        // Erase all expired weak pointers from the vector.
+        auto& vect = it.second;
+        vect.erase(
+                std::remove_if(vect.begin(), vect.end(),
+                        [](const std::weak_ptr<FenceTime>& ft) {
+                            return ft.expired();
+                        }),
+                vect.end());
+
+        // Also erase the map entry if the vector is now empty.
+        if (vect.empty()) {
+            mMap.erase(it.first);
+        }
+    }
+}
+
+} // namespace android
diff --git a/libs/ui/Gralloc1.cpp b/libs/ui/Gralloc1.cpp
index 4c73ce4..64a8b40 100644
--- a/libs/ui/Gralloc1.cpp
+++ b/libs/ui/Gralloc1.cpp
@@ -17,6 +17,8 @@
 //#define LOG_NDEBUG 0
 
 #include <ui/Gralloc1.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Gralloc1On0Adapter.h>
 
 #include <vector>
 
@@ -77,6 +79,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,
@@ -129,12 +142,6 @@
     return descriptor;
 }
 
-gralloc1_error_t Device::getStride(buffer_handle_t buffer, uint32_t* outStride)
-{
-    int32_t intError = mFunctions.getStride(mDevice, buffer, outStride);
-    return static_cast<gralloc1_error_t>(intError);
-}
-
 static inline bool allocationSucceded(gralloc1_error_t error)
 {
     return error == GRALLOC1_ERROR_NONE || error == GRALLOC1_ERROR_NOT_SHARED;
@@ -214,6 +221,99 @@
     return static_cast<gralloc1_error_t>(intError);
 }
 
+gralloc1_error_t Device::getDimensions(buffer_handle_t buffer,
+        uint32_t* outWidth, uint32_t* outHeight)
+{
+    uint32_t width = 0;
+    uint32_t height = 0;
+    int32_t intError = mFunctions.getDimensions(mDevice, buffer, &width,
+            &height);
+    auto error = static_cast<gralloc1_error_t>(intError);
+    if (error == GRALLOC1_ERROR_NONE) {
+        *outWidth = width;
+        *outHeight = height;
+    }
+    return error;
+}
+
+gralloc1_error_t Device::getFormat(buffer_handle_t buffer,
+        int32_t* outFormat)
+{
+    int32_t format = 0;
+    int32_t intError = mFunctions.getFormat(mDevice, buffer, &format);
+    auto error = static_cast<gralloc1_error_t>(intError);
+    if (error == GRALLOC1_ERROR_NONE) {
+        *outFormat = format;
+    }
+    return error;
+}
+
+gralloc1_error_t Device::getLayerCount(buffer_handle_t buffer,
+        uint32_t* outLayerCount)
+{
+    if (hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+        uint32_t layerCount = 0;
+        int32_t intError = mFunctions.getLayerCount(mDevice, buffer,
+                &layerCount);
+        auto error = static_cast<gralloc1_error_t>(intError);
+        if (error == GRALLOC1_ERROR_NONE) {
+            *outLayerCount = layerCount;
+        }
+        return error;
+    } else {
+        // Layered buffers are not supported on this device.
+        return GRALLOC1_ERROR_UNSUPPORTED;
+    }
+}
+
+gralloc1_error_t Device::getProducerUsage(buffer_handle_t buffer,
+        uint64_t* outProducerUsage)
+{
+    uint64_t usage = 0;
+    int32_t intError = mFunctions.getProducerUsage(mDevice, buffer, &usage);
+    auto error = static_cast<gralloc1_error_t>(intError);
+    if (error == GRALLOC1_ERROR_NONE) {
+        *outProducerUsage = usage;
+    }
+    return error;
+}
+
+gralloc1_error_t Device::getConsumerUsage(buffer_handle_t buffer,
+        uint64_t* outConsumerUsage)
+{
+    uint64_t usage = 0;
+    int32_t intError = mFunctions.getConsumerUsage(mDevice, buffer, &usage);
+    auto error = static_cast<gralloc1_error_t>(intError);
+    if (error == GRALLOC1_ERROR_NONE) {
+        *outConsumerUsage = usage;
+    }
+    return error;
+}
+
+gralloc1_error_t Device::getBackingStore(buffer_handle_t buffer,
+        uint64_t* outBackingStore)
+{
+    uint64_t store = 0;
+    int32_t intError = mFunctions.getBackingStore(mDevice, buffer, &store);
+    auto error = static_cast<gralloc1_error_t>(intError);
+    if (error == GRALLOC1_ERROR_NONE) {
+        *outBackingStore = store;
+    }
+    return error;
+}
+
+gralloc1_error_t Device::getStride(buffer_handle_t buffer,
+        uint32_t* outStride)
+{
+    uint32_t stride = 0;
+    int32_t intError = mFunctions.getStride(mDevice, buffer, &stride);
+    auto error = static_cast<gralloc1_error_t>(intError);
+    if (error == GRALLOC1_ERROR_NONE) {
+        *outStride = stride;
+    }
+    return error;
+}
+
 gralloc1_error_t Device::getNumFlexPlanes(buffer_handle_t buffer,
         uint32_t* outNumPlanes)
 {
@@ -233,7 +333,7 @@
         const sp<Fence>& acquireFence)
 {
     ALOGV("Calling lock(%p)", buffer);
-    return lockHelper(mFunctions.lock.pfn, buffer, producerUsage,
+    return lockHelper(mFunctions.lock, buffer, producerUsage,
             consumerUsage, accessRegion, outData, acquireFence);
 }
 
@@ -245,7 +345,7 @@
         const sp<Fence>& acquireFence)
 {
     ALOGV("Calling lockFlex(%p)", buffer);
-    return lockHelper(mFunctions.lockFlex.pfn, buffer, producerUsage,
+    return lockHelper(mFunctions.lockFlex, buffer, producerUsage,
             consumerUsage, accessRegion, outData, acquireFence);
 }
 
@@ -257,7 +357,7 @@
         const sp<Fence>& acquireFence)
 {
     ALOGV("Calling lockYCbCr(%p)", buffer);
-    return lockHelper(mFunctions.lockYCbCr.pfn, buffer, producerUsage,
+    return lockHelper(mFunctions.lockYCbCr, buffer, producerUsage,
             consumerUsage, accessRegion, outData, acquireFence);
 }
 
@@ -366,6 +466,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..b8bc6c4 100644
--- a/libs/ui/Gralloc1On0Adapter.cpp
+++ b/libs/ui/Gralloc1On0Adapter.cpp
@@ -18,9 +18,13 @@
 #define LOG_TAG "Gralloc1On0Adapter"
 //#define LOG_NDEBUG 0
 
+#include <ui/Gralloc1On0Adapter.h>
+
+
 #include <hardware/gralloc.h>
 
-#include <ui/Gralloc1On0Adapter.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Gralloc1.h>
 
 #include <utils/Log.h>
 
@@ -97,6 +101,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 +119,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..7af55e7
--- /dev/null
+++ b/libs/ui/GrallocAllocator.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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 <ui/GrallocAllocator.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+Allocator::Allocator()
+{
+    mAllocator = IAllocator::getService();
+    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..8095247
--- /dev/null
+++ b/libs/ui/GrallocMapper.cpp
@@ -0,0 +1,284 @@
+/*
+ * 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 <ui/GrallocMapper.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace Gralloc2 {
+
+static constexpr Error kDefaultError = Error::NO_RESOURCES;
+
+Mapper::Mapper()
+{
+    mMapper = IMapper::getService();
+    if (mMapper != nullptr && mMapper->isRemote()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+    }
+}
+
+Error Mapper::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::getDimensions(buffer_handle_t handle,
+        uint32_t* outWidth, uint32_t* outHeight) const
+{
+    Error error = kDefaultError;
+    mMapper->getDimensions(handle,
+            [&](const auto& tmpError, const auto& tmpWidth,
+                    const auto& tmpHeight)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outWidth = tmpWidth;
+                *outHeight = tmpHeight;
+            });
+
+    return error;
+}
+
+Error Mapper::getFormat(buffer_handle_t handle, int32_t* outFormat) const
+{
+    Error error = kDefaultError;
+    mMapper->getFormat(handle,
+            [&](const auto& tmpError, const auto& tmpFormat)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outFormat = static_cast<int32_t>(tmpFormat);
+            });
+
+    return error;
+}
+
+Error Mapper::getLayerCount(buffer_handle_t handle,
+        uint32_t* outLayerCount) const
+{
+    Error error = kDefaultError;
+    mMapper->getLayerCount(handle,
+            [&](const auto& tmpError, const auto& tmpLayerCount)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outLayerCount = tmpLayerCount;
+            });
+
+    return error;
+}
+
+Error Mapper::getProducerUsage(buffer_handle_t handle,
+    uint64_t* outProducerUsage) const
+{
+    Error error = kDefaultError;
+    mMapper->getProducerUsageMask(handle,
+            [&](const auto& tmpError, const auto& tmpProducerUsage)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outProducerUsage = tmpProducerUsage;
+            });
+
+    return error;
+}
+
+Error Mapper::getConsumerUsage(buffer_handle_t handle,
+        uint64_t* outConsumerUsage) const
+{
+    Error error = kDefaultError;
+    mMapper->getConsumerUsageMask(handle,
+            [&](const auto& tmpError, const auto& tmpConsumerUsage)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outConsumerUsage = tmpConsumerUsage;
+            });
+
+    return error;
+}
+
+Error Mapper::getBackingStore(buffer_handle_t handle,
+        uint64_t* outBackingStore) const
+{
+    Error error = kDefaultError;
+    mMapper->getBackingStore(handle,
+            [&](const auto& tmpError, const auto& tmpStore)
+            {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outBackingStore = tmpStore;
+            });
+
+    return 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 producerUsage,
+        uint64_t consumerUsage,
+        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, producerUsage, consumerUsage,
+            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 producerUsage,
+        uint64_t consumerUsage,
+        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, producerUsage, consumerUsage,
+            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..9006178 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -16,17 +16,15 @@
 
 #define LOG_TAG "GraphicBuffer"
 
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
+#include <cutils/atomic.h>
 
 #include <ui/GraphicBuffer.h>
+
+#include <cutils/atomic.h>
+
+#include <ui/GrallocMapper.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
-#include <ui/PixelFormat.h>
 
 namespace android {
 
@@ -50,6 +48,7 @@
     stride =
     format =
     usage  = 0;
+    layerCount = 0;
     handle = NULL;
 }
 
@@ -63,13 +62,48 @@
     stride =
     format =
     usage  = 0;
+    layerCount = 0;
     handle = NULL;
-    mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage,
+    mInitCheck = initSize(inWidth, inHeight, inFormat, 1, inUsage, inUsage,
             std::move(requestorName));
 }
 
 GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage, uint32_t inStride,
+        PixelFormat inFormat, uint32_t inLayerCount, uint64_t producerUsage,
+        uint64_t consumerUsage, std::string requestorName)
+    : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
+      mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
+{
+    width  =
+    height =
+    stride =
+    format =
+    usage  = 0;
+    layerCount = 0;
+    handle = NULL;
+    mInitCheck = initSize(inWidth, inHeight, inFormat, inLayerCount,
+            producerUsage, consumerUsage, 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)
+{
+    width  = static_cast<int>(inWidth);
+    height = static_cast<int>(inHeight);
+    stride = static_cast<int>(inStride);
+    format = inFormat;
+    layerCount = inLayerCount;
+    usage  = static_cast<int>(inUsage);
+    handle = inHandle;
+}
+
+GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inProducerUsage,
+        uint32_t inConsumerUsage, uint32_t inStride,
         native_handle_t* inHandle, bool keepOwnership)
     : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
       mBufferMapper(GraphicBufferMapper::get()),
@@ -79,10 +113,12 @@
     height = static_cast<int>(inHeight);
     stride = static_cast<int>(inStride);
     format = inFormat;
-    usage  = static_cast<int>(inUsage);
+    layerCount = inLayerCount;
+    usage  = static_cast<int>(inConsumerUsage | inProducerUsage);
     handle = inHandle;
 }
 
+
 GraphicBuffer::GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership)
     : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
       mBufferMapper(GraphicBufferMapper::get()),
@@ -93,6 +129,7 @@
     height = buffer->height;
     stride = buffer->stride;
     format = buffer->format;
+    layerCount = buffer->layerCount;
     usage  = buffer->usage;
     handle = buffer->handle;
 }
@@ -108,8 +145,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 +174,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 +183,7 @@
             static_cast<int>(inWidth) == width &&
             static_cast<int>(inHeight) == height &&
             inFormat == format &&
+            inLayerCount == layerCount &&
             static_cast<int>(inUsage) == usage)
         return NO_ERROR;
 
@@ -152,31 +192,36 @@
         allocator.free(handle);
         handle = 0;
     }
-    return initSize(inWidth, inHeight, inFormat, inUsage, "[Reallocation]");
+    return initSize(inWidth, inHeight, inFormat, inLayerCount, inUsage, 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, uint64_t inProducerUsage,
+        uint64_t inConsumerUsage, 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,
+            inProducerUsage, inConsumerUsage, &handle, &outStride, mId,
+            std::move(requestorName));
     if (err == NO_ERROR) {
         width = static_cast<int>(inWidth);
         height = static_cast<int>(inHeight);
         format = inFormat;
-        usage = static_cast<int>(inUsage);
+        layerCount = inLayerCount;
+        usage = static_cast<int>(inProducerUsage | inConsumerUsage);
         stride = static_cast<int>(outStride);
     }
     return err;
@@ -239,6 +284,12 @@
 status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect,
         void** vaddr, int fenceFd)
 {
+    return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd);
+}
+
+status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage,
+        uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd)
+{
     if (rect.left < 0 || rect.right  > width ||
         rect.top  < 0 || rect.bottom > height) {
         ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
@@ -246,8 +297,8 @@
                 width, height);
         return BAD_VALUE;
     }
-    status_t res = getBufferMapper().lockAsync(handle, inUsage, rect, vaddr,
-            fenceFd);
+    status_t res = getBufferMapper().lockAsync(handle, inProducerUsage,
+            inConsumerUsage, rect, vaddr, fenceFd);
     return res;
 }
 
@@ -281,7 +332,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 +352,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 +381,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 +418,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..3f18bbc 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -18,13 +18,18 @@
 #define LOG_TAG "GraphicBufferAllocator"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <ui/GraphicBufferAllocator.h>
+
+#include <stdio.h>
+
 #include <log/log.h>
 #include <utils/Singleton.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
-#include <ui/GraphicBufferAllocator.h>
-#include <ui/Gralloc1On0Adapter.h>
+#include <ui/GrallocAllocator.h>
+#include <ui/GrallocMapper.h>
+#include <ui/GraphicBufferMapper.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -36,8 +41,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,14 +65,18 @@
     for (size_t i=0 ; i<c ; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
         if (rec.size) {
-            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
+                    ", 0x%" PRIx64 " | %s\n",
                     list.keyAt(i), rec.size/1024.0,
-                    rec.width, rec.stride, rec.height, rec.format, rec.usage,
+                    rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+                    rec.producerUsage, rec.consumerUsage,
                     rec.requestorName.c_str());
         } else {
-            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64
+                    ", 0x%" PRIx64 " | %s\n",
                     list.keyAt(i),
-                    rec.width, rec.stride, rec.height, rec.format, rec.usage,
+                    rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+                    rec.producerUsage, rec.consumerUsage,
                     rec.requestorName.c_str());
         }
         result.append(buffer);
@@ -69,7 +84,14 @@
     }
     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 +102,113 @@
     ALOGD("%s", s.string());
 }
 
+namespace {
+
+class HalBuffer {
+public:
+    HalBuffer(const Gralloc2::Allocator* allocator,
+            uint32_t width, uint32_t height,
+            PixelFormat format, uint32_t layerCount, uint64_t producerUsage,
+            uint64_t consumerUsage)
+        : 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 = producerUsage;
+        info.consumerUsageMask = consumerUsage;
+
+        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 producerUsage %" PRIx64
+                    " consumerUsage %" PRIx64 ": %d",
+                    width, height, layerCount, format, producerUsage,
+                    consumerUsage, 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 producerUsage %" PRIx64
+                    " consumerUsage %" PRIx64 ": %d",
+                    width, height, layerCount, format, producerUsage,
+                    consumerUsage, 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, uint64_t producerUsage,
+        uint64_t consumerUsage, buffer_handle_t* handle, uint32_t* stride,
+        uint64_t graphicBufferId, std::string requestorName)
 {
     ATRACE_CALL();
 
@@ -91,43 +217,71 @@
     if (!width || !height)
         width = height = 1;
 
-    // Filter out any usage bits that should not be passed to the gralloc module
-    usage &= GRALLOC_USAGE_ALLOC_MASK;
+    // Ensure that layerCount is valid.
+    if (layerCount < 1)
+        layerCount = 1;
 
-    auto descriptor = mDevice->createDescriptor();
-    auto error = descriptor->setDimensions(width, height);
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to set dimensions to (%u, %u): %d", width, height, error);
-        return BAD_VALUE;
-    }
-    error = descriptor->setFormat(static_cast<android_pixel_format_t>(format));
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to set format to %d: %d", format, error);
-        return BAD_VALUE;
-    }
-    error = descriptor->setProducerUsage(
-            static_cast<gralloc1_producer_usage_t>(usage));
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to set producer usage to %u: %d", usage, error);
-        return BAD_VALUE;
-    }
-    error = descriptor->setConsumerUsage(
-            static_cast<gralloc1_consumer_usage_t>(usage));
-    if (error != GRALLOC1_ERROR_NONE) {
-        ALOGE("Failed to set consumer usage to %u: %d", usage, error);
-        return BAD_VALUE;
-    }
+    gralloc1_error_t error;
+    if (mAllocator->valid()) {
+        HalBuffer buffer(mAllocator.get(), width, height, format, layerCount,
+                producerUsage, consumerUsage);
+        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>(producerUsage));
+        if (error != GRALLOC1_ERROR_NONE) {
+            ALOGE("Failed to set producer usage to %" PRIx64 ": %d",
+                    producerUsage, error);
+            return BAD_VALUE;
+        }
+        error = descriptor->setConsumerUsage(
+                static_cast<gralloc1_consumer_usage_t>(consumerUsage));
+        if (error != GRALLOC1_ERROR_NONE) {
+            ALOGE("Failed to set consumer usage to %" PRIx64 ": %d",
+                    consumerUsage, 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 "
+                    "producerUsage %" PRIx64 " consumerUsage %" PRIx64 ": %d",
+                    width, height, layerCount, format, producerUsage,
+                    consumerUsage, 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,7 +293,9 @@
         rec.height = height;
         rec.stride = *stride;
         rec.format = format;
-        rec.usage = usage;
+        rec.layerCount = layerCount;
+        rec.producerUsage = producerUsage;
+        rec.consumerUsage = consumerUsage;
         rec.size = static_cast<size_t>(height * (*stride) * bpp);
         rec.requestorName = std::move(requestorName);
         list.add(*handle, rec);
@@ -152,7 +308,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..656472f 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -18,8 +18,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
-#include <stdint.h>
-#include <errno.h>
+#include <ui/GraphicBufferMapper.h>
 
 // We would eliminate the non-conforming zero-length array, but we can't since
 // this is effectively included from the Linux kernel
@@ -28,13 +27,11 @@
 #include <sync/sync.h>
 #pragma clang diagnostic pop
 
-#include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
-#include <ui/Gralloc1On0Adapter.h>
-#include <ui/GraphicBufferMapper.h>
-#include <ui/Rect.h>
+#include <ui/GrallocMapper.h>
+#include <ui/GraphicBuffer.h>
 
 #include <system/graphics.h>
 
@@ -44,8 +41,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 +55,20 @@
 {
     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 {
+        // This always returns GRALLOC1_BAD_HANDLE when handle is from a
+        // remote process and mDevice is backed by Gralloc1On0Adapter.
+        error = mDevice->retain(handle);
+        if (error == GRALLOC1_ERROR_BAD_HANDLE &&
+                mDevice->hasCapability(GRALLOC1_CAPABILITY_ON_ADAPTER)) {
+            ALOGE("registerBuffer by handle is not supported with "
+                  "Gralloc1On0Adapter");
+        }
+    }
+
     ALOGW_IF(error != GRALLOC1_ERROR_NONE, "registerBuffer(%p) failed: %d",
             handle, error);
 
@@ -64,7 +79,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 +97,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);
 
@@ -91,6 +120,140 @@
     return outRect;
 }
 
+
+status_t GraphicBufferMapper::getDimensions(buffer_handle_t handle,
+        uint32_t* outWidth, uint32_t* outHeight) const
+{
+    ATRACE_CALL();
+
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        mMapper->getDimensions(handle, outWidth, outHeight);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        error = mDevice->getDimensions(handle, outWidth, outHeight);
+    }
+
+    ALOGW_IF(error != GRALLOC1_ERROR_NONE, "getDimensions(%p, ...): failed %d",
+            handle, error);
+
+    return error;
+}
+
+status_t GraphicBufferMapper::getFormat(buffer_handle_t handle,
+        int32_t* outFormat) const
+{
+    ATRACE_CALL();
+
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        mMapper->getFormat(handle, outFormat);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        error = mDevice->getFormat(handle, outFormat);
+    }
+
+    ALOGW_IF(error != GRALLOC1_ERROR_NONE, "getFormat(%p, ...): failed %d",
+            handle, error);
+
+    return error;
+}
+
+status_t GraphicBufferMapper::getLayerCount(buffer_handle_t handle,
+        uint32_t* outLayerCount) const
+{
+    ATRACE_CALL();
+
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        mMapper->getLayerCount(handle, outLayerCount);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        error = mDevice->getLayerCount(handle, outLayerCount);
+    }
+
+    ALOGW_IF(error != GRALLOC1_ERROR_NONE, "getLayerCount(%p, ...): failed %d",
+            handle, error);
+
+    return error;
+}
+
+status_t GraphicBufferMapper::getProducerUsage(buffer_handle_t handle,
+        uint64_t* outProducerUsage) const
+{
+    ATRACE_CALL();
+
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        mMapper->getProducerUsage(handle, outProducerUsage);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        error = mDevice->getProducerUsage(handle, outProducerUsage);
+    }
+
+    ALOGW_IF(error != GRALLOC1_ERROR_NONE,
+            "getProducerUsage(%p, ...): failed %d", handle, error);
+
+    return error;
+}
+
+status_t GraphicBufferMapper::getConsumerUsage(buffer_handle_t handle,
+        uint64_t* outConsumerUsage) const
+{
+    ATRACE_CALL();
+
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        mMapper->getConsumerUsage(handle, outConsumerUsage);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        error = mDevice->getConsumerUsage(handle, outConsumerUsage);
+    }
+
+    ALOGW_IF(error != GRALLOC1_ERROR_NONE,
+            "getConsumerUsage(%p, ...): failed %d", handle, error);
+
+    return error;
+}
+
+status_t GraphicBufferMapper::getBackingStore(buffer_handle_t handle,
+        uint64_t* outBackingStore) const
+{
+    ATRACE_CALL();
+
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        mMapper->getBackingStore(handle, outBackingStore);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        error = mDevice->getBackingStore(handle, outBackingStore);
+    }
+
+    ALOGW_IF(error != GRALLOC1_ERROR_NONE,
+            "getBackingStore(%p, ...): failed %d", handle, error);
+
+    return error;
+}
+
+status_t GraphicBufferMapper::getStride(buffer_handle_t handle,
+        uint32_t* outStride) const
+{
+    ATRACE_CALL();
+
+    gralloc1_error_t error;
+    if (mMapper->valid()) {
+        mMapper->getStride(handle, outStride);
+        error = GRALLOC1_ERROR_NONE;
+    } else {
+        error = mDevice->getStride(handle, outStride);
+    }
+
+    ALOGW_IF(error != GRALLOC1_ERROR_NONE, "getStride(%p, ...): failed %d",
+            handle, error);
+
+    return error;
+}
+
 status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage,
         const Rect& bounds, void** vaddr)
 {
@@ -117,14 +280,31 @@
 status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
         uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd)
 {
+    return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd);
+}
+
+status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle,
+        uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds,
+        void** vaddr, int fenceFd)
+{
     ATRACE_CALL();
 
     gralloc1_rect_t accessRegion = asGralloc1Rect(bounds);
-    sp<Fence> fence = new Fence(fenceFd);
-    gralloc1_error_t error = mDevice->lock(handle,
-            static_cast<gralloc1_producer_usage_t>(usage),
-            static_cast<gralloc1_consumer_usage_t>(usage),
-            &accessRegion, vaddr, fence);
+    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, producerUsage, consumerUsage, accessRect,
+                    fenceFd, vaddr));
+    } else {
+        sp<Fence> fence = new Fence(fenceFd);
+        error = mDevice->lock(handle,
+                static_cast<gralloc1_producer_usage_t>(producerUsage),
+                static_cast<gralloc1_consumer_usage_t>(consumerUsage),
+                &accessRegion, vaddr, fence);
+    }
+
     ALOGW_IF(error != GRALLOC1_ERROR_NONE, "lock(%p, ...) failed: %d", handle,
             error);
 
@@ -160,38 +340,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 +481,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/GraphicsEnv.cpp b/libs/ui/GraphicsEnv.cpp
new file mode 100644
index 0000000..1d20424
--- /dev/null
+++ b/libs/ui/GraphicsEnv.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 1
+#define LOG_TAG "GraphicsEnv"
+#include <ui/GraphicsEnv.h>
+
+#include <mutex>
+
+#include <log/log.h>
+#include <nativeloader/dlext_namespaces.h>
+
+namespace android {
+
+/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
+    static GraphicsEnv env;
+    return env;
+}
+
+void GraphicsEnv::setDriverPath(const std::string path) {
+    if (!mDriverPath.empty()) {
+        ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
+                mDriverPath.c_str(), path.c_str());
+        return;
+    }
+    ALOGV("setting driver path to '%s'", path.c_str());
+    mDriverPath = path;
+}
+
+android_namespace_t* GraphicsEnv::getDriverNamespace() {
+    static std::once_flag once;
+    std::call_once(once, [this]() {
+        // TODO; In the next version of Android, all graphics drivers will be
+        // loaded into a custom namespace. To minimize risk for this release,
+        // only updated drivers use a custom namespace.
+        //
+        // Additionally, the custom namespace will be
+        // ANDROID_NAMESPACE_TYPE_ISOLATED, and will only have access to a
+        // subset of the system.
+        if (mDriverPath.empty())
+            return;
+
+        char defaultPath[PATH_MAX];
+        android_get_LD_LIBRARY_PATH(defaultPath, sizeof(defaultPath));
+        size_t defaultPathLen = strlen(defaultPath);
+
+        std::string path;
+        path.reserve(mDriverPath.size() + 1 + defaultPathLen);
+        path.append(mDriverPath);
+        path.push_back(':');
+        path.append(defaultPath, defaultPathLen);
+
+        mDriverNamespace = android_create_namespace(
+                "gfx driver",
+                nullptr,                    // ld_library_path
+                path.c_str(),               // default_library_path
+                ANDROID_NAMESPACE_TYPE_SHARED,
+                nullptr,                    // permitted_when_isolated_path
+                nullptr);                   // parent
+    });
+    return mDriverNamespace;
+}
+
+} // namespace android
+
+extern "C" android_namespace_t* android_getDriverNamespace() {
+    return android::GraphicsEnv::getInstance().getDriverNamespace();
+}
diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp
index 511f68a..39adc5e 100644
--- a/libs/ui/HdrCapabilities.cpp
+++ b/libs/ui/HdrCapabilities.cpp
@@ -16,44 +16,76 @@
 
 #include <ui/HdrCapabilities.h>
 
-#include <binder/Parcel.h>
-
 namespace android {
 
-status_t HdrCapabilities::writeToParcel(Parcel* parcel) const
-{
-    status_t result = parcel->writeInt32Vector(mSupportedHdrTypes);
-    if (result != OK) {
-        return result;
-    }
-    result = parcel->writeFloat(mMaxLuminance);
-    if (result != OK) {
-        return result;
-    }
-    result = parcel->writeFloat(mMaxAverageLuminance);
-    if (result != OK) {
-        return result;
-    }
-    result = parcel->writeFloat(mMinLuminance);
-    return result;
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundefined-reinterpret-cast"
+#endif
+
+HdrCapabilities::~HdrCapabilities() = default;
+HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) = default;
+HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) = default;
+
+
+size_t HdrCapabilities::getFlattenedSize() const {
+    return  sizeof(mMaxLuminance) +
+            sizeof(mMaxAverageLuminance) +
+            sizeof(mMinLuminance) +
+            sizeof(int32_t) +
+            mSupportedHdrTypes.size() * sizeof(int32_t);
 }
 
-status_t HdrCapabilities::readFromParcel(const Parcel* parcel)
-{
-    status_t result = parcel->readInt32Vector(&mSupportedHdrTypes);
-    if (result != OK) {
-        return result;
+status_t HdrCapabilities::flatten(void* buffer, size_t size) const {
+
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
     }
-    result = parcel->readFloat(&mMaxLuminance);
-    if (result != OK) {
-        return result;
+
+    int32_t* const buf = static_cast<int32_t*>(buffer);
+    reinterpret_cast<float&>(buf[0]) = mMaxLuminance;
+    reinterpret_cast<float&>(buf[1]) = mMaxAverageLuminance;
+    reinterpret_cast<float&>(buf[2]) = mMinLuminance;
+    buf[3] = static_cast<int32_t>(mSupportedHdrTypes.size());
+    for (size_t i = 0, c = mSupportedHdrTypes.size(); i < c; ++i) {
+        buf[4 + i] = mSupportedHdrTypes[i];
     }
-    result = parcel->readFloat(&mMaxAverageLuminance);
-    if (result != OK) {
-        return result;
-    }
-    result = parcel->readFloat(&mMinLuminance);
-    return result;
+    return NO_ERROR;
 }
 
+status_t HdrCapabilities::unflatten(void const* buffer, size_t size) {
+
+    size_t minSize = sizeof(mMaxLuminance) +
+                     sizeof(mMaxAverageLuminance) +
+                     sizeof(mMinLuminance) +
+                     sizeof(int32_t);
+
+    if (size < minSize) {
+        return NO_MEMORY;
+    }
+
+    int32_t const * const buf = static_cast<int32_t const *>(buffer);
+    const size_t itemCount = size_t(buf[3]);
+
+    // check the buffer is large enough
+    if (size < minSize + itemCount * sizeof(int32_t)) {
+        return BAD_VALUE;
+    }
+
+    mMaxLuminance        = reinterpret_cast<float const&>(buf[0]);
+    mMaxAverageLuminance = reinterpret_cast<float const&>(buf[1]);
+    mMinLuminance        = reinterpret_cast<float const&>(buf[2]);
+    if (itemCount) {
+        mSupportedHdrTypes.reserve(itemCount);
+        for (size_t i = 0; i < itemCount; ++i) {
+            mSupportedHdrTypes[i] = buf[4 + i];
+        }
+    }
+    return NO_ERROR;
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
 } // namespace android
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index cab1dde..e88fdd5 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -22,9 +22,12 @@
 
 uint32_t bytesPerPixel(PixelFormat format) {
     switch (format) {
+        case PIXEL_FORMAT_RGBA_FP16:
+            return 8;
         case PIXEL_FORMAT_RGBA_8888:
         case PIXEL_FORMAT_RGBX_8888:
         case PIXEL_FORMAT_BGRA_8888:
+        case PIXEL_FORMAT_RGBA_1010102:
             return 4;
         case PIXEL_FORMAT_RGB_888:
             return 3;
@@ -38,9 +41,12 @@
 
 uint32_t bitsPerPixel(PixelFormat format) {
     switch (format) {
+        case PIXEL_FORMAT_RGBA_FP16:
+            return 64;
         case PIXEL_FORMAT_RGBA_8888:
         case PIXEL_FORMAT_RGBX_8888:
         case PIXEL_FORMAT_BGRA_8888:
+        case PIXEL_FORMAT_RGBA_1010102:
             return 32;
         case PIXEL_FORMAT_RGB_888:
             return 24;
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 8cdab8c..b55c212 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -21,11 +21,13 @@
 }
 
 cc_test {
-    name: "vec_test",
-    srcs: ["vec_test.cpp"],
+    name: "colorspace_test",
+    shared_libs: ["libui"],
+    srcs: ["colorspace_test.cpp"],
 }
 
 cc_test {
-    name: "mat_test",
-    srcs: ["mat_test.cpp"],
+    name: "Gralloc1Mapper_test",
+    shared_libs: ["libui", "libutils"],
+    srcs: ["Gralloc1Mapper_test.cpp"],
 }
diff --git a/libs/ui/tests/Gralloc1Mapper_test.cpp b/libs/ui/tests/Gralloc1Mapper_test.cpp
new file mode 100644
index 0000000..b7c9f0f
--- /dev/null
+++ b/libs/ui/tests/Gralloc1Mapper_test.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Gralloc1Mapper_test"
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferMapper.h>
+#include <utils/Errors.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+class Gralloc1MapperTest : public ::testing::Test
+{
+public:
+    ~Gralloc1MapperTest() override = default;
+
+protected:
+    void SetUp() override {
+        buffer = new GraphicBuffer(4, 8, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN,
+                GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN, "Gralloc1MapperTest");
+        ASSERT_NE(nullptr, buffer.get());
+
+        handle = static_cast<buffer_handle_t>(buffer->handle);
+
+        mapper = &GraphicBufferMapper::get();
+    }
+
+    sp<GraphicBuffer> buffer;
+    buffer_handle_t handle;
+    GraphicBufferMapper* mapper;
+};
+
+TEST_F(Gralloc1MapperTest, Gralloc1MapperTest_getDimensions) {
+    uint32_t width = 0;
+    uint32_t height = 0;
+    status_t err = mapper->getDimensions(handle, &width, &height);
+    ASSERT_EQ(GRALLOC1_ERROR_NONE, err);
+    EXPECT_EQ(4U, width);
+    EXPECT_EQ(8U, height);
+}
+
+TEST_F(Gralloc1MapperTest, Gralloc1MapperTest_getFormat) {
+    int32_t value = 0;
+    status_t err = mapper->getFormat(handle, &value);
+    ASSERT_EQ(GRALLOC1_ERROR_NONE, err);
+    EXPECT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, value);
+}
+
+TEST_F(Gralloc1MapperTest, Gralloc1MapperTest_getLayerCount) {
+    uint32_t value = 0;
+    status_t err = mapper->getLayerCount(handle, &value);
+    if (err != GRALLOC1_ERROR_UNSUPPORTED) {
+        EXPECT_EQ(1U, value);
+    }
+}
+
+TEST_F(Gralloc1MapperTest, Gralloc1MapperTest_getProducerUsage) {
+    uint64_t value = 0;
+    status_t err = mapper->getProducerUsage(handle, &value);
+    ASSERT_EQ(GRALLOC1_ERROR_NONE, err);
+    EXPECT_EQ(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN, value);
+}
+
+TEST_F(Gralloc1MapperTest, Gralloc1MapperTest_getConsumerUsage) {
+    uint64_t value = 0;
+    status_t err = mapper->getConsumerUsage(handle, &value);
+    ASSERT_EQ(GRALLOC1_ERROR_NONE, err);
+    EXPECT_EQ(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN, value);
+}
+
+TEST_F(Gralloc1MapperTest, Gralloc1MapperTest_getBackingStore) {
+    uint64_t value = 0;
+    status_t err = mapper->getBackingStore(handle, &value);
+    ASSERT_EQ(GRALLOC1_ERROR_NONE, err);
+}
+
+TEST_F(Gralloc1MapperTest, Gralloc1MapperTest_getStride) {
+    uint32_t value = 0;
+    status_t err = mapper->getStride(handle, &value);
+    ASSERT_EQ(GRALLOC1_ERROR_NONE, err);
+    // The stride should be at least the width of the buffer.
+    EXPECT_LE(4U, value);
+}
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/mat_test.cpp b/libs/ui/tests/mat_test.cpp
deleted file mode 100644
index a2c63ac..0000000
--- a/libs/ui/tests/mat_test.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "RegionTest"
-
-#include <stdlib.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
-#include <gtest/gtest.h>
-
-#include <ui/mat4.h>
-
-namespace android {
-
-class MatTest : public testing::Test {
-protected:
-};
-
-TEST_F(MatTest, Basics) {
-    mat4 m0;
-    EXPECT_EQ(sizeof(mat4), sizeof(float)*16);
-}
-
-TEST_F(MatTest, ComparisonOps) {
-    mat4 m0;
-    mat4 m1(2);
-
-    EXPECT_TRUE(m0 == m0);
-    EXPECT_TRUE(m0 != m1);
-    EXPECT_FALSE(m0 != m0);
-    EXPECT_FALSE(m0 == m1);
-}
-
-TEST_F(MatTest, Constructors) {
-    mat4 m0;
-    ASSERT_EQ(m0[0].x, 1);
-    ASSERT_EQ(m0[0].y, 0);
-    ASSERT_EQ(m0[0].z, 0);
-    ASSERT_EQ(m0[0].w, 0);
-    ASSERT_EQ(m0[1].x, 0);
-    ASSERT_EQ(m0[1].y, 1);
-    ASSERT_EQ(m0[1].z, 0);
-    ASSERT_EQ(m0[1].w, 0);
-    ASSERT_EQ(m0[2].x, 0);
-    ASSERT_EQ(m0[2].y, 0);
-    ASSERT_EQ(m0[2].z, 1);
-    ASSERT_EQ(m0[2].w, 0);
-    ASSERT_EQ(m0[3].x, 0);
-    ASSERT_EQ(m0[3].y, 0);
-    ASSERT_EQ(m0[3].z, 0);
-    ASSERT_EQ(m0[3].w, 1);
-
-    mat4 m1(2);
-    mat4 m2(vec4(2));
-    mat4 m3(m2);
-
-    EXPECT_EQ(m1, m2);
-    EXPECT_EQ(m2, m3);
-    EXPECT_EQ(m3, m1);
-
-    mat4 m4(vec4(1), vec4(2), vec4(3), vec4(4));
-}
-
-TEST_F(MatTest, ArithmeticOps) {
-    mat4 m0;
-    mat4 m1(2);
-    mat4 m2(vec4(2));
-
-    m1 += m2;
-    EXPECT_EQ(mat4(4), m1);
-
-    m2 -= m1;
-    EXPECT_EQ(mat4(-2), m2);
-
-    m1 *= 2;
-    EXPECT_EQ(mat4(8), m1);
-
-    m1 /= 2;
-    EXPECT_EQ(mat4(4), m1);
-
-    m0 = -m0;
-    EXPECT_EQ(mat4(-1), m0);
-}
-
-TEST_F(MatTest, UnaryOps) {
-    const mat4 identity;
-    mat4 m0;
-
-    ++m0;
-    EXPECT_EQ(mat4( vec4(2,1,1,1), vec4(1,2,1,1), vec4(1,1,2,1), vec4(1,1,1,2) ), m0);
-    EXPECT_EQ(mat4( -vec4(2,1,1,1), -vec4(1,2,1,1), -vec4(1,1,2,1), -vec4(1,1,1,2) ), -m0);
-
-    --m0;
-    EXPECT_EQ(identity, m0);
-}
-
-TEST_F(MatTest, MiscOps) {
-    const mat4 identity;
-    mat4 m0;
-    EXPECT_EQ(4, trace(m0));
-
-    mat4 m1(vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16));
-    mat4 m2(vec4(1,5,9,13), vec4(2,6,10,14), vec4(3,7,11,15), vec4(4,8,12,16));
-    EXPECT_EQ(m1, transpose(m2));
-    EXPECT_EQ(m2, transpose(m1));
-    EXPECT_EQ(vec4(1,6,11,16), diag(m1));
-
-    EXPECT_EQ(identity, inverse(identity));
-
-    mat4 m3(vec4(4,3,0,0), vec4(3,2,0,0), vec4(0,0,1,0), vec4(0,0,0,1));
-    mat4 m3i(inverse(m3));
-    EXPECT_FLOAT_EQ(-2, m3i[0][0]);
-    EXPECT_FLOAT_EQ( 3, m3i[0][1]);
-    EXPECT_FLOAT_EQ( 3, m3i[1][0]);
-    EXPECT_FLOAT_EQ(-4, m3i[1][1]);
-
-    mat4 m3ii(inverse(m3i));
-    EXPECT_FLOAT_EQ(m3[0][0], m3ii[0][0]);
-    EXPECT_FLOAT_EQ(m3[0][1], m3ii[0][1]);
-    EXPECT_FLOAT_EQ(m3[1][0], m3ii[1][0]);
-    EXPECT_FLOAT_EQ(m3[1][1], m3ii[1][1]);
-
-    EXPECT_EQ(m1, m1*identity);
-}
-
-}; // namespace android
diff --git a/libs/ui/tests/vec_test.cpp b/libs/ui/tests/vec_test.cpp
deleted file mode 100644
index 454c999..0000000
--- a/libs/ui/tests/vec_test.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "RegionTest"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include <ui/Region.h>
-#include <ui/Rect.h>
-#include <ui/vec4.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-
-class VecTest : public testing::Test {
-};
-
-TEST_F(VecTest, Basics) {
-    vec4 v4;
-    vec3& v3(v4.xyz);
-
-    EXPECT_EQ(sizeof(vec4), sizeof(float)*4);
-    EXPECT_EQ(sizeof(vec3), sizeof(float)*3);
-    EXPECT_EQ(sizeof(vec2), sizeof(float)*2);
-    EXPECT_EQ((void*)&v3, (void*)&v4);
-}
-
-TEST_F(VecTest, Constructors) {
-    vec4 v0;
-    EXPECT_EQ(v0.x, 0);
-    EXPECT_EQ(v0.y, 0);
-    EXPECT_EQ(v0.z, 0);
-    EXPECT_EQ(v0.w, 0);
-
-    vec4 v1(1);
-    EXPECT_EQ(v1.x, 1);
-    EXPECT_EQ(v1.y, 1);
-    EXPECT_EQ(v1.z, 1);
-    EXPECT_EQ(v1.w, 1);
-
-    vec4 v2(1,2,3,4);
-    EXPECT_EQ(v2.x, 1);
-    EXPECT_EQ(v2.y, 2);
-    EXPECT_EQ(v2.z, 3);
-    EXPECT_EQ(v2.w, 4);
-
-    vec4 v3(v2);
-    EXPECT_EQ(v3.x, 1);
-    EXPECT_EQ(v3.y, 2);
-    EXPECT_EQ(v3.z, 3);
-    EXPECT_EQ(v3.w, 4);
-
-    vec4 v4(v3.xyz, 42);
-    EXPECT_EQ(v4.x, 1);
-    EXPECT_EQ(v4.y, 2);
-    EXPECT_EQ(v4.z, 3);
-    EXPECT_EQ(v4.w, 42);
-
-    vec4 v5(vec3(v2.xy, 42), 24);
-    EXPECT_EQ(v5.x, 1);
-    EXPECT_EQ(v5.y, 2);
-    EXPECT_EQ(v5.z, 42);
-    EXPECT_EQ(v5.w, 24);
-
-    tvec4<double> vd(2);
-    EXPECT_EQ(vd.x, 2);
-    EXPECT_EQ(vd.y, 2);
-    EXPECT_EQ(vd.z, 2);
-    EXPECT_EQ(vd.w, 2);
-}
-
-TEST_F(VecTest, Access) {
-    vec4 v0(1,2,3,4);
-    v0.x = 10;
-    v0.y = 20;
-    v0.z = 30;
-    v0.w = 40;
-    EXPECT_EQ(v0.x, 10);
-    EXPECT_EQ(v0.y, 20);
-    EXPECT_EQ(v0.z, 30);
-    EXPECT_EQ(v0.w, 40);
-
-    v0[0] = 100;
-    v0[1] = 200;
-    v0[2] = 300;
-    v0[3] = 400;
-    EXPECT_EQ(v0.x, 100);
-    EXPECT_EQ(v0.y, 200);
-    EXPECT_EQ(v0.z, 300);
-    EXPECT_EQ(v0.w, 400);
-
-    v0.xyz = vec3(1,2,3);
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 400);
-}
-
-TEST_F(VecTest, UnaryOps) {
-    vec4 v0(1,2,3,4);
-
-    v0 += 1;
-    EXPECT_EQ(v0.x, 2);
-    EXPECT_EQ(v0.y, 3);
-    EXPECT_EQ(v0.z, 4);
-    EXPECT_EQ(v0.w, 5);
-
-    v0 -= 1;
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 4);
-
-    v0 *= 2;
-    EXPECT_EQ(v0.x, 2);
-    EXPECT_EQ(v0.y, 4);
-    EXPECT_EQ(v0.z, 6);
-    EXPECT_EQ(v0.w, 8);
-
-    v0 /= 2;
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 4);
-
-    vec4 v1(10, 20, 30, 40);
-
-    v0 += v1;
-    EXPECT_EQ(v0.x, 11);
-    EXPECT_EQ(v0.y, 22);
-    EXPECT_EQ(v0.z, 33);
-    EXPECT_EQ(v0.w, 44);
-
-    v0 -= v1;
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 4);
-
-    v0 *= v1;
-    EXPECT_EQ(v0.x, 10);
-    EXPECT_EQ(v0.y, 40);
-    EXPECT_EQ(v0.z, 90);
-    EXPECT_EQ(v0.w, 160);
-
-    v0 /= v1;
-    EXPECT_EQ(v0.x, 1);
-    EXPECT_EQ(v0.y, 2);
-    EXPECT_EQ(v0.z, 3);
-    EXPECT_EQ(v0.w, 4);
-
-    ++v0;
-    EXPECT_EQ(v0.x, 2);
-    EXPECT_EQ(v0.y, 3);
-    EXPECT_EQ(v0.z, 4);
-    EXPECT_EQ(v0.w, 5);
-
-    ++++v0;
-    EXPECT_EQ(v0.x, 4);
-    EXPECT_EQ(v0.y, 5);
-    EXPECT_EQ(v0.z, 6);
-    EXPECT_EQ(v0.w, 7);
-
-    --v1;
-    EXPECT_EQ(v1.x,  9);
-    EXPECT_EQ(v1.y, 19);
-    EXPECT_EQ(v1.z, 29);
-    EXPECT_EQ(v1.w, 39);
-
-    v1 = -v1;
-    EXPECT_EQ(v1.x,  -9);
-    EXPECT_EQ(v1.y, -19);
-    EXPECT_EQ(v1.z, -29);
-    EXPECT_EQ(v1.w, -39);
-
-    tvec4<double> dv(1,2,3,4);
-    v1 += dv;
-    EXPECT_EQ(v1.x,  -8);
-    EXPECT_EQ(v1.y, -17);
-    EXPECT_EQ(v1.z, -26);
-    EXPECT_EQ(v1.w, -35);
-}
-
-TEST_F(VecTest, ComparisonOps) {
-    vec4 v0(1,2,3,4);
-    vec4 v1(10,20,30,40);
-
-    EXPECT_TRUE(v0 == v0);
-    EXPECT_TRUE(v0 != v1);
-    EXPECT_FALSE(v0 != v0);
-    EXPECT_FALSE(v0 == v1);
-}
-
-TEST_F(VecTest, ArithmeticOps) {
-    vec4 v0(1,2,3,4);
-    vec4 v1(10,20,30,40);
-
-    vec4 v2(v0 + v1);
-    EXPECT_EQ(v2.x, 11);
-    EXPECT_EQ(v2.y, 22);
-    EXPECT_EQ(v2.z, 33);
-    EXPECT_EQ(v2.w, 44);
-
-    v0 = v1 * 2;
-    EXPECT_EQ(v0.x, 20);
-    EXPECT_EQ(v0.y, 40);
-    EXPECT_EQ(v0.z, 60);
-    EXPECT_EQ(v0.w, 80);
-
-    v0 = 2 * v1;
-    EXPECT_EQ(v0.x, 20);
-    EXPECT_EQ(v0.y, 40);
-    EXPECT_EQ(v0.z, 60);
-    EXPECT_EQ(v0.w, 80);
-
-    tvec4<double> vd(2);
-    v0 = v1 * vd;
-    EXPECT_EQ(v0.x, 20);
-    EXPECT_EQ(v0.y, 40);
-    EXPECT_EQ(v0.z, 60);
-    EXPECT_EQ(v0.w, 80);
-}
-
-TEST_F(VecTest, ArithmeticFunc) {
-    vec3 east(1, 0, 0);
-    vec3 north(0, 1, 0);
-    vec3 up( cross(east, north) );
-    EXPECT_EQ(up, vec3(0,0,1));
-    EXPECT_EQ(dot(east, north), 0);
-    EXPECT_EQ(length(east), 1);
-    EXPECT_EQ(distance(east, north), sqrtf(2));
-
-    vec3 v0(1,2,3);
-    vec3 vn(normalize(v0));
-    EXPECT_FLOAT_EQ(1, length(vn));
-    EXPECT_FLOAT_EQ(length(v0), dot(v0, vn));
-
-    tvec3<double> vd(east);
-    EXPECT_EQ(length(vd), 1);
-}
-
-}; // namespace android
diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp
new file mode 100644
index 0000000..fb46c2b
--- /dev/null
+++ b/libs/ui/tools/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+    name: "libui_tools_default",
+    clang_cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
+
+cc_binary {
+    name: "lutgen",
+    cppflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    shared_libs: ["libui"],
+    srcs: ["lutgen.cpp"],
+}
diff --git a/libs/ui/tools/lutgen.cpp b/libs/ui/tools/lutgen.cpp
new file mode 100644
index 0000000..97b0822
--- /dev/null
+++ b/libs/ui/tools/lutgen.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <string>
+
+#include <getopt.h>
+
+#include <ui/ColorSpace.h>
+
+using namespace android;
+using namespace std;
+
+uint32_t gSize = 32;
+ColorSpace gColorSpaceSrc = ColorSpace::DisplayP3();
+ColorSpace gColorSpaceDst = ColorSpace::extendedSRGB();
+string gNameSrc = "DisplayP3";
+string gNameDst = "extendedSRGB";
+
+static void printHelp() {
+    cout << "lutgen -d SIZE -s SOURCE -t TARGET <lut file>" << endl;
+    cout << endl;
+    cout << "Generate a 3D LUT to convert between two color spaces." << endl;
+    cout << endl;
+    cout << "If <lut file> ends in .inc, data is generated without the array declaration." << endl;
+    cout << endl;
+    cout << "Options:" << endl;
+    cout << "  --help, -h" << endl;
+    cout << "    print this message" << endl;
+    cout << "  --dimension=, -d" << endl;
+    cout << "    the dimension of the 3D LUT. Example: 17 for a 17x17x17 LUT. 32 by default" << endl;
+    cout << "  --source=COLORSPACE, -s" << endl;
+    cout << "    the source color space, see below for available names. DisplayP3 by default" << endl;
+    cout << "  --target=COLORSPACE, -t" << endl;
+    cout << "    the target color space, see below for available names. extendedSRGB by default" << endl;
+    cout << endl;
+    cout << "Colorspace names:" << endl;
+    cout << "    sRGB" << endl;
+    cout << "    linearSRGB" << endl;
+    cout << "    extendedSRGB" << endl;
+    cout << "    linearExtendedSRGB" << endl;
+    cout << "    NTSC" << endl;
+    cout << "    BT709" << endl;
+    cout << "    BT2020" << endl;
+    cout << "    AdobeRGB" << endl;
+    cout << "    ProPhotoRGB" << endl;
+    cout << "    DisplayP3" << endl;
+    cout << "    DCIP3" << endl;
+    cout << "    ACES" << endl;
+    cout << "    ACEScg" << endl;
+}
+
+static const ColorSpace findColorSpace(const string& name) {
+    if (name == "linearSRGB") return ColorSpace::linearSRGB();
+    if (name == "extendedSRGB") return ColorSpace::extendedSRGB();
+    if (name == "linearExtendedSRGB") return ColorSpace::linearExtendedSRGB();
+    if (name == "NTSC") return ColorSpace::NTSC();
+    if (name == "BT709") return ColorSpace::BT709();
+    if (name == "BT2020") return ColorSpace::BT2020();
+    if (name == "AdobeRGB") return ColorSpace::AdobeRGB();
+    if (name == "ProPhotoRGB") return ColorSpace::ProPhotoRGB();
+    if (name == "DisplayP3") return ColorSpace::DisplayP3();
+    if (name == "DCIP3") return ColorSpace::DCIP3();
+    if (name == "ACES") return ColorSpace::ACES();
+    if (name == "ACEScg") return ColorSpace::ACEScg();
+    return ColorSpace::sRGB();
+}
+
+static int handleCommandLineArgments(int argc, char* argv[]) {
+    static constexpr const char* OPTSTR = "h:d:s:t:";
+    static const struct option OPTIONS[] = {
+            { "help",       no_argument,       0, 'h' },
+            { "dimension",  required_argument, 0, 'd' },
+            { "source",     required_argument, 0, 's' },
+            { "target",     required_argument, 0, 't' },
+            { 0, 0, 0, 0 }  // termination of the option list
+    };
+
+    int opt;
+    int index = 0;
+
+    while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &index)) >= 0) {
+        string arg(optarg ? optarg : "");
+        switch (opt) {
+            default:
+            case 'h':
+                printHelp();
+                exit(0);
+                break;
+            case 'd':
+                gSize = max(2, min(stoi(arg), 256));
+                break;
+            case 's':
+                gNameSrc = arg;
+                gColorSpaceSrc = findColorSpace(arg);
+                break;
+            case 't':
+                gNameDst = arg;
+                gColorSpaceDst = findColorSpace(arg);
+                break;
+        }
+    }
+
+    return optind;
+}
+
+int main(int argc, char* argv[]) {
+    int optionIndex = handleCommandLineArgments(argc, argv);
+    int numArgs = argc - optionIndex;
+
+    if (numArgs < 1) {
+        printHelp();
+        return 1;
+    }
+    
+    bool isInclude = false;
+
+    string filename(argv[optionIndex]);
+    size_t index = filename.find_last_of('.');
+
+    if (index != string::npos) {
+        string extension(filename.substr(index + 1));
+        isInclude = extension == "inc";
+    }
+
+    ofstream outputStream(filename, ios::trunc);
+    if (outputStream.good()) {
+        auto lut = ColorSpace::createLUT(gSize, gColorSpaceSrc, gColorSpaceDst);
+        auto data = lut.get();
+
+        outputStream << "// generated with lutgen " << filename.c_str() << endl;
+        outputStream << "// 3D LUT stored as an RGB16F texture, in GL order" << endl;
+        outputStream << "// Size is " << gSize << "x" << gSize << "x" << gSize << endl;
+
+        string src(gNameSrc);
+        string dst(gNameDst);
+
+        if (!isInclude) {
+            transform(src.begin(), src.end(), src.begin(), ::toupper);
+            transform(dst.begin(), dst.end(), dst.begin(), ::toupper);
+
+            outputStream << "const size_t LUT_" << src << "_TO_" << dst << "_SIZE = " << gSize << endl;
+            outputStream << "const uint16_t LUT_" << src << "_TO_" << dst << "[] = {";
+        } else {
+            outputStream << "// From " << src << " to " << dst << endl;
+        }
+
+        for (size_t z = 0; z < gSize; z++) {
+            for (size_t y = 0; y < gSize; y++) {
+                for (size_t x = 0; x < gSize; x++) {
+                    if (x % 4 == 0) outputStream << endl << "    ";
+
+                    half3 rgb = half3(*data++);
+
+                    const uint16_t r = rgb.r.getBits();
+                    const uint16_t g = rgb.g.getBits();
+                    const uint16_t b = rgb.b.getBits();
+
+                    outputStream << "0x" << setfill('0') << setw(4) << hex << r << ", ";
+                    outputStream << "0x" << setfill('0') << setw(4) << hex << g << ", ";
+                    outputStream << "0x" << setfill('0') << setw(4) << hex << b << ", ";
+                }
+            }
+        }
+
+        if (!isInclude) {
+            outputStream << endl << "}; // end LUT" << endl;
+        }
+
+        outputStream << endl;
+        outputStream.flush();
+        outputStream.close();
+    } else {
+        cerr << "Could not write to file: " << filename << endl;
+        return 1;
+
+    }
+
+    return 0;
+}
diff --git a/libs/vr/.clang-format b/libs/vr/.clang-format
new file mode 100644
index 0000000..04d7970
--- /dev/null
+++ b/libs/vr/.clang-format
@@ -0,0 +1,5 @@
+BasedOnStyle: Google
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/libs/vr/Android.bp b/libs/vr/Android.bp
new file mode 100644
index 0000000..e8176cf
--- /dev/null
+++ b/libs/vr/Android.bp
@@ -0,0 +1,3 @@
+subdirs = [
+    "*",
+]
diff --git a/libs/vr/CPPLINT.cfg b/libs/vr/CPPLINT.cfg
new file mode 100644
index 0000000..87fb641
--- /dev/null
+++ b/libs/vr/CPPLINT.cfg
@@ -0,0 +1,2 @@
+set noparent
+filter=-build/include_order,-legal/copyright,-build/include,-build/c++11,+build/include_alpha
diff --git a/libs/vr/libbufferhub/Android.mk b/libs/vr/libbufferhub/Android.mk
new file mode 100644
index 0000000..0877b0b
--- /dev/null
+++ b/libs/vr/libbufferhub/Android.mk
@@ -0,0 +1,56 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	buffer_hub_client.cpp \
+	buffer_hub_rpc.cpp \
+	ion_buffer.cpp
+
+includeFiles := \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	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..e2413bd
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -0,0 +1,417 @@
+#include <private/dvr/buffer_hub_client.h>
+
+#include <log/log.h>
+#include <poll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/platform_defines.h>
+
+#include "include/private/dvr/bufferhub_rpc.h"
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::WrapBuffer;
+using android::pdx::Status;
+
+namespace {
+
+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..fa61c4a
--- /dev/null
+++ b/libs/vr/libbufferhub/bufferhub_tests.cpp
@@ -0,0 +1,226 @@
+#include <gtest/gtest.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <mutex>
+#include <thread>
+
+#define RETRY_EINTR(fnc_call)                 \
+  ([&]() -> decltype(fnc_call) {              \
+    decltype(fnc_call) result;                \
+    do {                                      \
+      result = (fnc_call);                    \
+    } while (result == -1 && errno == EINTR); \
+    return result;                            \
+  })()
+
+using android::dvr::BufferProducer;
+using android::dvr::BufferConsumer;
+using android::pdx::LocalHandle;
+
+const int kWidth = 640;
+const int kHeight = 480;
+const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+const int kUsage = 0;
+const uint64_t kContext = 42;
+
+using LibBufferHubTest = ::testing::Test;
+
+TEST_F(LibBufferHubTest, TestBasicUsage) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  // Check that consumers can spawn other consumers.
+  std::unique_ptr<BufferConsumer> c2 =
+      BufferConsumer::Import(c->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+
+  EXPECT_EQ(0, p->Post(LocalHandle(), kContext));
+  // Both consumers should be triggered.
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+  EXPECT_LT(0, RETRY_EINTR(c2->Poll(10)));
+
+  uint64_t context;
+  LocalHandle fence;
+  EXPECT_LE(0, c->Acquire(&fence, &context));
+  EXPECT_EQ(kContext, context);
+  EXPECT_GE(0, RETRY_EINTR(c->Poll(0)));
+
+  EXPECT_LE(0, c2->Acquire(&fence, &context));
+  EXPECT_EQ(kContext, context);
+  EXPECT_GE(0, RETRY_EINTR(c2->Poll(0)));
+
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, c2->Discard());
+
+  EXPECT_LE(0, RETRY_EINTR(p->Poll(0)));
+  EXPECT_EQ(0, p->Gain(&fence));
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  Metadata m = {1, 3};
+  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+  EXPECT_LE(0, RETRY_EINTR(c->Poll(10)));
+
+  LocalHandle fence;
+  Metadata m2 = {};
+  EXPECT_EQ(0, c->Acquire(&fence, &m2));
+  EXPECT_EQ(m.field1, m2.field1);
+  EXPECT_EQ(m.field2, m2.field2);
+
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(0)));
+}
+
+TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+  EXPECT_GE(0, RETRY_EINTR(c->Poll(10)));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) {
+  struct Metadata {
+    int64_t field1;
+    int64_t field2;
+  };
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(Metadata));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  Metadata m = {1, 3};
+  EXPECT_EQ(0, p->Post(LocalHandle(), m));
+
+  LocalHandle fence;
+  int64_t sequence;
+  EXPECT_NE(0, c->Acquire(&fence, &sequence));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_EQ(0, p->Post(LocalHandle(), sequence));
+
+  LocalHandle fence;
+  EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestWithNoMeta) {
+  std::unique_ptr<BufferProducer> p =
+      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  LocalHandle fence;
+
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+  EXPECT_EQ(0, c->Acquire(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) {
+  std::unique_ptr<BufferProducer> p =
+      BufferProducer::Create(kWidth, kHeight, kFormat, kUsage);
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  int64_t sequence = 3;
+  EXPECT_NE(0, p->Post(LocalHandle(), sequence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferPersistence) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  // Record the original buffer id for later comparison.
+  const int buffer_id = p->id();
+
+  auto c = BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_NE(nullptr, c);
+
+  EXPECT_EQ(0, p->Post<void>(LocalHandle()));
+
+  // Close the connection to the producer. This should not affect the consumer.
+  p = nullptr;
+
+  LocalHandle fence;
+  EXPECT_EQ(0, c->Acquire(&fence));
+  EXPECT_EQ(0, c->Release(LocalHandle()));
+
+  // Attempt to reconnect to the persistent buffer.
+  p = BufferProducer::Create("TestPersistentBuffer");
+  ASSERT_NE(nullptr, p);
+  EXPECT_EQ(buffer_id, p->id());
+  EXPECT_EQ(0, p->Gain(&fence));
+}
+
+TEST_F(LibBufferHubTest, TestPersistentBufferMismatchParams) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  // Close the connection to the producer.
+  p = nullptr;
+
+  // Mismatch the params.
+  p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth * 2,
+                             kHeight, kFormat, kUsage);
+  ASSERT_EQ(nullptr, p);
+}
+
+TEST_F(LibBufferHubTest, TestRemovePersistentBuffer) {
+  auto p = BufferProducer::Create("TestPersistentBuffer", -1, -1, kWidth,
+                                  kHeight, kFormat, kUsage);
+  ASSERT_NE(nullptr, p);
+
+  LocalHandle fence;
+  auto c = BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_NE(nullptr, c);
+  EXPECT_NE(-EPIPE, c->Acquire(&fence));
+
+  // Test that removing persistence and closing the producer orphans the
+  // consumer.
+  EXPECT_EQ(0, p->RemovePersistence());
+  p = nullptr;
+
+  EXPECT_EQ(-EPIPE, c->Release(LocalHandle()));
+}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
new file mode 100644
index 0000000..cefde7b
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -0,0 +1,323 @@
+#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;
+
+  Status<int> GetEventMask(int events) {
+    if (auto* client_channel = GetChannel()) {
+      return client_channel->GetEventMask(events);
+    } else {
+      return pdx::ErrorStatus(EINVAL);
+    }
+  }
+
+  native_handle_t* native_handle() const {
+    return const_cast<native_handle_t*>(slices_[0].handle());
+  }
+  // If index is greater than or equal to slice_count(), the result is
+  // undefined.
+  native_handle_t* native_handle(size_t index) const {
+    return const_cast<native_handle_t*>(slices_[index].handle());
+  }
+
+  IonBuffer* buffer() { return &slices_[0]; }
+  // 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..f9b6975
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/native_buffer.h
@@ -0,0 +1,224 @@
+#ifndef ANDROID_DVR_NATIVE_BUFFER_H_
+#define ANDROID_DVR_NATIVE_BUFFER_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <log/log.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+#include <utils/RefBase.h>
+
+#include <private/dvr/buffer_hub_client.h>
+
+namespace android {
+namespace dvr {
+
+// ANativeWindowBuffer is the abstraction Android HALs and frameworks use to
+// pass around hardware graphics buffers. The following classes implement this
+// abstraction with different DVR backing buffers, all of which provide
+// different semantics on top of ion/gralloc buffers.
+
+// An implementation of ANativeWindowBuffer backed by an IonBuffer.
+class NativeBuffer
+    : public android::ANativeObjectBase<ANativeWindowBuffer, NativeBuffer,
+                                        android::LightRefBase<NativeBuffer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBuffer(const std::shared_ptr<IonBuffer>& buffer)
+      : BASE(), buffer_(buffer), fence_(kEmptyFence) {
+    ANativeWindowBuffer::width = buffer->width();
+    ANativeWindowBuffer::height = buffer->height();
+    ANativeWindowBuffer::stride = buffer->stride();
+    ANativeWindowBuffer::format = buffer->format();
+    ANativeWindowBuffer::usage = buffer->usage();
+    handle = buffer_->handle();
+  }
+
+  virtual ~NativeBuffer() {}
+
+  std::shared_ptr<IonBuffer> buffer() { return buffer_; }
+  int fence() const { return fence_.Get(); }
+
+  void SetFence(int fence) { fence_.Reset(fence); }
+
+ private:
+  friend class android::LightRefBase<NativeBuffer>;
+
+  std::shared_ptr<IonBuffer> buffer_;
+  pdx::LocalHandle fence_;
+
+  NativeBuffer(const NativeBuffer&) = delete;
+  void operator=(NativeBuffer&) = delete;
+};
+
+// NativeBufferProducerSlice is an implementation of ANativeWindowBuffer backed
+// by a buffer slice of a BufferProducer.
+class NativeBufferProducerSlice
+    : public android::ANativeObjectBase<
+          ANativeWindowBuffer, NativeBufferProducerSlice,
+          android::LightRefBase<NativeBufferProducerSlice>> {
+ public:
+  NativeBufferProducerSlice(const std::shared_ptr<BufferProducer>& buffer,
+                            int buffer_index)
+      : BASE(), buffer_(buffer) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle(buffer_index);
+  }
+
+  virtual ~NativeBufferProducerSlice() {}
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducerSlice>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+
+  NativeBufferProducerSlice(const NativeBufferProducerSlice&) = delete;
+  void operator=(NativeBufferProducerSlice&) = delete;
+};
+
+// NativeBufferProducer is an implementation of ANativeWindowBuffer backed by a
+// BufferProducer.
+class NativeBufferProducer : public android::ANativeObjectBase<
+  ANativeWindowBuffer, NativeBufferProducer,
+  android::LightRefBase<NativeBufferProducer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer,
+                       EGLDisplay display, uint32_t surface_buffer_index)
+      : BASE(),
+        buffer_(buffer),
+        surface_buffer_index_(surface_buffer_index),
+        display_(display) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    handle = buffer_->native_handle();
+    for (int i = 0; i < buffer->slice_count(); ++i) {
+      // display == null means don't create an EGL image. This is used by our
+      // Vulkan code.
+      slices_.push_back(new NativeBufferProducerSlice(buffer, i));
+      if (display_ != nullptr) {
+        egl_images_.push_back(eglCreateImageKHR(
+            display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+            static_cast<ANativeWindowBuffer*>(slices_.back().get()), nullptr));
+        if (egl_images_.back() == EGL_NO_IMAGE_KHR) {
+          ALOGE("NativeBufferProducer: eglCreateImageKHR failed");
+        }
+      }
+    }
+  }
+
+  explicit NativeBufferProducer(const std::shared_ptr<BufferProducer>& buffer)
+      : NativeBufferProducer(buffer, nullptr, 0) {}
+
+  virtual ~NativeBufferProducer() {
+    for (EGLImageKHR egl_image : egl_images_) {
+      if (egl_image != EGL_NO_IMAGE_KHR)
+        eglDestroyImageKHR(display_, egl_image);
+    }
+  }
+
+  EGLImageKHR image_khr(int index) const { return egl_images_[index]; }
+  std::shared_ptr<BufferProducer> buffer() const { return buffer_; }
+  int release_fence() const { return release_fence_.Get(); }
+  uint32_t surface_buffer_index() const { return surface_buffer_index_; }
+
+  // Return the release fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimReleaseFence() { return std::move(release_fence_); }
+
+  // Post the buffer consumer, closing the acquire and release fences.
+  int Post(int acquire_fence, uint64_t sequence) {
+    release_fence_.Close();
+    return buffer_->Post(pdx::LocalHandle(acquire_fence), sequence);
+  }
+
+  // Gain the buffer producer, closing the previous release fence if valid.
+  int Gain() { return buffer_->Gain(&release_fence_); }
+
+  // Asynchronously gain the buffer, closing the previous release fence.
+  int GainAsync() {
+    release_fence_.Close();
+    return buffer_->GainAsync();
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferProducer>;
+
+  std::shared_ptr<BufferProducer> buffer_;
+  pdx::LocalHandle release_fence_;
+  std::vector<android::sp<NativeBufferProducerSlice>> slices_;
+  std::vector<EGLImageKHR> egl_images_;
+  uint32_t surface_buffer_index_;
+  EGLDisplay display_;
+
+  NativeBufferProducer(const NativeBufferProducer&) = delete;
+  void operator=(NativeBufferProducer&) = delete;
+};
+
+// NativeBufferConsumer is an implementation of ANativeWindowBuffer backed by a
+// BufferConsumer.
+class NativeBufferConsumer : public android::ANativeObjectBase<
+                                 ANativeWindowBuffer, NativeBufferConsumer,
+                                 android::LightRefBase<NativeBufferConsumer>> {
+ public:
+  static constexpr int kEmptyFence = -1;
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer,
+                                int index)
+      : BASE(), buffer_(buffer), acquire_fence_(kEmptyFence), sequence_(0) {
+    ANativeWindowBuffer::width = buffer_->width();
+    ANativeWindowBuffer::height = buffer_->height();
+    ANativeWindowBuffer::stride = buffer_->stride();
+    ANativeWindowBuffer::format = buffer_->format();
+    ANativeWindowBuffer::usage = buffer_->usage();
+    LOG_ALWAYS_FATAL_IF(buffer_->slice_count() <= index);
+    handle = buffer_->slice(index)->handle();
+  }
+
+  explicit NativeBufferConsumer(const std::shared_ptr<BufferConsumer>& buffer)
+      : NativeBufferConsumer(buffer, 0) {}
+
+  virtual ~NativeBufferConsumer() {}
+
+  std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+  int acquire_fence() const { return acquire_fence_.Get(); }
+  uint64_t sequence() const { return sequence_; }
+
+  // Return the acquire fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimAcquireFence() { return std::move(acquire_fence_); }
+
+  // Acquire the underlying buffer consumer, closing the previous acquire fence
+  // if valid.
+  int Acquire() { return buffer_->Acquire(&acquire_fence_, &sequence_); }
+
+  // Release the buffer consumer, closing the acquire and release fences if
+  // valid.
+  int Release(int release_fence) {
+    acquire_fence_.Close();
+    sequence_ = 0;
+    return buffer_->Release(pdx::LocalHandle(release_fence));
+  }
+
+ private:
+  friend class android::LightRefBase<NativeBufferConsumer>;
+
+  std::shared_ptr<BufferConsumer> buffer_;
+  pdx::LocalHandle acquire_fence_;
+  uint64_t sequence_;
+
+  NativeBufferConsumer(const NativeBufferConsumer&) = delete;
+  void operator=(NativeBufferConsumer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_NATIVE_BUFFER_H_
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
new file mode 100644
index 0000000..4db2164
--- /dev/null
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -0,0 +1,322 @@
+#include <private/dvr/ion_buffer.h>
+
+#include <log/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..3ed7ff2
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/Android.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+	buffer_hub_queue_client.cpp \
+	buffer_hub_queue_core.cpp \
+	buffer_hub_queue_consumer.cpp \
+	buffer_hub_queue_producer.cpp \
+
+includeFiles := \
+	$(LOCAL_PATH)/include
+
+staticLibraries := \
+	libbufferhub \
+	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..0576b21
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -0,0 +1,418 @@
+#include "include/private/dvr/buffer_hub_queue_client.h"
+
+#include <inttypes.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+
+#include <array>
+
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/bufferhub_rpc.h>
+
+using android::pdx::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) {
+    ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s",
+          strerror(-ret));
+    return;
+  }
+
+  epoll_event event = {.events = EPOLLIN | EPOLLET,
+                       .data = {.u64 = static_cast<uint64_t>(
+                                    BufferHubQueue::kEpollQueueEventIndex)}};
+  ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
+  if (ret < 0) {
+    ALOGE("Failed to register ConsumerQueue into epoll event: %s",
+          strerror(-ret));
+  }
+}
+
+std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
+  Status<std::pair<LocalChannelHandle, size_t>> status =
+      InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>();
+
+  if (!status) {
+    ALOGE("Cannot create ConsumerQueue: %s", status.GetErrorMessage().c_str());
+    return nullptr;
+  }
+
+  auto return_value = status.take();
+
+  ALOGD("CreateConsumerQueue: meta_size_bytes=%zu", 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) {
+      ALOGD("Wait on epoll returns nothing before timeout.");
+      return false;
+    }
+
+    if (ret < 0 && ret != -EINTR) {
+      ALOGE("Failed to wait for buffers: %s", strerror(-ret));
+      return false;
+    }
+
+    const int num_events = ret;
+
+    // A BufferQueue's epoll fd tracks N+1 events, where there are N events,
+    // one for each buffer, in the queue and one extra event for the queue
+    // client itself.
+    for (int i = 0; i < num_events; i++) {
+      int64_t index = static_cast<int64_t>(events[i].data.u64);
+
+      ALOGD("New BufferHubQueue event %d: index=%" PRId64, i, index);
+
+      if (is_buffer_event_index(index) && (events[i].events & EPOLLIN)) {
+        auto buffer = buffers_[index];
+        ret = OnBufferReady(buffer);
+        if (ret < 0) {
+          ALOGE("Failed to set buffer ready: %s", 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.
+        ALOGW("Receives EPOLLHUP at slot: %" PRId64, 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) {
+          ALOGE("Failed to import buffer: %s", strerror(-ret));
+          continue;
+        }
+      } else {
+        ALOGW("Unknown event %d: u64=%" PRId64 ": events=%" PRIu32, i, index,
+              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.
+    ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu",
+          capacity_);
+    return -E2BIG;
+  }
+
+  if (buffers_[slot] != nullptr) {
+    // Replace the buffer if the slot is preoccupied. This could happen when the
+    // producer side replaced the slot with a newly allocated buffer. Detach the
+    // buffer 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) {
+    ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s",
+          strerror(-ret));
+    return ret;
+  }
+
+  buffers_[slot] = buf;
+  capacity_++;
+  return 0;
+}
+
+int BufferHubQueue::DetachBuffer(size_t slot) {
+  auto& buf = buffers_[slot];
+  if (buf == nullptr) {
+    ALOGE("BufferHubQueue::DetachBuffer: Invalid slot: %zu", slot);
+    return -EINVAL;
+  }
+
+  const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, buf->event_fd(), nullptr);
+  if (ret < 0) {
+    ALOGE(
+        "BufferHubQueue::DetachBuffer: Failed to detach buffer from epoll set: "
+        "%s",
+        strerror(-ret));
+    return ret;
+  }
+
+  buffers_[slot] = nullptr;
+  capacity_--;
+  return 0;
+}
+
+void BufferHubQueue::Enqueue(std::shared_ptr<BufferHubBuffer> buf,
+                             size_t slot) {
+  if (count() == capacity_) {
+    ALOGE("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) {
+  ALOGD("Dequeue: count=%zu, timeout=%d", count(), timeout);
+
+  if (count() == 0 && !WaitForBuffers(timeout))
+    return nullptr;
+
+  std::shared_ptr<BufferHubBuffer> buf;
+  BufferInfo& buffer_info = available_buffers_.Front();
+
+  // Report current pos as the output slot.
+  std::swap(buffer_info.slot, *slot);
+  // Swap buffer from vector to be returned later.
+  std::swap(buffer_info.buffer, buf);
+  // Swap metadata from vector into tmp so that we can write out to |meta|.
+  std::swap(buffer_info.metadata, meta_buffer_tmp_);
+
+  available_buffers_.PopFront();
+
+  if (!buf) {
+    ALOGE("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) {
+    ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s",
+          status.GetErrorMessage().c_str());
+    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) {
+    ALOGE("Parameter out_slot cannot be null.");
+    return -EINVAL;
+  }
+
+  if (is_full()) {
+    ALOGE("ProducerQueue::AllocateBuffer queue is at maximum capacity: %zu",
+          capacity());
+    return -E2BIG;
+  }
+
+  const size_t kBufferCount = 1U;
+
+  Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
+          width, height, format, usage, slice_count, kBufferCount);
+  if (!status) {
+    ALOGE(
+        "ProducerQueue::AllocateBuffer failed to create producer buffer "
+        "through BufferHub.");
+    return -status.error();
+  }
+
+  auto buffer_handle_slots = status.take();
+  LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != kBufferCount,
+                      "BufferHubRPC::ProducerQueueAllocateBuffers should "
+                      "return one and only one buffer handle.");
+
+  // We only allocate one buffer at a time.
+  auto& buffer_handle = buffer_handle_slots[0].first;
+  size_t buffer_slot = buffer_handle_slots[0].second;
+  ALOGD("ProducerQueue::AllocateBuffer, new buffer, channel_handle: %d",
+        buffer_handle.value());
+
+  *out_slot = buffer_slot;
+  return AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
+                   buffer_slot);
+}
+
+int ProducerQueue::AddBuffer(const std::shared_ptr<BufferProducer>& buf,
+                             size_t slot) {
+  // For producer buffer, we need to enqueue the newly added buffer
+  // immediately. Producer queue starts with all buffers in available state.
+  const int ret = BufferHubQueue::AddBuffer(buf, slot);
+  if (ret < 0)
+    return ret;
+
+  Enqueue(buf, slot);
+  return 0;
+}
+
+int ProducerQueue::DetachBuffer(size_t slot) {
+  Status<int> status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerQueueDetachBuffer>(slot);
+  if (!status) {
+    ALOGE(
+        "ProducerQueue::DetachBuffer failed to detach producer buffer through "
+        "BufferHub, error: %s",
+        status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  return BufferHubQueue::DetachBuffer(slot);
+}
+
+std::shared_ptr<BufferProducer> ProducerQueue::Dequeue(int timeout,
+                                                       size_t* slot) {
+  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) {
+    ALOGE(
+        "ConsumerQueue::ImportBuffers failed to import consumer buffer through "
+        "BufferBub, error: %s",
+        status.GetErrorMessage().c_str());
+    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) {
+    ALOGD("ConsumerQueue::ImportBuffers, new buffer, buffer_handle: %d",
+          buffer_handle_slot.first.value());
+
+    std::unique_ptr<BufferConsumer> buffer_consumer =
+        BufferConsumer::Import(std::move(buffer_handle_slot.first));
+    int ret = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second);
+    if (ret < 0) {
+      ALOGE("ConsumerQueue::ImportBuffers failed to add buffer, ret: %s",
+            strerror(-ret));
+      last_error = ret;
+      continue;
+    } else {
+      imported_buffers++;
+    }
+  }
+
+  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_) {
+    ALOGE(
+        "metadata size (%zu) for the dequeuing buffer does not match metadata "
+        "size (%zu) for the queue.",
+        meta_size, meta_size_);
+    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) {
+    ALOGW("No new buffer can be imported on buffer allocated event.");
+  } else if (ret < 0) {
+    ALOGE("Failed to import buffers on buffer allocated event.");
+  }
+  ALOGD("Imported %d consumer buffers.", ret);
+  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..a108042
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_core.cpp
@@ -0,0 +1,95 @@
+#include "include/private/dvr/buffer_hub_queue_core.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+/* static */
+std::shared_ptr<BufferHubQueueCore> BufferHubQueueCore::Create() {
+  auto core = std::shared_ptr<BufferHubQueueCore>(new BufferHubQueueCore());
+  core->producer_ = ProducerQueue::Create<BufferMetadata>();
+  return core;
+}
+
+/* static */
+std::shared_ptr<BufferHubQueueCore> BufferHubQueueCore::Create(
+    const std::shared_ptr<ProducerQueue>& producer) {
+  if (producer->metadata_size() != sizeof(BufferMetadata)) {
+    ALOGE(
+        "BufferHubQueueCore::Create producer's metadata size is different than "
+        "the size of BufferHubQueueCore::BufferMetadata");
+    return nullptr;
+  }
+
+  auto core = std::shared_ptr<BufferHubQueueCore>(new BufferHubQueueCore());
+  core->producer_ = producer;
+  return core;
+}
+
+BufferHubQueueCore::BufferHubQueueCore()
+    : generation_number_(0),
+      dequeue_timeout_ms_(BufferHubQueue::kNoTimeOut),
+      unique_id_(getUniqueId()) {}
+
+status_t BufferHubQueueCore::AllocateBuffer(uint32_t width, uint32_t height,
+                                            PixelFormat format, uint32_t usage,
+                                            size_t slice_count) {
+  size_t slot;
+
+  // Allocate new buffer through BufferHub and add it into |producer_| queue for
+  // bookkeeping.
+  if (producer_->AllocateBuffer(width, height, format, usage, slice_count,
+                                &slot) < 0) {
+    ALOGE("Failed to allocate new buffer in BufferHub.");
+    return NO_MEMORY;
+  }
+
+  auto buffer_producer = producer_->GetBuffer(slot);
+
+  LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr,
+                      "Failed to get buffer producer at slot: %zu", slot);
+
+  // Allocating a new buffer, |buffers_[slot]| should be in initial state.
+  LOG_ALWAYS_FATAL_IF(buffers_[slot].mGraphicBuffer != nullptr,
+                      "AllocateBuffer: slot %zu is not empty.", slot);
+
+  // 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));
+
+  LOG_ALWAYS_FATAL_IF(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) {
+    ALOGE("BufferHubQueueCore::DetachBuffer failed through RPC, ret=%s",
+          strerror(-ret));
+    return ret;
+  }
+
+  // Reset in memory objects related the the buffer.
+  buffers_[slot].mBufferProducer = nullptr;
+  buffers_[slot].mGraphicBuffer = nullptr;
+  buffers_[slot].mBufferState.detachProducer();
+  return NO_ERROR;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
new file mode 100644
index 0000000..752e8c4
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -0,0 +1,388 @@
+#include "include/private/dvr/buffer_hub_queue_producer.h"
+
+#include <inttypes.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+BufferHubQueueProducer::BufferHubQueueProducer(
+    const std::shared_ptr<BufferHubQueueCore>& core)
+    : core_(core), req_buffer_count_(kInvalidBufferCount) {}
+
+status_t BufferHubQueueProducer::requestBuffer(int slot,
+                                               sp<GraphicBuffer>* buf) {
+  ALOGD("requestBuffer: slot=%d", slot);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (slot < 0 || slot >= req_buffer_count_) {
+    ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot,
+          req_buffer_count_);
+    return BAD_VALUE;
+  } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  }
+
+  core_->buffers_[slot].mRequestBufferCalled = true;
+  *buf = core_->buffers_[slot].mGraphicBuffer;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setMaxDequeuedBufferCount(
+    int max_dequeued_buffers) {
+  ALOGD("setMaxDequeuedBufferCount: max_dequeued_buffers=%d",
+        max_dequeued_buffers);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (max_dequeued_buffers <= 0 ||
+      max_dequeued_buffers >
+          static_cast<int>(BufferHubQueue::kMaxQueueCapacity)) {
+    ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]",
+          max_dequeued_buffers, BufferHubQueue::kMaxQueueCapacity);
+    return BAD_VALUE;
+  }
+
+  req_buffer_count_ = max_dequeued_buffers;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::setAsyncMode(bool /* async */) {
+  ALOGE("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 */) {
+  ALOGD("dequeueBuffer: w=%u, h=%u, format=%d, usage=%u", width, height, format,
+        usage);
+
+  status_t ret;
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (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.
+    ALOGI(
+        "dequeueBuffer: requested buffer (w=%u, h=%u, format=%d) is different "
+        "from the buffer returned at slot: %zu (w=%d, h=%d, format=%d). Need "
+        "re-allocattion.",
+        width, height, format, slot, buffer_producer->width(),
+        buffer_producer->height(), buffer_producer->format());
+    // Mark the slot as reallocating, so that later we can set
+    // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
+    core_->buffers_[slot].mIsReallocating = true;
+
+    // Detach the old buffer once the allocation before allocating its
+    // replacement.
+    core_->DetachBuffer(slot);
+
+    // Allocate a new producer buffer with new buffer configs. Note that if
+    // there are already multiple buffers in the queue, the next one returned
+    // from |core_->producer_->Dequeue| may not be the new buffer we just
+    // reallocated. Retry up to BufferHubQueue::kMaxQueueCapacity times.
+    ret = core_->AllocateBuffer(width, height, format, usage, 1);
+    if (ret < 0)
+      return ret;
+  }
+
+  // With the BufferHub backed solution. Buffer slot returned from
+  // |core_->producer_->Dequeue| is guaranteed to avaiable for producer's use.
+  // It's either in free state (if the buffer has never been used before) or
+  // in queued state (if the buffer has been dequeued and queued back to
+  // BufferHubQueue).
+  // TODO(jwcai) Clean this up, make mBufferState compatible with BufferHub's
+  // model.
+  LOG_ALWAYS_FATAL_IF(!core_->buffers_[slot].mBufferState.isFree() &&
+                          !core_->buffers_[slot].mBufferState.isQueued(),
+                      "dequeueBuffer: slot %zu is not free or queued.", slot);
+
+  core_->buffers_[slot].mBufferState.freeQueued();
+  core_->buffers_[slot].mBufferState.dequeue();
+  ALOGD("dequeueBuffer: slot=%zu", slot);
+
+  // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
+  // just need to exopose that through |BufferHubQueue| once we need fence.
+  *out_fence = Fence::NO_FENCE;
+  *out_slot = slot;
+  ret = NO_ERROR;
+
+  if (core_->buffers_[slot].mIsReallocating) {
+    ret |= BUFFER_NEEDS_REALLOCATION;
+    core_->buffers_[slot].mIsReallocating = false;
+  }
+
+  return ret;
+}
+
+status_t BufferHubQueueProducer::detachBuffer(int /* slot */) {
+  ALOGE("BufferHubQueueProducer::detachBuffer not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::detachNextBuffer(
+    sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */) {
+  ALOGE("BufferHubQueueProducer::detachNextBuffer not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::attachBuffer(
+    int* /* out_slot */, const sp<GraphicBuffer>& /* buffer */) {
+  // With this BufferHub backed implementation, we assume (for now) all buffers
+  // are allocated and owned by the BufferHub. Thus the attempt of transfering
+  // ownership of a buffer to the buffer queue is intentionally unsupported.
+  LOG_ALWAYS_FATAL("BufferHubQueueProducer::attachBuffer not supported.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::queueBuffer(int slot,
+                                             const QueueBufferInput& input,
+                                             QueueBufferOutput* /* output */) {
+  ALOGD("queueBuffer: slot %d", 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) {
+    ALOGE("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_) {
+    ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot,
+          req_buffer_count_);
+    return BAD_VALUE;
+  } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  }
+
+  // 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) {
+  ALOGD(__FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (slot < 0 || slot >= req_buffer_count_) {
+    ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot,
+          req_buffer_count_);
+    return BAD_VALUE;
+  } else if (!core_->buffers_[slot].mBufferState.isDequeued()) {
+    ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)",
+          slot, core_->buffers_[slot].mBufferState.string());
+    return BAD_VALUE;
+  } else if (fence == NULL) {
+    ALOGE("cancelBuffer: fence is NULL");
+    return BAD_VALUE;
+  }
+
+  auto buffer_producer = core_->buffers_[slot].mBufferProducer;
+  core_->producer_->Enqueue(buffer_producer, slot);
+  core_->buffers_[slot].mBufferState.cancel();
+  core_->buffers_[slot].mFence = fence;
+  ALOGD("cancelBuffer: slot %d", slot);
+
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::query(int what, int* out_value) {
+  ALOGD(__FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+
+  if (out_value == NULL) {
+    ALOGE("query: out_value was NULL");
+    return BAD_VALUE;
+  }
+
+  int value = 0;
+  switch (what) {
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+      value = 0;
+      break;
+    case NATIVE_WINDOW_BUFFER_AGE:
+      value = 0;
+      break;
+    // 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;
+  }
+
+  ALOGD("query: key=%d, v=%d", what, value);
+  *out_value = value;
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::connect(
+    const sp<IProducerListener>& /* listener */, int /* api */,
+    bool /* producer_controlled_by_app */, QueueBufferOutput* /* output */) {
+  // Consumer interaction are actually handled by buffer hub, and we need
+  // to maintain consumer operations here. Hence |connect| is a NO-OP.
+  ALOGD(__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.
+  ALOGD(__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.
+    ALOGE("SidebandStream is not currently supported.");
+    return INVALID_OPERATION;
+  }
+  return NO_ERROR;
+}
+
+void BufferHubQueueProducer::allocateBuffers(uint32_t /* width */,
+                                             uint32_t /* height */,
+                                             PixelFormat /* format */,
+                                             uint32_t /* usage */) {
+  // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
+  // of buffers permitted by the current BufferQueue configuration (aka
+  // |req_buffer_count_|).
+  ALOGE("BufferHubQueueProducer::allocateBuffers not implemented.");
+}
+
+status_t BufferHubQueueProducer::allowAllocation(bool /* allow */) {
+  ALOGE("BufferHubQueueProducer::allowAllocation not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setGenerationNumber(
+    uint32_t generation_number) {
+  ALOGD(__FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+  core_->generation_number_ = generation_number;
+  return NO_ERROR;
+}
+
+String8 BufferHubQueueProducer::getConsumerName() const {
+  // BufferHub based implementation could have one to many producer/consumer
+  // relationship, thus |getConsumerName| from the producer side does not
+  // make any sense.
+  ALOGE("BufferHubQueueProducer::getConsumerName not supported.");
+  return String8("BufferHubQueue::DummyConsumer");
+}
+
+status_t BufferHubQueueProducer::setSharedBufferMode(
+    bool /* shared_buffer_mode */) {
+  ALOGE("BufferHubQueueProducer::setSharedBufferMode not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setAutoRefresh(bool /* auto_refresh */) {
+  ALOGE("BufferHubQueueProducer::setAutoRefresh not implemented.");
+  return INVALID_OPERATION;
+}
+
+status_t BufferHubQueueProducer::setDequeueTimeout(nsecs_t timeout) {
+  ALOGD(__FUNCTION__);
+
+  std::unique_lock<std::mutex> lock(core_->mutex_);
+  core_->dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
+  return NO_ERROR;
+}
+
+status_t BufferHubQueueProducer::getLastQueuedBuffer(
+    sp<GraphicBuffer>* /* out_buffer */, sp<Fence>* /* out_fence */,
+    float /*out_transform_matrix*/[16]) {
+  ALOGE("BufferHubQueueProducer::getLastQueuedBuffer not implemented.");
+  return INVALID_OPERATION;
+}
+
+void BufferHubQueueProducer::getFrameTimestamps(
+    FrameEventHistoryDelta* /*outDelta*/) {
+  ALOGE("BufferHubQueueProducer::getFrameTimestamps not implemented.");
+}
+
+status_t BufferHubQueueProducer::getUniqueId(uint64_t* out_id) const {
+  ALOGD(__FUNCTION__);
+
+  *out_id = core_->unique_id_;
+  return NO_ERROR;
+}
+
+IBinder* BufferHubQueueProducer::onAsBinder() {
+  // BufferHubQueueProducer is a non-binder implementation of
+  // IGraphicBufferProducer.
+  ALOGW("BufferHubQueueProducer::onAsBinder is not supported.");
+  return nullptr;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
new file mode 100644
index 0000000..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..6a9458c
--- /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 \
+	libnativewindow \
+
+staticLibraries := \
+	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..54098e8
--- /dev/null
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -0,0 +1,276 @@
+#include "include/private/dvr/display_client.h"
+
+#include <cutils/native_handle.h>
+#include <log/log.h>
+#include <pdx/default_transport/client_channel.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/status.h>
+
+#include <mutex>
+
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/native_buffer.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+using android::pdx::rpc::IfAnyOf;
+
+namespace android {
+namespace dvr {
+
+SurfaceClient::SurfaceClient(LocalChannelHandle channel_handle,
+                             SurfaceType type)
+    : Client{pdx::default_transport::ClientChannel::Create(
+          std::move(channel_handle))},
+      type_(type) {}
+
+SurfaceClient::SurfaceClient(const std::string& endpoint_path, SurfaceType type)
+    : Client{pdx::default_transport::ClientChannelFactory::Create(
+                 endpoint_path),
+             kInfiniteTimeout},
+      type_(type) {}
+
+int SurfaceClient::GetMetadataBufferFd(LocalHandle* out_fd) {
+  auto buffer_producer = GetMetadataBuffer();
+  if (!buffer_producer)
+    return -ENOMEM;
+
+  *out_fd = buffer_producer->GetBlobFd();
+  return 0;
+}
+
+std::shared_ptr<BufferProducer> SurfaceClient::GetMetadataBuffer() {
+  if (!metadata_buffer_) {
+    auto status = InvokeRemoteMethod<DisplayRPC::GetMetadataBuffer>();
+    if (!status) {
+      ALOGE(
+          "SurfaceClient::AllocateMetadataBuffer: Failed to allocate buffer: "
+          "%s",
+          status.GetErrorMessage().c_str());
+      return nullptr;
+    }
+
+    metadata_buffer_ = BufferProducer::Import(status.take());
+  }
+
+  return metadata_buffer_;
+}
+
+DisplaySurfaceClient::DisplaySurfaceClient(int width, int height, int format,
+                                           int usage, int flags)
+    : BASE(DisplayRPC::kClientPath, SurfaceTypeEnum::Normal),
+      width_(width),
+      height_(height),
+      format_(format),
+      usage_(usage),
+      flags_(flags),
+      z_order_(0),
+      visible_(true),
+      exclude_from_blur_(false),
+      blur_behind_(true),
+      mapped_metadata_buffer_(nullptr) {
+  auto status = InvokeRemoteMethod<DisplayRPC::CreateSurface>(
+      width, height, format, usage, flags);
+  if (!status) {
+    ALOGE(
+        "DisplaySurfaceClient::DisplaySurfaceClient: Failed to create display "
+        "surface: %s",
+        status.GetErrorMessage().c_str());
+    Close(status.error());
+  }
+}
+
+void DisplaySurfaceClient::SetVisible(bool visible) {
+  SetAttributes({{DisplaySurfaceAttributeEnum::Visible,
+                  DisplaySurfaceAttributeValue{visible}}});
+}
+
+void DisplaySurfaceClient::SetZOrder(int z_order) {
+  SetAttributes({{DisplaySurfaceAttributeEnum::ZOrder,
+                  DisplaySurfaceAttributeValue{z_order}}});
+}
+
+void DisplaySurfaceClient::SetExcludeFromBlur(bool exclude_from_blur) {
+  SetAttributes({{DisplaySurfaceAttributeEnum::ExcludeFromBlur,
+                  DisplaySurfaceAttributeValue{exclude_from_blur}}});
+}
+
+void DisplaySurfaceClient::SetBlurBehind(bool blur_behind) {
+  SetAttributes({{DisplaySurfaceAttributeEnum::BlurBehind,
+                  DisplaySurfaceAttributeValue{blur_behind}}});
+}
+
+void DisplaySurfaceClient::SetAttributes(
+    const DisplaySurfaceAttributes& attributes) {
+  Status<int> status =
+      InvokeRemoteMethod<DisplayRPC::SetAttributes>(attributes);
+  if (!status) {
+    ALOGE(
+        "DisplaySurfaceClient::SetAttributes: Failed to set display surface "
+        "attributes: %s",
+        status.GetErrorMessage().c_str());
+    return;
+  }
+
+  // Set the local cached copies of the attributes we care about from the full
+  // set of attributes sent to the display service.
+  for (const auto& attribute : attributes) {
+    const auto& key = attribute.first;
+    const auto* variant = &attribute.second;
+    bool invalid_value = false;
+    switch (key) {
+      case DisplaySurfaceAttributeEnum::Visible:
+        invalid_value =
+            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
+        break;
+      case DisplaySurfaceAttributeEnum::ZOrder:
+        invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
+        break;
+      case DisplaySurfaceAttributeEnum::ExcludeFromBlur:
+        invalid_value =
+            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &exclude_from_blur_);
+        break;
+      case DisplaySurfaceAttributeEnum::BlurBehind:
+        invalid_value =
+            !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &blur_behind_);
+        break;
+    }
+
+    if (invalid_value) {
+      ALOGW(
+          "DisplaySurfaceClient::SetAttributes: Failed to set display "
+          "surface attribute '%s' because of incompatible type: %d",
+          DisplaySurfaceAttributeEnum::ToString(key).c_str(), variant->index());
+    }
+  }
+}
+
+std::shared_ptr<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..8fd3627
--- /dev/null
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -0,0 +1,126 @@
+#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 dvrDisplayManagerClientGetEventFd(DvrDisplayManagerClient* client) {
+  return client->client->event_fd();
+}
+
+int dvrDisplayManagerClientTranslateEpollEventMask(
+    DvrDisplayManagerClient* client, int in_events, int* out_events) {
+  auto result = client->client->GetChannel()->GetEventMask(in_events);
+
+  if (!result) {
+    return -EIO;
+  }
+
+  *out_events = result.get();
+
+  return 0;
+}
+
+int dvrDisplayManagerClientGetSurfaceList(
+    DvrDisplayManagerClient* client,
+    DvrDisplayManagerClientSurfaceList** surface_list) {
+  std::vector<android::dvr::DisplaySurfaceInfo> list;
+  int ret = client->client->GetSurfaceList(&list);
+  if (ret < 0)
+    return ret;
+
+  *surface_list = new DvrDisplayManagerClientSurfaceList(std::move(list));
+  return ret;
+}
+
+void dvrDisplayManagerClientSurfaceListDestroy(
+    DvrDisplayManagerClientSurfaceList* surface_list) {
+  delete surface_list;
+}
+
+size_t dvrDisplayManagerClientSurfaceListGetSize(
+    DvrDisplayManagerClientSurfaceList* surface_list) {
+  return surface_list->list.size();
+}
+
+int dvrDisplayManagerClientSurfaceListGetSurfaceId(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+  return surface_list->list[index].surface_id;
+}
+
+int dvrDisplayManagerClientSurfaceListGetClientZOrder(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+  return surface_list->list[index].ClientZOrder();
+}
+
+bool dvrDisplayManagerClientSurfaceListGetClientIsVisible(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index) {
+  return surface_list->list[index].IsClientVisible();
+}
+
+int dvrDisplayManagerClientGetSurfaceBuffers(
+    DvrDisplayManagerClient* client, int surface_id,
+    DvrDisplayManagerClientSurfaceBuffers** surface_buffers) {
+  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..154afbe
--- /dev/null
+++ b/libs/vr/libdisplay/frame_history.cpp
@@ -0,0 +1,147 @@
+#include <private/dvr/frame_history.h>
+
+#include <errno.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/sync_util.h>
+
+using android::pdx::LocalHandle;
+
+constexpr int kNumFramesToUseForSchedulePrediction = 10;
+constexpr int kDefaultVsyncIntervalPrediction = 1;
+constexpr int kMaxVsyncIntervalPrediction = 4;
+constexpr int kDefaultPendingFrameBufferSize = 10;
+
+namespace android {
+namespace dvr {
+
+FrameHistory::PendingFrame::PendingFrame()
+    : start_ns(0), scheduled_vsync(0), scheduled_finish_ns(0) {}
+
+FrameHistory::PendingFrame::PendingFrame(int64_t start_ns,
+                                         uint32_t scheduled_vsync,
+                                         int64_t scheduled_finish_ns,
+                                         LocalHandle&& fence)
+    : start_ns(start_ns), scheduled_vsync(scheduled_vsync),
+      scheduled_finish_ns(scheduled_finish_ns), fence(std::move(fence)) {}
+
+FrameHistory::FrameHistory() : FrameHistory(kDefaultPendingFrameBufferSize) {}
+
+FrameHistory::FrameHistory(int pending_frame_buffer_size)
+    : pending_frames_(pending_frame_buffer_size),
+      finished_frames_(pending_frame_buffer_size),
+      frame_duration_history_(kNumFramesToUseForSchedulePrediction) {}
+
+void FrameHistory::Reset(int pending_frame_buffer_size) {
+  pending_frames_.Reset(pending_frame_buffer_size);
+  finished_frames_.Reset(pending_frame_buffer_size);
+  frame_duration_history_.Clear();
+}
+
+void FrameHistory::OnFrameStart(uint32_t scheduled_vsync,
+                                int64_t scheduled_finish_ns) {
+  if (!pending_frames_.IsEmpty() && !pending_frames_.Back().fence) {
+    // If we don't have a fence set for the previous frame it's because
+    // OnFrameStart() was called twice in a row with no OnFrameSubmit() call. In
+    // that case throw out the pending frame data for the last frame.
+    pending_frames_.PopBack();
+  }
+
+  if (pending_frames_.IsFull()) {
+    ALOGW("Pending frames buffer is full. Discarding pending frame data.");
+  }
+
+  pending_frames_.Append(PendingFrame(GetSystemClockNs(), scheduled_vsync,
+                                      scheduled_finish_ns, LocalHandle()));
+}
+
+void FrameHistory::OnFrameSubmit(LocalHandle&& fence) {
+  // Add the fence to the previous frame data in pending_frames so we can
+  // track when it finishes.
+  if (!pending_frames_.IsEmpty() && !pending_frames_.Back().fence) {
+    if (fence && pending_frames_.Back().scheduled_vsync != UINT32_MAX)
+      pending_frames_.Back().fence = std::move(fence);
+    else
+      pending_frames_.PopBack();
+  }
+}
+
+void FrameHistory::CheckForFinishedFrames() {
+  if (pending_frames_.IsEmpty())
+    return;
+
+  android::dvr::FenceInfoBuffer fence_info_buffer;
+  while (!pending_frames_.IsEmpty()) {
+    const auto& pending_frame = pending_frames_.Front();
+    if (!pending_frame.fence) {
+      // The frame hasn't been submitted yet, so there's nothing more to do
+      break;
+    }
+
+    int64_t fence_signaled_time = -1;
+    int fence = pending_frame.fence.Get();
+    int sync_result = sync_wait(fence, 0);
+    if (sync_result == 0) {
+      int fence_signaled_result =
+          GetFenceSignaledTimestamp(fence, &fence_info_buffer,
+                                    &fence_signaled_time);
+      if (fence_signaled_result < 0) {
+        ALOGE("Failed getting signaled timestamp from fence");
+      } else {
+        // The frame is finished. Record the duration and move the frame data
+        // from pending_frames_ to finished_frames_.
+        DvrFrameScheduleResult schedule_result = {};
+        schedule_result.vsync_count = pending_frame.scheduled_vsync;
+        schedule_result.scheduled_frame_finish_ns =
+            pending_frame.scheduled_finish_ns;
+        schedule_result.frame_finish_offset_ns =
+            fence_signaled_time - pending_frame.scheduled_finish_ns;
+        finished_frames_.Append(schedule_result);
+        frame_duration_history_.Append(
+            fence_signaled_time - pending_frame.start_ns);
+      }
+      pending_frames_.PopFront();
+    } else {
+      if (errno != ETIME) {
+        ALOGE("sync_wait on frame fence failed. fence=%d errno=%d (%s).",
+              fence, errno, strerror(errno));
+      }
+      break;
+    }
+  }
+}
+
+int FrameHistory::PredictNextFrameVsyncInterval(int64_t vsync_period_ns) const {
+  if (frame_duration_history_.IsEmpty())
+    return kDefaultVsyncIntervalPrediction;
+
+  double total = 0;
+  for (size_t i = 0; i < frame_duration_history_.GetSize(); ++i)
+    total += frame_duration_history_.Get(i);
+  double avg_duration = total / frame_duration_history_.GetSize();
+
+  return std::min(kMaxVsyncIntervalPrediction,
+                  static_cast<int>(avg_duration / vsync_period_ns) + 1);
+}
+
+int FrameHistory::GetPreviousFrameResults(DvrFrameScheduleResult* results,
+                                          int in_result_count) {
+  int out_result_count =
+      std::min(in_result_count, static_cast<int>(finished_frames_.GetSize()));
+  for (int i = 0; i < out_result_count; ++i) {
+    results[i] = finished_frames_.Get(0);
+    finished_frames_.PopFront();
+  }
+  return out_result_count;
+}
+
+uint32_t FrameHistory::GetCurrentFrameVsync() const {
+  return pending_frames_.IsEmpty() ?
+      UINT32_MAX : pending_frames_.Back().scheduled_vsync;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/gl_fenced_flush.cpp b/libs/vr/libdisplay/gl_fenced_flush.cpp
new file mode 100644
index 0000000..c70d554
--- /dev/null
+++ b/libs/vr/libdisplay/gl_fenced_flush.cpp
@@ -0,0 +1,39 @@
+#include "include/private/dvr/gl_fenced_flush.h"
+
+#include <EGL/eglext.h>
+#include <GLES3/gl31.h>
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <log/log.h>
+
+using android::pdx::LocalHandle;
+
+namespace android {
+namespace dvr {
+
+LocalHandle CreateGLSyncAndFlush(EGLDisplay display) {
+  ATRACE_NAME("CreateGLSyncAndFlush");
+
+  EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
+                      EGL_NO_NATIVE_FENCE_FD_ANDROID, EGL_NONE};
+  EGLSyncKHR sync_point =
+      eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+  glFlush();
+  if (sync_point == EGL_NO_SYNC_KHR) {
+    ALOGE("sync_point == EGL_NO_SYNC_KHR");
+    return LocalHandle();
+  }
+  EGLint fence_fd = eglDupNativeFenceFDANDROID(display, sync_point);
+  eglDestroySyncKHR(display, sync_point);
+
+  if (fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+    ALOGE("fence_fd == EGL_NO_NATIVE_FENCE_FD_ANDROID");
+    return LocalHandle();
+  }
+  return LocalHandle(fence_fd);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/graphics.cpp b/libs/vr/libdisplay/graphics.cpp
new file mode 100644
index 0000000..d0557a9
--- /dev/null
+++ b/libs/vr/libdisplay/graphics.cpp
@@ -0,0 +1,1592 @@
+#include <dvr/graphics.h>
+
+#include <inttypes.h>
+#include <sys/timerfd.h>
+#include <array>
+#include <vector>
+
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR 1
+#endif
+#include <vulkan/vulkan.h>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/frame_history.h>
+#include <private/dvr/gl_fenced_flush.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+#include <private/dvr/graphics_private.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/native_buffer_queue.h>
+#include <private/dvr/sensor_constants.h>
+#include <private/dvr/video_mesh_surface_client.h>
+#include <private/dvr/vsync_client.h>
+
+#include <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=%" PRId64,
+              p->key, p->value);
+        return nullptr;
+    }
+  }
+
+  request_flags |= disable_distortion
+                       ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION
+                       : 0;
+  request_flags |=
+      disable_stabilization ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS : 0;
+  request_flags |=
+      disable_cac ? DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC : 0;
+  request_flags |= vertical_flip ? DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP : 0;
+  request_flags |= (geometry_type == DVR_SURFACE_GEOMETRY_SEPARATE_2)
+                       ? DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2
+                       : 0;
+
+  if (request_width == -1) {
+    request_width = disable_distortion ? metrics->display_native_width
+                                       : metrics->distorted_width;
+    if (!disable_distortion &&
+        geometry_type == DVR_SURFACE_GEOMETRY_SEPARATE_2) {
+      // The metrics always return the single wide buffer resolution.
+      // When split between eyes, we need to halve the width of the surface.
+      request_width /= 2;
+    }
+  }
+  if (request_height == -1) {
+    request_height = disable_distortion ? metrics->display_native_height
+                                        : metrics->distorted_height;
+  }
+
+  std::shared_ptr<android::dvr::DisplaySurfaceClient> surface =
+      client->CreateDisplaySurface(request_width, request_height,
+                                   request_format, request_usage,
+                                   request_flags);
+  surface->SetAttributes(
+      {{DisplaySurfaceAttributeEnum::Visible,
+        DisplaySurfaceAttributeValue{request_visible}},
+       {DisplaySurfaceAttributeEnum::ZOrder,
+        DisplaySurfaceAttributeValue{request_z_order}},
+       {DisplaySurfaceAttributeEnum::ExcludeFromBlur,
+        DisplaySurfaceAttributeValue{request_exclude_from_blur}},
+       {DisplaySurfaceAttributeEnum::BlurBehind,
+        DisplaySurfaceAttributeValue{request_blur_behind}}});
+
+  // Handle parameter output requests down here so we can return surface info.
+  for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+    switch (p->key) {
+      case DVR_SURFACE_PARAMETER_DISPLAY_WIDTH_OUT:
+        *static_cast<int32_t*>(p->value_out) = metrics->display_native_width;
+        break;
+      case DVR_SURFACE_PARAMETER_DISPLAY_HEIGHT_OUT:
+        *static_cast<int32_t*>(p->value_out) = metrics->display_native_height;
+        break;
+      case DVR_SURFACE_PARAMETER_SURFACE_WIDTH_OUT:
+        *static_cast<int32_t*>(p->value_out) = surface->width();
+        break;
+      case DVR_SURFACE_PARAMETER_SURFACE_HEIGHT_OUT:
+        *static_cast<int32_t*>(p->value_out) = surface->height();
+        break;
+      case DVR_SURFACE_PARAMETER_INTER_LENS_METERS_OUT:
+        *static_cast<float*>(p->value_out) = metrics->inter_lens_distance_m;
+        break;
+      case DVR_SURFACE_PARAMETER_LEFT_FOV_LRBT_OUT:
+        for (int i = 0; i < 4; ++i) {
+          float* float_values_out = static_cast<float*>(p->value_out);
+          float_values_out[i] = metrics->left_fov_lrbt[i];
+        }
+        break;
+      case DVR_SURFACE_PARAMETER_RIGHT_FOV_LRBT_OUT:
+        for (int i = 0; i < 4; ++i) {
+          float* float_values_out = static_cast<float*>(p->value_out);
+          float_values_out[i] = metrics->right_fov_lrbt[i];
+        }
+        break;
+      case DVR_SURFACE_PARAMETER_VSYNC_PERIOD_OUT:
+        *static_cast<uint64_t*>(p->value_out) = metrics->vsync_period_ns;
+        break;
+      default:
+        break;
+    }
+  }
+
+  return surface;
+}
+
+extern "C" int dvrGetNativeDisplayDimensions(int* native_width,
+                                             int* native_height) {
+  int error = 0;
+  auto client = android::dvr::DisplayClient::Create(&error);
+  if (!client) {
+    ALOGE("Failed to create display client!");
+    return error;
+  }
+
+  android::dvr::SystemDisplayMetrics metrics;
+  const int ret = client->GetDisplayMetrics(&metrics);
+
+  if (ret != 0) {
+    ALOGE("Failed to get display metrics!");
+    return ret;
+  }
+
+  *native_width = static_cast<int>(metrics.display_native_width);
+  *native_height = static_cast<int>(metrics.display_native_height);
+  return 0;
+}
+
+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);
+
+  DvrGraphicsContext(const DvrGraphicsContext&) = delete;
+  void operator=(const DvrGraphicsContext&) = delete;
+};
+
+DvrGraphicsContext::DvrGraphicsContext()
+    : graphics_api(DVR_GRAPHICS_API_GLES),
+      gl{},
+      vk{},
+      current_buffer(nullptr),
+      buffer_already_posted(false),
+      surface_metadata(nullptr) {
+  gl.egl_display = EGL_NO_DISPLAY;
+  gl.egl_context = EGL_NO_CONTEXT;
+  gl.owns_egl_context = true;
+  gl.texture_target_type = GL_TEXTURE_2D;
+
+  ANativeWindow::setSwapInterval = SetSwapInterval;
+  ANativeWindow::dequeueBuffer = DequeueBuffer;
+  ANativeWindow::cancelBuffer = CancelBuffer;
+  ANativeWindow::queueBuffer = QueueBuffer;
+  ANativeWindow::query = Query;
+  ANativeWindow::perform = Perform;
+
+  ANativeWindow::dequeueBuffer_DEPRECATED = DequeueBuffer_DEPRECATED;
+  ANativeWindow::cancelBuffer_DEPRECATED = CancelBuffer_DEPRECATED;
+  ANativeWindow::lockBuffer_DEPRECATED = LockBuffer_DEPRECATED;
+  ANativeWindow::queueBuffer_DEPRECATED = QueueBuffer_DEPRECATED;
+}
+
+DvrGraphicsContext::~DvrGraphicsContext() {
+  if (graphics_api == DVR_GRAPHICS_API_GLES) {
+    glDeleteTextures(gl.texture_count, gl.texture_id);
+    if (gl.owns_egl_context)
+      DestroyEglContext(gl.egl_display, &gl.egl_context);
+  } else if (graphics_api == DVR_GRAPHICS_API_VULKAN) {
+    if (vk.swapchain != VK_NULL_HANDLE) {
+      for (auto view : vk.swapchain_image_views) {
+        vkDestroyImageView(vk.device, view, vk.allocation_callbacks);
+      }
+      vkDestroySwapchainKHR(vk.device, vk.swapchain, vk.allocation_callbacks);
+      vkDestroySurfaceKHR(vk.instance, vk.surface, vk.allocation_callbacks);
+      delete vk.window;
+    }
+  }
+}
+
+int dvrGraphicsContextCreate(struct DvrSurfaceParameter* parameters,
+                             DvrGraphicsContext** return_graphics_context) {
+  std::unique_ptr<DvrGraphicsContext> context(new DvrGraphicsContext);
+
+  // See whether we're using GL or Vulkan
+  for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+    switch (p->key) {
+      case DVR_SURFACE_PARAMETER_GRAPHICS_API_IN:
+        context->graphics_api = p->value;
+        break;
+    }
+  }
+
+  if (context->graphics_api == DVR_GRAPHICS_API_GLES) {
+    context->gl.egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (context->gl.egl_display == EGL_NO_DISPLAY) {
+      ALOGE("eglGetDisplay failed");
+      return -ENXIO;
+    }
+
+    // See if we should create a GL context
+    for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+      switch (p->key) {
+        case DVR_SURFACE_PARAMETER_CREATE_GL_CONTEXT_IN:
+          context->gl.owns_egl_context = p->value != 0;
+          break;
+      }
+    }
+
+    if (context->gl.owns_egl_context) {
+      int ret = CreateEglContext(context->gl.egl_display, parameters,
+                                 &context->gl.egl_context);
+      if (ret < 0)
+        return ret;
+    } else {
+      context->gl.egl_context = eglGetCurrentContext();
+    }
+
+    int ret = InitGl(context->gl.owns_egl_context);
+    if (ret < 0)
+      return ret;
+  } else if (context->graphics_api == DVR_GRAPHICS_API_VULKAN) {
+    for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+      switch (p->key) {
+        case DVR_SURFACE_PARAMETER_VK_INSTANCE_IN:
+          context->vk.instance = reinterpret_cast<VkInstance>(p->value);
+          break;
+        case DVR_SURFACE_PARAMETER_VK_PHYSICAL_DEVICE_IN:
+          context->vk.physical_device =
+              reinterpret_cast<VkPhysicalDevice>(p->value);
+          break;
+        case DVR_SURFACE_PARAMETER_VK_DEVICE_IN:
+          context->vk.device = reinterpret_cast<VkDevice>(p->value);
+          break;
+        case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_IN:
+          context->vk.present_queue = reinterpret_cast<VkQueue>(p->value);
+          break;
+        case DVR_SURFACE_PARAMETER_VK_PRESENT_QUEUE_FAMILY_IN:
+          context->vk.present_queue_family = static_cast<uint32_t>(p->value);
+          break;
+      }
+    }
+  } else {
+    ALOGE("Error: invalid graphics API type");
+    return -EINVAL;
+  }
+
+  context->display_surface =
+      CreateDisplaySurfaceClient(parameters, &context->display_metrics);
+  if (!context->display_surface) {
+    ALOGE("Error: failed to create display surface client");
+    return -ECOMM;
+  }
+
+  context->buffer_queue.reset(new android::dvr::NativeBufferQueue(
+      context->gl.egl_display, context->display_surface, kDefaultBufferCount));
+
+  // The way the call sequence works we need 1 more than the buffer queue
+  // capacity to store data for all pending frames
+  context->frame_history.Reset(context->buffer_queue->GetQueueCapacity() + 1);
+
+  context->vsync_client = android::dvr::VSyncClient::Create();
+  if (!context->vsync_client) {
+    ALOGE("Error: failed to create vsync client");
+    return -ECOMM;
+  }
+
+  context->timerfd.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
+  if (!context->timerfd) {
+    ALOGE("Error: timerfd_create failed because: %s", strerror(errno));
+    return -EPERM;
+  }
+
+  context->surface_metadata = context->display_surface->GetMetadataBufferPtr();
+  if (!context->surface_metadata) {
+    ALOGE("Error: surface metadata allocation failed");
+    return -ENOMEM;
+  }
+
+  ALOGI("buffer: %d x %d\n", context->display_surface->width(),
+        context->display_surface->height());
+
+  if (context->graphics_api == DVR_GRAPHICS_API_GLES) {
+    context->gl.texture_count = (context->display_surface->flags() &
+                                 DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2)
+                                    ? 2
+                                    : 1;
+
+    // Create the GL textures.
+    glGenTextures(context->gl.texture_count, context->gl.texture_id);
+
+    // We must make sure that we have at least one buffer allocated at this time
+    // so that anyone who tries to bind an FBO to context->texture_id
+    // will not get an incomplete buffer.
+    context->current_buffer = context->buffer_queue->Dequeue();
+    LOG_ALWAYS_FATAL_IF(context->gl.texture_count !=
+                        context->current_buffer->buffer()->slice_count());
+    for (int i = 0; i < context->gl.texture_count; ++i) {
+      glBindTexture(context->gl.texture_target_type, context->gl.texture_id[i]);
+      glEGLImageTargetTexture2DOES(context->gl.texture_target_type,
+                                   context->current_buffer->image_khr(i));
+    }
+    glBindTexture(context->gl.texture_target_type, 0);
+    CHECK_GL();
+
+    bool is_late_latch = false;
+
+    // Pass back the texture target type and id.
+    for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+      switch (p->key) {
+        case DVR_SURFACE_PARAMETER_ENABLE_LATE_LATCH_IN:
+          is_late_latch = !!p->value;
+          break;
+        case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_TYPE_OUT:
+          *static_cast<GLenum*>(p->value_out) = context->gl.texture_target_type;
+          break;
+        case DVR_SURFACE_PARAMETER_SURFACE_TEXTURE_TARGET_ID_OUT:
+          for (int i = 0; i < context->gl.texture_count; ++i) {
+            *(static_cast<GLuint*>(p->value_out) + i) =
+                context->gl.texture_id[i];
+          }
+          break;
+      }
+    }
+
+    // Initialize late latch.
+    if (is_late_latch) {
+      LocalHandle fd;
+      int ret = context->display_surface->GetMetadataBufferFd(&fd);
+      if (ret == 0) {
+        context->late_latch.reset(
+            new android::dvr::LateLatch(true, std::move(fd)));
+      } else {
+        ALOGE("Error: failed to get surface metadata buffer fd for late latch");
+      }
+    }
+  } else if (context->graphics_api == DVR_GRAPHICS_API_VULKAN) {
+    VkResult result = VK_SUCCESS;
+    // Create a VkSurfaceKHR from the ANativeWindow.
+    VkAndroidSurfaceCreateInfoKHR android_surface_ci = {};
+    android_surface_ci.sType =
+        VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+    android_surface_ci.window = context.get();
+    result = vkCreateAndroidSurfaceKHR(
+        context->vk.instance, &android_surface_ci,
+        context->vk.allocation_callbacks, &context->vk.surface);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    VkBool32 surface_supports_present = VK_FALSE;
+    result = vkGetPhysicalDeviceSurfaceSupportKHR(
+        context->vk.physical_device, context->vk.present_queue_family,
+        context->vk.surface, &surface_supports_present);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    if (!surface_supports_present) {
+      ALOGE("Error: provided queue family (%u) does not support presentation",
+            context->vk.present_queue_family);
+      return -EPERM;
+    }
+    VkSurfaceCapabilitiesKHR surface_capabilities = {};
+    result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+        context->vk.physical_device, context->vk.surface,
+        &surface_capabilities);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    // Determine the swapchain image format.
+    uint32_t device_surface_format_count = 0;
+    result = vkGetPhysicalDeviceSurfaceFormatsKHR(
+        context->vk.physical_device, context->vk.surface,
+        &device_surface_format_count, nullptr);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    std::vector<VkSurfaceFormatKHR> device_surface_formats(
+        device_surface_format_count);
+    result = vkGetPhysicalDeviceSurfaceFormatsKHR(
+        context->vk.physical_device, context->vk.surface,
+        &device_surface_format_count, device_surface_formats.data());
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(device_surface_format_count == 0U);
+    LOG_ALWAYS_FATAL_IF(device_surface_formats[0].format ==
+                        VK_FORMAT_UNDEFINED);
+    VkSurfaceFormatKHR present_surface_format = device_surface_formats[0];
+    // Determine the swapchain present mode.
+    // TODO(cort): query device_present_modes to make sure MAILBOX is supported.
+    // But according to libvulkan, it is.
+    uint32_t device_present_mode_count = 0;
+    result = vkGetPhysicalDeviceSurfacePresentModesKHR(
+        context->vk.physical_device, context->vk.surface,
+        &device_present_mode_count, nullptr);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    std::vector<VkPresentModeKHR> device_present_modes(
+        device_present_mode_count);
+    result = vkGetPhysicalDeviceSurfacePresentModesKHR(
+        context->vk.physical_device, context->vk.surface,
+        &device_present_mode_count, device_present_modes.data());
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
+    // Extract presentation surface extents, image count, transform, usages,
+    // etc.
+    LOG_ALWAYS_FATAL_IF(
+        static_cast<int>(surface_capabilities.currentExtent.width) == -1 ||
+        static_cast<int>(surface_capabilities.currentExtent.height) == -1);
+    VkExtent2D swapchain_extent = surface_capabilities.currentExtent;
+
+    uint32_t desired_image_count = surface_capabilities.minImageCount;
+    if (surface_capabilities.maxImageCount > 0 &&
+        desired_image_count > surface_capabilities.maxImageCount) {
+      desired_image_count = surface_capabilities.maxImageCount;
+    }
+    VkSurfaceTransformFlagBitsKHR surface_transform =
+        surface_capabilities.currentTransform;
+    VkImageUsageFlags image_usage_flags =
+        surface_capabilities.supportedUsageFlags;
+    LOG_ALWAYS_FATAL_IF(surface_capabilities.supportedCompositeAlpha ==
+                        static_cast<VkFlags>(0));
+    VkCompositeAlphaFlagBitsKHR composite_alpha =
+        VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+    if (!(surface_capabilities.supportedCompositeAlpha &
+          VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) {
+      composite_alpha = VkCompositeAlphaFlagBitsKHR(
+          static_cast<int>(surface_capabilities.supportedCompositeAlpha) &
+          -static_cast<int>(surface_capabilities.supportedCompositeAlpha));
+    }
+    // Create VkSwapchainKHR
+    VkSwapchainCreateInfoKHR swapchain_ci = {};
+    swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+    swapchain_ci.pNext = nullptr;
+    swapchain_ci.surface = context->vk.surface;
+    swapchain_ci.minImageCount = desired_image_count;
+    swapchain_ci.imageFormat = present_surface_format.format;
+    swapchain_ci.imageColorSpace = present_surface_format.colorSpace;
+    swapchain_ci.imageExtent.width = swapchain_extent.width;
+    swapchain_ci.imageExtent.height = swapchain_extent.height;
+    swapchain_ci.imageUsage = image_usage_flags;
+    swapchain_ci.preTransform = surface_transform;
+    swapchain_ci.compositeAlpha = composite_alpha;
+    swapchain_ci.imageArrayLayers = 1;
+    swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+    swapchain_ci.queueFamilyIndexCount = 0;
+    swapchain_ci.pQueueFamilyIndices = nullptr;
+    swapchain_ci.presentMode = present_mode;
+    swapchain_ci.clipped = VK_TRUE;
+    swapchain_ci.oldSwapchain = VK_NULL_HANDLE;
+    result = vkCreateSwapchainKHR(context->vk.device, &swapchain_ci,
+                                  context->vk.allocation_callbacks,
+                                  &context->vk.swapchain);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    // Create swapchain image views
+    uint32_t image_count = 0;
+    result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
+                                     &image_count, nullptr);
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    LOG_ALWAYS_FATAL_IF(image_count == 0U);
+    context->vk.swapchain_images.resize(image_count);
+    result = vkGetSwapchainImagesKHR(context->vk.device, context->vk.swapchain,
+                                     &image_count,
+                                     context->vk.swapchain_images.data());
+    LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    context->vk.swapchain_image_views.resize(image_count);
+    VkImageViewCreateInfo image_view_ci = {};
+    image_view_ci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    image_view_ci.pNext = nullptr;
+    image_view_ci.flags = 0;
+    image_view_ci.format = swapchain_ci.imageFormat;
+    image_view_ci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_ci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_ci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_ci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+    image_view_ci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    image_view_ci.subresourceRange.baseMipLevel = 0;
+    image_view_ci.subresourceRange.levelCount = 1;
+    image_view_ci.subresourceRange.baseArrayLayer = 0;
+    image_view_ci.subresourceRange.layerCount = 1;
+    image_view_ci.viewType = VK_IMAGE_VIEW_TYPE_2D;
+    image_view_ci.image = VK_NULL_HANDLE;  // filled in below
+    for (uint32_t i = 0; i < image_count; ++i) {
+      image_view_ci.image = context->vk.swapchain_images[i];
+      result = vkCreateImageView(context->vk.device, &image_view_ci,
+                                 context->vk.allocation_callbacks,
+                                 &context->vk.swapchain_image_views[i]);
+      LOG_ALWAYS_FATAL_IF(result != VK_SUCCESS);
+    }
+    // Fill in any requested output parameters.
+    for (auto p = parameters; p && p->key != DVR_SURFACE_PARAMETER_NONE; ++p) {
+      switch (p->key) {
+        case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_COUNT_OUT:
+          *static_cast<uint32_t*>(p->value_out) = image_count;
+          break;
+        case DVR_SURFACE_PARAMETER_VK_SWAPCHAIN_IMAGE_FORMAT_OUT:
+          *static_cast<VkFormat*>(p->value_out) = swapchain_ci.imageFormat;
+          break;
+      }
+    }
+  }
+
+  *return_graphics_context = context.release();
+  return 0;
+}
+
+void dvrGraphicsContextDestroy(DvrGraphicsContext* graphics_context) {
+  delete graphics_context;
+}
+
+// ANativeWindow function implementations. These should only be used
+// by the Vulkan path.
+int DvrGraphicsContext::Post(android::dvr::NativeBufferProducer* buffer,
+                             int fence_fd) {
+  LOG_ALWAYS_FATAL_IF(graphics_api != DVR_GRAPHICS_API_VULKAN);
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+  ALOGI_IF(TRACE, "DvrGraphicsContext::Post: buffer_id=%d, fence_fd=%d",
+           buffer->buffer()->id(), fence_fd);
+  ALOGW_IF(!display_surface->visible(),
+           "DvrGraphicsContext::Post: Posting buffer on invisible surface!!!");
+  // The NativeBufferProducer closes the fence fd, so dup it for tracking in the
+  // frame history.
+  frame_history.OnFrameSubmit(LocalHandle::AsDuplicate(fence_fd));
+  int result = buffer->Post(fence_fd, 0);
+  return result;
+}
+
+int DvrGraphicsContext::SetSwapInterval(ANativeWindow* window, int interval) {
+  ALOGI_IF(TRACE, "SetSwapInterval: window=%p interval=%d", window, interval);
+  DvrGraphicsContext* self = getSelf(window);
+  (void)self;
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::DequeueBuffer(ANativeWindow* window,
+                                      ANativeWindowBuffer** buffer,
+                                      int* fence_fd) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+
+  DvrGraphicsContext* self = getSelf(window);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  if (!self->current_buffer) {
+    self->current_buffer = self->buffer_queue.get()->Dequeue();
+  }
+  ATRACE_ASYNC_BEGIN("BufferDraw", self->current_buffer->buffer()->id());
+  *fence_fd = self->current_buffer->ClaimReleaseFence().Release();
+  *buffer = self->current_buffer;
+
+  ALOGI_IF(TRACE, "DvrGraphicsContext::DequeueBuffer: fence_fd=%d", *fence_fd);
+  return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::QueueBuffer(ANativeWindow* window,
+                                    ANativeWindowBuffer* buffer, int fence_fd) {
+  ATRACE_NAME("NativeWindow::QueueBuffer");
+  ALOGI_IF(TRACE, "NativeWindow::QueueBuffer: fence_fd=%d", fence_fd);
+
+  DvrGraphicsContext* self = getSelf(window);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  android::dvr::NativeBufferProducer* native_buffer =
+      static_cast<android::dvr::NativeBufferProducer*>(buffer);
+  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+  bool do_post = true;
+  if (self->buffer_already_posted) {
+    // Check that the buffer is the one we expect, but handle it if this happens
+    // in production by allowing this buffer to post on top of the previous one.
+    LOG_FATAL_IF(native_buffer != self->current_buffer);
+    if (native_buffer == self->current_buffer) {
+      do_post = false;
+      if (fence_fd >= 0)
+        close(fence_fd);
+    }
+  }
+  if (do_post) {
+    ATRACE_ASYNC_BEGIN("BufferPost", native_buffer->buffer()->id());
+    self->Post(native_buffer, fence_fd);
+  }
+  self->buffer_already_posted = false;
+  self->current_buffer = nullptr;
+
+  return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::CancelBuffer(ANativeWindow* window,
+                                     ANativeWindowBuffer* buffer,
+                                     int fence_fd) {
+  ATRACE_NAME("DvrGraphicsContext::CancelBuffer");
+  ALOGI_IF(TRACE, "DvrGraphicsContext::CancelBuffer: fence_fd: %d", fence_fd);
+
+  DvrGraphicsContext* self = getSelf(window);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  android::dvr::NativeBufferProducer* native_buffer =
+      static_cast<android::dvr::NativeBufferProducer*>(buffer);
+  ATRACE_ASYNC_END("BufferDraw", native_buffer->buffer()->id());
+  ATRACE_INT("CancelBuffer", native_buffer->buffer()->id());
+  bool do_enqueue = true;
+  if (self->buffer_already_posted) {
+    // Check that the buffer is the one we expect, but handle it if this happens
+    // in production by returning this buffer to the buffer queue.
+    LOG_FATAL_IF(native_buffer != self->current_buffer);
+    if (native_buffer == self->current_buffer) {
+      do_enqueue = false;
+    }
+  }
+  if (do_enqueue) {
+    self->buffer_queue.get()->Enqueue(native_buffer);
+  }
+  if (fence_fd >= 0)
+    close(fence_fd);
+  self->buffer_already_posted = false;
+  self->current_buffer = nullptr;
+
+  return android::NO_ERROR;
+}
+
+int DvrGraphicsContext::Query(const ANativeWindow* window, int what,
+                              int* value) {
+  DvrGraphicsContext* self = getSelf(const_cast<ANativeWindow*>(window));
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  switch (what) {
+    case NATIVE_WINDOW_WIDTH:
+      *value = self->display_surface->width();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_HEIGHT:
+      *value = self->display_surface->height();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_FORMAT:
+      *value = self->display_surface->format();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+      *value = 1;
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_CONCRETE_TYPE:
+      *value = NATIVE_WINDOW_SURFACE;
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+      *value = 1;
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_DEFAULT_WIDTH:
+      *value = self->display_surface->width();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_DEFAULT_HEIGHT:
+      *value = self->display_surface->height();
+      return android::NO_ERROR;
+    case NATIVE_WINDOW_TRANSFORM_HINT:
+      *value = 0;
+      return android::NO_ERROR;
+  }
+
+  *value = 0;
+  return android::BAD_VALUE;
+}
+
+int DvrGraphicsContext::Perform(ANativeWindow* window, int operation, ...) {
+  DvrGraphicsContext* self = getSelf(window);
+  LOG_ALWAYS_FATAL_IF(self->graphics_api != DVR_GRAPHICS_API_VULKAN);
+  std::lock_guard<std::mutex> autolock(self->lock_);
+
+  va_list args;
+  va_start(args, operation);
+
+  // TODO(eieio): The following operations are not used at this time. They are
+  // included here to help document which operations may be useful and what
+  // parameters they take.
+  switch (operation) {
+    case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: {
+      int w = va_arg(args, int);
+      int h = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: w=%d h=%d", w, h);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+      int format = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_FORMAT: format=%d", format);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: {
+      int transform = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: transform=%d",
+               transform);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_SET_USAGE: {
+      int usage = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_USAGE: usage=%d", usage);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_CONNECT:
+    case NATIVE_WINDOW_DISCONNECT:
+    case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+    case NATIVE_WINDOW_API_CONNECT:
+    case NATIVE_WINDOW_API_DISCONNECT:
+      // TODO(eieio): we should implement these
+      return android::NO_ERROR;
+
+    case NATIVE_WINDOW_SET_BUFFER_COUNT: {
+      int buffer_count = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFER_COUNT: bufferCount=%d",
+               buffer_count);
+      return android::NO_ERROR;
+    }
+    case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+      android_dataspace_t data_space =
+          static_cast<android_dataspace_t>(va_arg(args, int));
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_BUFFERS_DATASPACE: dataSpace=%d",
+               data_space);
+      return android::NO_ERROR;
+    }
+    case NATIVE_WINDOW_SET_SCALING_MODE: {
+      int mode = va_arg(args, int);
+      ALOGD_IF(TRACE, "NATIVE_WINDOW_SET_SCALING_MODE: mode=%d", mode);
+      return android::NO_ERROR;
+    }
+
+    case NATIVE_WINDOW_LOCK:
+    case NATIVE_WINDOW_UNLOCK_AND_POST:
+    case NATIVE_WINDOW_SET_CROP:
+    case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+      return android::INVALID_OPERATION;
+  }
+
+  return android::NAME_NOT_FOUND;
+}
+
+int DvrGraphicsContext::DequeueBuffer_DEPRECATED(ANativeWindow* window,
+                                                 ANativeWindowBuffer** buffer) {
+  int fence_fd = -1;
+  int ret = DequeueBuffer(window, buffer, &fence_fd);
+
+  // wait for fence
+  if (ret == android::NO_ERROR && fence_fd != -1)
+    close(fence_fd);
+
+  return ret;
+}
+
+int DvrGraphicsContext::CancelBuffer_DEPRECATED(ANativeWindow* window,
+                                                ANativeWindowBuffer* buffer) {
+  return CancelBuffer(window, buffer, -1);
+}
+
+int DvrGraphicsContext::QueueBuffer_DEPRECATED(ANativeWindow* window,
+                                               ANativeWindowBuffer* buffer) {
+  return QueueBuffer(window, buffer, -1);
+}
+
+int DvrGraphicsContext::LockBuffer_DEPRECATED(ANativeWindow* /*window*/,
+                                              ANativeWindowBuffer* /*buffer*/) {
+  return android::NO_ERROR;
+}
+// End ANativeWindow implementation
+
+int dvrSetEdsPose(DvrGraphicsContext* graphics_context,
+                  float32x4_t render_pose_orientation,
+                  float32x4_t render_pose_translation) {
+  ATRACE_NAME("dvrSetEdsPose");
+  if (!graphics_context->current_buffer) {
+    ALOGE("dvrBeginRenderFrame must be called before dvrSetEdsPose");
+    return -EPERM;
+  }
+
+  // When late-latching is enabled, the pose buffer is written by the GPU, so
+  // we don't touch it here.
+  float32x4_t is_late_latch = DVR_POSE_LATE_LATCH;
+  if (render_pose_orientation[0] != is_late_latch[0]) {
+    volatile android::dvr::DisplaySurfaceMetadata* data =
+        graphics_context->surface_metadata;
+    uint32_t buffer_index =
+        graphics_context->current_buffer->surface_buffer_index();
+    ALOGE_IF(TRACE, "write pose index %d %f %f", buffer_index,
+             render_pose_orientation[0], render_pose_orientation[1]);
+    data->orientation[buffer_index] = render_pose_orientation;
+    data->translation[buffer_index] = render_pose_translation;
+  }
+
+  return 0;
+}
+
+int dvrBeginRenderFrameEds(DvrGraphicsContext* graphics_context,
+                           float32x4_t render_pose_orientation,
+                           float32x4_t render_pose_translation) {
+  ATRACE_NAME("dvrBeginRenderFrameEds");
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
+  CHECK_GL();
+  // Grab a buffer from the queue and set its pose.
+  if (!graphics_context->current_buffer) {
+    graphics_context->current_buffer =
+        graphics_context->buffer_queue->Dequeue();
+  }
+
+  int ret = dvrSetEdsPose(graphics_context, render_pose_orientation,
+                          render_pose_translation);
+  if (ret < 0)
+    return ret;
+
+  ATRACE_ASYNC_BEGIN("BufferDraw",
+                     graphics_context->current_buffer->buffer()->id());
+
+  {
+    ATRACE_NAME("glEGLImageTargetTexture2DOES");
+    // Bind the texture to the latest buffer in the queue.
+    for (int i = 0; i < graphics_context->gl.texture_count; ++i) {
+      glBindTexture(graphics_context->gl.texture_target_type,
+                    graphics_context->gl.texture_id[i]);
+      glEGLImageTargetTexture2DOES(
+          graphics_context->gl.texture_target_type,
+          graphics_context->current_buffer->image_khr(i));
+    }
+    glBindTexture(graphics_context->gl.texture_target_type, 0);
+  }
+  CHECK_GL();
+  return 0;
+}
+int dvrBeginRenderFrameEdsVk(DvrGraphicsContext* graphics_context,
+                             float32x4_t render_pose_orientation,
+                             float32x4_t render_pose_translation,
+                             VkSemaphore acquire_semaphore,
+                             VkFence acquire_fence,
+                             uint32_t* swapchain_image_index,
+                             VkImageView* swapchain_image_view) {
+  ATRACE_NAME("dvrBeginRenderFrameEds");
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api !=
+                      DVR_GRAPHICS_API_VULKAN);
+
+  // Acquire a swapchain image. This calls Dequeue() internally.
+  VkResult result = vkAcquireNextImageKHR(
+      graphics_context->vk.device, graphics_context->vk.swapchain, UINT64_MAX,
+      acquire_semaphore, acquire_fence, swapchain_image_index);
+  if (result != VK_SUCCESS)
+    return -EINVAL;
+
+  // Set the pose pose.
+  int ret = dvrSetEdsPose(graphics_context, render_pose_orientation,
+                          render_pose_translation);
+  if (ret < 0)
+    return ret;
+  *swapchain_image_view =
+      graphics_context->vk.swapchain_image_views[*swapchain_image_index];
+  return 0;
+}
+
+int dvrBeginRenderFrame(DvrGraphicsContext* graphics_context) {
+  return dvrBeginRenderFrameEds(graphics_context, DVR_POSE_NO_EDS,
+                                DVR_POSE_NO_EDS);
+}
+int dvrBeginRenderFrameVk(DvrGraphicsContext* graphics_context,
+                          VkSemaphore acquire_semaphore, VkFence acquire_fence,
+                          uint32_t* swapchain_image_index,
+                          VkImageView* swapchain_image_view) {
+  return dvrBeginRenderFrameEdsVk(
+      graphics_context, DVR_POSE_NO_EDS, DVR_POSE_NO_EDS, acquire_semaphore,
+      acquire_fence, swapchain_image_index, swapchain_image_view);
+}
+
+int dvrBeginRenderFrameLateLatch(DvrGraphicsContext* graphics_context,
+                                 uint32_t /*flags*/,
+                                 uint32_t target_vsync_count, int num_views,
+                                 const float** projection_matrices,
+                                 const float** eye_from_head_matrices,
+                                 const float** pose_offset_matrices,
+                                 uint32_t* out_late_latch_buffer_id) {
+  if (!graphics_context->late_latch) {
+    return -EPERM;
+  }
+  if (num_views > DVR_GRAPHICS_SURFACE_MAX_VIEWS) {
+    ALOGE("dvrBeginRenderFrameLateLatch called with too many views.");
+    return -EINVAL;
+  }
+  dvrBeginRenderFrameEds(graphics_context, DVR_POSE_LATE_LATCH,
+                         DVR_POSE_LATE_LATCH);
+  auto& ll = graphics_context->late_latch;
+  // TODO(jbates) Need to change this shader so that it dumps the single
+  // captured pose for both eyes into the display surface metadata buffer at
+  // the right index.
+  android::dvr::LateLatchInput input;
+  memset(&input, 0, sizeof(input));
+  for (int i = 0; i < num_views; ++i) {
+    memcpy(input.proj_mat + i, *(projection_matrices + i), 16 * sizeof(float));
+    memcpy(input.eye_from_head_mat + i, *(eye_from_head_matrices + i),
+           16 * sizeof(float));
+    memcpy(input.pose_offset + i, *(pose_offset_matrices + i),
+           16 * sizeof(float));
+  }
+  input.pose_index =
+      target_vsync_count & android::dvr::kPoseAsyncBufferIndexMask;
+  input.render_pose_index =
+      graphics_context->current_buffer->surface_buffer_index();
+  ll->AddLateLatch(input);
+  *out_late_latch_buffer_id = ll->output_buffer_id();
+  return 0;
+}
+
+extern "C" int dvrGraphicsWaitNextFrame(
+    DvrGraphicsContext* graphics_context, int64_t start_delay_ns,
+    DvrFrameSchedule* out_next_frame_schedule) {
+  start_delay_ns = std::max(start_delay_ns, static_cast<int64_t>(0));
+
+  // We only do one-shot timers:
+  int64_t wake_time_ns = 0;
+
+  uint32_t current_frame_vsync;
+  int64_t current_frame_scheduled_finish_ns;
+  int64_t vsync_period_ns;
+
+  int fetch_schedule_result = graphics_context->vsync_client->GetSchedInfo(
+      &vsync_period_ns, &current_frame_scheduled_finish_ns,
+      &current_frame_vsync);
+  if (fetch_schedule_result == 0) {
+    wake_time_ns = current_frame_scheduled_finish_ns + start_delay_ns;
+    // If the last wakeup time is still in the future, use it instead to avoid
+    // major schedule jumps when applications call WaitNextFrame with
+    // aggressive offsets.
+    int64_t now = android::dvr::GetSystemClockNs();
+    if (android::dvr::TimestampGT(wake_time_ns - vsync_period_ns, now)) {
+      wake_time_ns -= vsync_period_ns;
+      --current_frame_vsync;
+    }
+    // If the next wakeup time is in the past, add a vsync period to keep the
+    // application on schedule.
+    if (android::dvr::TimestampLT(wake_time_ns, now)) {
+      wake_time_ns += vsync_period_ns;
+      ++current_frame_vsync;
+    }
+  } else {
+    ALOGE("Error getting frame schedule because: %s",
+          strerror(-fetch_schedule_result));
+    // Sleep for a vsync period to avoid cascading failure.
+    wake_time_ns = android::dvr::GetSystemClockNs() +
+                   graphics_context->display_metrics.vsync_period_ns;
+  }
+
+  // Adjust nsec to [0..999,999,999].
+  struct itimerspec wake_time;
+  wake_time.it_interval.tv_sec = 0;
+  wake_time.it_interval.tv_nsec = 0;
+  wake_time.it_value = android::dvr::NsToTimespec(wake_time_ns);
+  bool sleep_result =
+      timerfd_settime(graphics_context->timerfd.Get(), TFD_TIMER_ABSTIME,
+                      &wake_time, nullptr) == 0;
+  if (sleep_result) {
+    ATRACE_NAME("sleep");
+    uint64_t expirations = 0;
+    sleep_result = read(graphics_context->timerfd.Get(), &expirations,
+                        sizeof(uint64_t)) == sizeof(uint64_t);
+    if (!sleep_result) {
+      ALOGE("Error: timerfd read failed");
+    }
+  } else {
+    ALOGE("Error: timerfd_settime failed because: %s", strerror(errno));
+  }
+
+  auto& frame_history = graphics_context->frame_history;
+  frame_history.CheckForFinishedFrames();
+  if (fetch_schedule_result == 0) {
+    uint32_t next_frame_vsync =
+        current_frame_vsync +
+        frame_history.PredictNextFrameVsyncInterval(vsync_period_ns);
+    int64_t next_frame_scheduled_finish =
+        (wake_time_ns - start_delay_ns) + vsync_period_ns;
+    frame_history.OnFrameStart(next_frame_vsync, next_frame_scheduled_finish);
+    if (out_next_frame_schedule) {
+      out_next_frame_schedule->vsync_count = next_frame_vsync;
+      out_next_frame_schedule->scheduled_frame_finish_ns =
+          next_frame_scheduled_finish;
+    }
+  } else {
+    frame_history.OnFrameStart(UINT32_MAX, -1);
+  }
+
+  return (fetch_schedule_result == 0 && sleep_result) ? 0 : -1;
+}
+
+extern "C" void dvrGraphicsPostEarly(DvrGraphicsContext* graphics_context) {
+  ATRACE_NAME("dvrGraphicsPostEarly");
+  ALOGI_IF(TRACE, "dvrGraphicsPostEarly");
+
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
+
+  // Note that this function can be called before or after
+  // dvrBeginRenderFrame.
+  if (!graphics_context->buffer_already_posted) {
+    graphics_context->buffer_already_posted = true;
+
+    if (!graphics_context->current_buffer) {
+      graphics_context->current_buffer =
+          graphics_context->buffer_queue->Dequeue();
+    }
+
+    auto buffer = graphics_context->current_buffer->buffer().get();
+    ATRACE_ASYNC_BEGIN("BufferPost", buffer->id());
+    int result = buffer->Post<uint64_t>(LocalHandle(), 0);
+    if (result < 0)
+      ALOGE("Buffer post failed: %d (%s)", result, strerror(-result));
+  }
+}
+
+int dvrPresent(DvrGraphicsContext* graphics_context) {
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api != DVR_GRAPHICS_API_GLES);
+
+  std::array<char, 128> buf;
+  snprintf(buf.data(), buf.size(), "dvrPresent|vsync=%d|",
+           graphics_context->frame_history.GetCurrentFrameVsync());
+  ATRACE_NAME(buf.data());
+
+  if (!graphics_context->current_buffer) {
+    ALOGE("Error: dvrPresent called without dvrBeginRenderFrame");
+    return -EPERM;
+  }
+
+  LocalHandle fence_fd =
+      android::dvr::CreateGLSyncAndFlush(graphics_context->gl.egl_display);
+
+  ALOGI_IF(TRACE, "PostBuffer: buffer_id=%d, fence_fd=%d",
+           graphics_context->current_buffer->buffer()->id(), fence_fd.Get());
+  ALOGW_IF(!graphics_context->display_surface->visible(),
+           "PostBuffer: Posting buffer on invisible surface!!!");
+
+  auto buffer = graphics_context->current_buffer->buffer().get();
+  ATRACE_ASYNC_END("BufferDraw", buffer->id());
+  if (!graphics_context->buffer_already_posted) {
+    ATRACE_ASYNC_BEGIN("BufferPost", buffer->id());
+    int result = buffer->Post<uint64_t>(fence_fd, 0);
+    if (result < 0)
+      ALOGE("Buffer post failed: %d (%s)", result, strerror(-result));
+  }
+
+  graphics_context->frame_history.OnFrameSubmit(std::move(fence_fd));
+  graphics_context->buffer_already_posted = false;
+  graphics_context->current_buffer = nullptr;
+  return 0;
+}
+
+int dvrPresentVk(DvrGraphicsContext* graphics_context,
+                 VkSemaphore submit_semaphore, uint32_t swapchain_image_index) {
+  LOG_ALWAYS_FATAL_IF(graphics_context->graphics_api !=
+                      DVR_GRAPHICS_API_VULKAN);
+
+  std::array<char, 128> buf;
+  snprintf(buf.data(), buf.size(), "dvrPresent|vsync=%d|",
+           graphics_context->frame_history.GetCurrentFrameVsync());
+  ATRACE_NAME(buf.data());
+
+  if (!graphics_context->current_buffer) {
+    ALOGE("Error: dvrPresentVk called without dvrBeginRenderFrameVk");
+    return -EPERM;
+  }
+
+  // Present the specified image. Internally, this gets a fence from the
+  // Vulkan driver and passes it to DvrGraphicsContext::Post(),
+  // which in turn passes it to buffer->Post() and adds it to frame_history.
+  VkPresentInfoKHR present_info = {};
+  present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+  present_info.swapchainCount = 1;
+  present_info.pSwapchains = &graphics_context->vk.swapchain;
+  present_info.pImageIndices = &swapchain_image_index;
+  present_info.waitSemaphoreCount =
+      (submit_semaphore != VK_NULL_HANDLE) ? 1 : 0;
+  present_info.pWaitSemaphores = &submit_semaphore;
+  VkResult result =
+      vkQueuePresentKHR(graphics_context->vk.present_queue, &present_info);
+  if (result != VK_SUCCESS) {
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+extern "C" int dvrGetFrameScheduleResults(DvrGraphicsContext* context,
+                                          DvrFrameScheduleResult* results,
+                                          int in_result_count) {
+  if (!context || !results)
+    return -EINVAL;
+
+  return context->frame_history.GetPreviousFrameResults(results,
+                                                        in_result_count);
+}
+
+extern "C" void dvrGraphicsSurfaceSetVisible(
+    DvrGraphicsContext* graphics_context, int visible) {
+  graphics_context->display_surface->SetVisible(visible);
+}
+
+extern "C" int dvrGraphicsSurfaceGetVisible(
+    DvrGraphicsContext* graphics_context) {
+  return graphics_context->display_surface->visible() ? 1 : 0;
+}
+
+extern "C" void dvrGraphicsSurfaceSetZOrder(
+    DvrGraphicsContext* graphics_context, int z_order) {
+  graphics_context->display_surface->SetZOrder(z_order);
+}
+
+extern "C" int dvrGraphicsSurfaceGetZOrder(
+    DvrGraphicsContext* graphics_context) {
+  return graphics_context->display_surface->z_order();
+}
+
+extern "C" DvrVideoMeshSurface* dvrGraphicsVideoMeshSurfaceCreate(
+    DvrGraphicsContext* graphics_context) {
+  auto display_surface = graphics_context->display_surface;
+  // A DisplaySurface must be created prior to the creation of a
+  // VideoMeshSurface.
+  LOG_ALWAYS_FATAL_IF(display_surface == nullptr);
+
+  LocalChannelHandle surface_handle = display_surface->CreateVideoMeshSurface();
+  if (!surface_handle.valid()) {
+    return nullptr;
+  }
+
+  std::unique_ptr<DvrVideoMeshSurface> surface(new DvrVideoMeshSurface);
+  surface->client =
+      android::dvr::VideoMeshSurfaceClient::Import(std::move(surface_handle));
+
+  // TODO(jwcai) The next line is not needed...
+  auto producer_queue = surface->client->GetProducerQueue();
+  return surface.release();
+}
+
+extern "C" void dvrGraphicsVideoMeshSurfaceDestroy(
+    DvrVideoMeshSurface* surface) {
+  delete surface;
+}
+
+extern "C" void dvrGraphicsVideoMeshSurfacePresent(
+    DvrGraphicsContext* graphics_context, DvrVideoMeshSurface* surface,
+    const int eye, const float* transform) {
+  volatile android::dvr::VideoMeshSurfaceMetadata* metadata =
+      surface->client->GetMetadataBufferPtr();
+
+  const uint32_t graphics_buffer_index =
+      graphics_context->current_buffer->surface_buffer_index();
+
+  for (int i = 0; i < 4; ++i) {
+    metadata->transform[graphics_buffer_index][eye].val[i] = {
+        transform[i + 0], transform[i + 4], transform[i + 8], transform[i + 12],
+    };
+  }
+}
diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libdisplay/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdisplay/include/dvr/graphics.h b/libs/vr/libdisplay/include/dvr/graphics.h
new file mode 100644
index 0000000..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..8ba9175
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
@@ -0,0 +1,88 @@
+#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);
+
+// Return an event fd for checking if there was an event on the server
+// Note that the only event which will be flagged is POLLIN. You must use
+// dvrDisplayManagerClientTranslateEpollEventMask in order to get the real
+// event flags.
+// @return the fd
+int dvrDisplayManagerClientGetEventFd(DvrDisplayManagerClient* client);
+
+// Once you have received an epoll event, you must translate it to its true
+// flags. This is a workaround for working with UDS.
+// @param in_events pass in the epoll revents that were initially returned
+// @param on success, this value will be overwritten with the true epoll values
+// @return 0 on success, non-zero otherwise
+int dvrDisplayManagerClientTranslateEpollEventMask(
+    DvrDisplayManagerClient* client, int in_events, int* out_events);
+
+// If successful, populates |surface_list| with a list of application
+// surfaces the display is currently using.
+//
+// @return 0 on success. Otherwise it returns a negative error value.
+int dvrDisplayManagerClientGetSurfaceList(
+    DvrDisplayManagerClient* client,
+    DvrDisplayManagerClientSurfaceList** surface_list);
+
+void dvrDisplayManagerClientSurfaceListDestroy(
+    DvrDisplayManagerClientSurfaceList* surface_list);
+
+// @return Returns the number of surfaces in the list.
+size_t dvrDisplayManagerClientSurfaceListGetSize(
+    DvrDisplayManagerClientSurfaceList* surface_list);
+
+// @return Return a unique identifier for a client surface. The identifier can
+// be used to query for other surface properties.
+int dvrDisplayManagerClientSurfaceListGetSurfaceId(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// @return Returns the stacking order of the client surface at |index|.
+int dvrDisplayManagerClientSurfaceListGetClientZOrder(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// @return Returns true if the client surface is visible, false otherwise.
+bool dvrDisplayManagerClientSurfaceListGetClientIsVisible(
+    DvrDisplayManagerClientSurfaceList* surface_list, size_t index);
+
+// 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..4ecd8d4
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client_impl.h
@@ -0,0 +1,38 @@
+#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);
+
+  using Client::event_fd;
+  using Client::GetChannel;
+
+ private:
+  friend BASE;
+
+  DisplayManagerClient();
+
+  DisplayManagerClient(const DisplayManagerClient&) = delete;
+  void operator=(const DisplayManagerClient&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_IMPL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_rpc.h b/libs/vr/libdisplay/include/private/dvr/display_rpc.h
new file mode 100644
index 0000000..1c1a5e0
--- /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/vr/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/vr/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/vr/display/screenshot";
+
+  // Op codes.
+  enum {
+    kOpGetFormat = 0,
+    kOpTakeScreenshot,
+  };
+
+  using Void = pdx::rpc::Void;
+
+  PDX_REMOTE_METHOD(GetFormat, kOpGetFormat, int(Void));
+  PDX_REMOTE_METHOD(TakeScreenshot, kOpTakeScreenshot,
+                    ScreenshotData(int layer_index));
+};
+
+struct VSyncSchedInfo {
+  int64_t vsync_period_ns;
+  int64_t timestamp_ns;
+  uint32_t next_vsync_count;
+
+ private:
+  PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns,
+                           next_vsync_count);
+};
+
+struct DisplayVSyncRPC {
+  // Service path.
+  static constexpr char kClientPath[] = "system/vr/display/vsync";
+
+  // Op codes.
+  enum {
+    kOpWait = 0,
+    kOpAck,
+    kOpGetLastTimestamp,
+    kOpGetSchedInfo,
+    kOpAcknowledge,
+  };
+
+  // Aliases.
+  using Void = pdx::rpc::Void;
+  using Timestamp = int64_t;
+
+  // Methods.
+  PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void));
+  PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void));
+  PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void));
+  PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, int(Void));
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_RPC_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_types.h b/libs/vr/libdisplay/include/private/dvr/display_types.h
new file mode 100644
index 0000000..2bd02bd
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/display_types.h
@@ -0,0 +1,83 @@
+#ifndef ANDROID_DVR_DISPLAY_TYPES_H_
+#define ANDROID_DVR_DISPLAY_TYPES_H_
+
+#ifdef __ARM_NEON
+#include <arm_neon.h>
+#else
+#ifndef __FLOAT32X4T_86
+#define __FLOAT32X4T_86
+typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
+typedef struct float32x4x4_t { float32x4_t val[4]; };
+#endif
+#endif
+
+#include <cutils/native_handle.h>
+
+// DVR display-related data types.
+
+enum dvr_display_surface_type {
+  // Normal display surface meant to be used by applications' GL context to
+  // render into.
+  DVR_SURFACE_TYPE_NORMAL = 0,
+
+  // VideoMeshSurface is used to composite video frames into the 3D world.
+  DVR_SURFACE_TYPE_VIDEO_MESH,
+
+  // System overlay surface type. This is not currently in use.
+  DVR_SURFACE_TYPE_OVERLAY,
+};
+
+enum dvr_display_surface_flags {
+  DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_EDS = (1 << 0),
+  DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION = (1 << 1),
+  DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP = (1 << 2),
+  DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2 = (1 << 3),
+  DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC = (1 << 4),
+};
+
+enum dvr_display_surface_item_flags {
+  DVR_DISPLAY_SURFACE_ITEM_FLAGS_BUFFERS_CHANGED = (1 << 0),
+};
+
+enum dvr_display_surface_attribute {
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_Z_ORDER = (1<<0),
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_VISIBLE = (1<<1),
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR = (1<<2),
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_EXCLUDE_FROM_BLUR = (1<<3),
+  DVR_DISPLAY_SURFACE_ATTRIBUTE_BLUR_BEHIND = (1<<4),
+};
+
+// Maximum number of buffers for a surface. Each buffer represents a single
+// frame and may actually be a buffer array if multiview rendering is in use.
+// Define so that it can be used in shader code.
+#define kSurfaceBufferMaxCount 4
+
+// Maximum number of views per surface. Each eye is a view, for example.
+#define kSurfaceViewMaxCount 4
+
+namespace android {
+namespace dvr {
+
+struct __attribute__((packed, aligned(16))) DisplaySurfaceMetadata {
+  // Array of orientations and translations corresponding with surface buffers.
+  // The index is associated with each allocated buffer by DisplaySurface and
+  // communicated to clients.
+  // The maximum number of buffers is hard coded here as 4 so that we can bind
+  // this data structure in GPU shaders.
+  float32x4_t orientation[kSurfaceBufferMaxCount];
+  float32x4_t translation[kSurfaceBufferMaxCount];
+};
+
+struct __attribute__((packed, aligned(16))) VideoMeshSurfaceMetadata {
+  // Array of transform matrices corresponding with surface buffers.
+  // Note that The index is associated with each allocated buffer by
+  // DisplaySurface instead of VideoMeshSurface due to the fact that the
+  // metadata here is interpreted as video mesh's transformation in each
+  // application's rendering frame.
+  float32x4x4_t transform[4][2];
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DISPLAY_TYPES_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h b/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h
new file mode 100644
index 0000000..b03eeaa
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/dummy_native_window.h
@@ -0,0 +1,30 @@
+#ifndef ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
+#define ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
+
+#include <android/native_window.h>
+#include <ui/ANativeObjectBase.h>
+
+namespace android {
+namespace dvr {
+
+// DummyNativeWindow is an implementation of ANativeWindow that is
+// essentially empty and is used as a surface placeholder during context
+// creation for contexts that we don't intend to call eglSwapBuffers on.
+class DummyNativeWindow
+    : public ANativeObjectBase<ANativeWindow, DummyNativeWindow,
+                               LightRefBase<DummyNativeWindow> > {
+ public:
+  DummyNativeWindow();
+
+ private:
+  static int Query(const ANativeWindow* window, int what, int* value);
+  static int Perform(ANativeWindow* window, int operation, ...);
+
+  DummyNativeWindow(const DummyNativeWindow&) = delete;
+  void operator=(DummyNativeWindow&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_DUMMY_NATIVE_WINDOW_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/frame_history.h b/libs/vr/libdisplay/include/private/dvr/frame_history.h
new file mode 100644
index 0000000..53e0717
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/frame_history.h
@@ -0,0 +1,71 @@
+#ifndef ANDROID_DVR_FRAME_HISTORY_H_
+#define ANDROID_DVR_FRAME_HISTORY_H_
+
+#include <dvr/graphics.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/ring_buffer.h>
+
+namespace android {
+namespace dvr {
+
+// FrameHistory tracks frame times from the start of rendering commands to when
+// the buffer is ready.
+class FrameHistory {
+ public:
+  FrameHistory();
+  explicit FrameHistory(int pending_frame_buffer_size);
+
+  void Reset(int pending_frame_buffer_size);
+
+  // Call when starting rendering commands (i.e. dvrBeginRenderFrame).
+  void OnFrameStart(uint32_t scheduled_vsync, int64_t scheduled_finish_ns);
+
+  // Call when rendering commands are finished (i.e. dvrPresent).
+  void OnFrameSubmit(android::pdx::LocalHandle&& fence);
+
+  // Call once per frame to see if any pending frames have finished.
+  void CheckForFinishedFrames();
+
+  // Uses the recently completed frame render times to predict how long the next
+  // frame will take, in vsync intervals. For example if the predicted frame
+  // time is 10ms and the vsync interval is 11ms, this will return 1. If the
+  // predicted frame time is 12ms and the vsync interval is 11ms, this will
+  // return 2.
+  int PredictNextFrameVsyncInterval(int64_t vsync_period_ns) const;
+
+  // Returns results for recently completed frames. Each frame's result is
+  // returned only once.
+  int GetPreviousFrameResults(DvrFrameScheduleResult* results,
+                              int result_count);
+
+  // Gets the vsync count for the most recently started frame. If there are no
+  // started frames this will return UINT32_MAX.
+  uint32_t GetCurrentFrameVsync() const;
+
+ private:
+  struct PendingFrame {
+    int64_t start_ns;
+    uint32_t scheduled_vsync;
+    int64_t scheduled_finish_ns;
+    android::pdx::LocalHandle fence;
+
+    PendingFrame();
+    PendingFrame(int64_t start_ns, uint32_t scheduled_vsync,
+                 int64_t scheduled_finish_ns,
+                 android::pdx::LocalHandle&& fence);
+
+    PendingFrame(PendingFrame&&) = default;
+    PendingFrame& operator=(PendingFrame&&) = default;
+    PendingFrame(const PendingFrame&) = delete;
+    PendingFrame& operator=(const PendingFrame&) = delete;
+  };
+
+  RingBuffer<PendingFrame> pending_frames_;
+  RingBuffer<DvrFrameScheduleResult> finished_frames_;
+  RingBuffer<int64_t> frame_duration_history_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_FRAME_HISTORY_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h b/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h
new file mode 100644
index 0000000..1d75335
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/gl_fenced_flush.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_DVR_GL_FENCED_FLUSH_H_
+#define ANDROID_DVR_GL_FENCED_FLUSH_H_
+
+#include <EGL/egl.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace dvr {
+
+// Creates a EGL_SYNC_NATIVE_FENCE_ANDROID and flushes. Returns the fence as a
+// file descriptor.
+pdx::LocalHandle CreateGLSyncAndFlush(EGLDisplay display);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_GL_FENCED_FLUSH_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/graphics_private.h b/libs/vr/libdisplay/include/private/dvr/graphics_private.h
new file mode 100644
index 0000000..57c99da
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/graphics_private.h
@@ -0,0 +1,39 @@
+#ifndef ANDROID_DVR_GRAPHICS_PRIVATE_H_
+#define ANDROID_DVR_GRAPHICS_PRIVATE_H_
+
+#ifdef __ARM_NEON
+#include <arm_neon.h>
+#else
+#ifndef __FLOAT32X4T_86
+#define __FLOAT32X4T_86
+typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
+typedef struct float32x4x4_t { float32x4_t val[4]; };
+#endif
+#endif
+
+#include <sys/cdefs.h>
+
+#include <dvr/graphics.h>
+
+__BEGIN_DECLS
+
+// Sets the pose used by the system for EDS. If dvrBeginRenderFrameEds() or
+// dvrBeginRenderFrameLateLatch() are called instead of dvrBeginRenderFrame()
+// it's not necessary to call this function. If this function is used, the call
+// must be made after dvrBeginRenderFrame() and before dvrPresent().
+//
+// @param[in] graphics_context The DvrGraphicsContext.
+// @param[in] render_pose_orientation Head pose orientation that rendering for
+//            this frame will be based off of. This must be an unmodified value
+//            from DvrPoseAsync, returned by dvrPoseGet.
+// @param[in] render_pose_translation Head pose translation that rendering for
+//            this frame will be based off of. This must be an unmodified value
+//            from DvrPoseAsync, returned by dvrPoseGet.
+// @return 0 on success or a negative error code on failure.
+int dvrSetEdsPose(DvrGraphicsContext* graphics_context,
+                  float32x4_t render_pose_orientation,
+                  float32x4_t render_pose_translation);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_GRAPHICS_PRIVATE_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/late_latch.h b/libs/vr/libdisplay/include/private/dvr/late_latch.h
new file mode 100644
index 0000000..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..e52d0b9
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/video_mesh_surface_client.h
@@ -0,0 +1,41 @@
+#ifndef ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
+#define ANDROID_DVR_VIDEO_MESH_SURFACE_CLIENT_H_
+
+#include <private/dvr/buffer_hub_queue_client.h>
+#include <private/dvr/display_client.h>
+
+namespace android {
+namespace dvr {
+
+class VideoMeshSurfaceClient
+    : pdx::ClientBase<VideoMeshSurfaceClient, SurfaceClient> {
+ public:
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+
+  // This call assumes ownership of |handle|.
+  static std::unique_ptr<VideoMeshSurfaceClient> Import(
+      LocalChannelHandle handle);
+
+  std::shared_ptr<ProducerQueue> GetProducerQueue();
+
+  // Get the shared memory metadata buffer for this video mesh surface. If it is
+  // not yet allocated, this will allocate it.
+  volatile VideoMeshSurfaceMetadata* GetMetadataBufferPtr();
+
+ private:
+  friend BASE;
+
+  std::shared_ptr<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..b1a1589
--- /dev/null
+++ b/libs/vr/libdisplay/late_latch.cpp
@@ -0,0 +1,460 @@
+#include "include/private/dvr/late_latch.h"
+
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <log/log.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sensor_constants.h>
+#include <private/dvr/types.h>
+
+#define PRINT_MATRIX 0
+
+#if PRINT_MATRIX
+#ifndef LOG_TAG
+#define LOG_TAG "latelatch"
+#endif
+
+#define PE(str, ...)                                                  \
+  fprintf(stderr, "[%s:%d] " str, __FILE__, __LINE__, ##__VA_ARGS__); \
+  ALOGI("[%s:%d] " str, __FILE__, __LINE__, ##__VA_ARGS__)
+
+#define PV4(v) PE(#v "=%f,%f,%f,%f\n", v[0], v[1], v[2], v[3]);
+#define PM4(m)                                                               \
+  PE(#m ":\n %f,%f,%f,%f\n %f,%f,%f,%f\n %f,%f,%f,%f\n %f,%f,%f,%f\n",       \
+     m(0, 0), m(0, 1), m(0, 2), m(0, 3), m(1, 0), m(1, 1), m(1, 2), m(1, 3), \
+     m(2, 0), m(2, 1), m(2, 2), m(2, 3), m(3, 0), m(3, 1), m(3, 2), m(3, 3))
+#endif  // PRINT_MATRIX
+
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+
+// Compute shader bindings.
+// GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS must be at least 8 for GLES 3.1.
+#define POSE_BINDING 0
+#define RENDER_POSE_BINDING 1
+#define INPUT_BINDING 2
+#define OUTPUT_BINDING 3
+
+using android::pdx::LocalHandle;
+
+namespace {
+
+static const std::string kShaderLateLatch = R"(  // NOLINT
+  struct Pose {
+    vec4 quat;
+    vec3 pos;
+  };
+
+  // Must match DvrPoseAsync C struct.
+  struct DvrPoseAsync {
+    vec4 orientation;
+    vec4 translation;
+    vec4 right_orientation;
+    vec4 right_translation;
+    vec4 angular_velocity;
+    vec4 velocity;
+    vec4 reserved[2];
+  };
+
+  // Must match LateLatchInputData C struct.
+  layout(binding = INPUT_BINDING, std140)
+  buffer InputData {
+    mat4 uEyeFromHeadMat[kSurfaceViewMaxCount];
+    mat4 uProjMat[kSurfaceViewMaxCount];
+    mat4 uPoseOffset[kSurfaceViewMaxCount];
+    mat4 uEdsMat1[kSurfaceViewMaxCount];
+    mat4 uEdsMat2[kSurfaceViewMaxCount];
+    uint uPoseIndex;
+    uint uRenderPoseIndex;
+  } bIn;
+
+  // std140 is to layout the structure in a consistent, standard way so we
+  // can access it from C++.
+  // This structure exactly matches the pose ring buffer in pose_client.h.
+  layout(binding = POSE_BINDING, std140)
+  buffer PoseBuffer {
+    DvrPoseAsync data[kPoseAsyncBufferTotalCount];
+  } bPose;
+
+  // Must stay in sync with DisplaySurfaceMetadata C struct.
+  // GPU thread 0 will exclusively read in a pose and capture it
+  // into this array.
+  layout(binding = RENDER_POSE_BINDING, std140)
+  buffer DisplaySurfaceMetadata {
+    vec4 orientation[kSurfaceBufferMaxCount];
+    vec4 translation[kSurfaceBufferMaxCount];
+  } bSurfaceData;
+
+  // Must stay in sync with DisplaySurfaceMetadata C struct.
+  // Each thread writes to a vertic
+  layout(binding = OUTPUT_BINDING, std140)
+  buffer Output {
+    mat4 viewProjMatrix[kSurfaceViewMaxCount];
+    mat4 viewMatrix[kSurfaceViewMaxCount];
+    vec4 quaternion;
+    vec4 translation;
+  } bOut;
+
+  // Thread 0 will also store the single quat/pos pair in shared variables
+  // for the other threads to use (left and right eye in this array).
+  shared Pose sharedPose[2];
+
+  // Rotate v1 by the given quaternion. This is based on mathfu's
+  // Quaternion::Rotate function. It is the typical implementation of this
+  // operation. Eigen has a similar method (Quaternion::_transformVector) that
+  // supposedly requires fewer operations, but I am skeptical of optimizing
+  // shader code without proper profiling first.
+  vec3 rotate(vec4 quat, vec3 v1) {
+    float ss = 2.0 * quat.w;
+    vec3 v = quat.xyz;
+    return ss * cross(v, v1) + (ss * quat.w - 1.0) * v1 +
+           2.0 * dot(v, v1) * v;
+  }
+
+  // See Eigen Quaternion::conjugate;
+  // Note that this isn't a true multiplicative inverse unless you can guarantee
+  // quat is also normalized, but that typically isn't an issue for our
+  // purposes.
+  vec4 quatInvert(vec4 quat) {
+    return vec4(-quat.xyz, quat.w);
+  }
+
+  // This is based on mathfu's Quaternion::operator*(Quaternion)
+  // Eigen's version is mathematically equivalent, just notationally different.
+  vec4 quatMul(vec4 q1, vec4 q2) {
+    return vec4(q1.w * q2.xyz + q2.w * q1.xyz + cross(q1.xyz, q2.xyz),
+                q1.w * q2.w - dot(q1.xyz, q2.xyz));
+  }
+
+  // Equivalent to pose.h GetObjectFromReferenceMatrix.
+  mat4 getInverseMatrix(Pose pose) {
+    // Invert quaternion and store fields the way Eigen does so we can
+    // keep in sync with Eigen methods easier.
+    vec4 quatInv = quatInvert(pose.quat);
+    vec3 v = quatInv.xyz;
+    float s = quatInv.w;
+    // Convert quaternion to matrix. See Eigen Quaternion::toRotationMatrix()
+    float x2 = v.x * v.x, y2 = v.y * v.y, z2 = v.z * v.z;
+    float sx = s * v.x, sy = s * v.y, sz = s * v.z;
+    float xz = v.x * v.z, yz = v.y * v.z, xy = v.x * v.y;
+    // Inverse translation.
+    vec3 point = -pose.pos;
+
+    return
+      mat4(1.0 - 2.0 * (y2 + z2), 2.0 * (xy + sz), 2.0 * (xz - sy), 0.0,
+           2.0 * (xy - sz), 1.0 - 2.0 * (x2 + z2), 2.0 * (sx + yz), 0.0,
+           2.0 * (sy + xz), 2.0 * (yz - sx), 1.0 - 2.0 * (x2 + y2), 0.0,
+           0.0, 0.0, 0.0, 1.0)*
+      mat4(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
+           point.x, point.y, point.z, 1.0);
+  }
+
+  void appLateLatch() {
+    uint poseIndex = (gl_LocalInvocationIndex & uint(1));
+    mat4 head_from_center = getInverseMatrix(sharedPose[poseIndex]);
+    bOut.viewMatrix[gl_LocalInvocationIndex] =
+        bIn.uEyeFromHeadMat[gl_LocalInvocationIndex] *
+        head_from_center * bIn.uPoseOffset[gl_LocalInvocationIndex];
+    bOut.viewProjMatrix[gl_LocalInvocationIndex] =
+        bIn.uProjMat[gl_LocalInvocationIndex] *
+        bOut.viewMatrix[gl_LocalInvocationIndex];
+  }
+
+  // Extract the app frame's pose.
+  Pose getPoseFromApp() {
+    Pose p;
+    p.quat = bSurfaceData.orientation[bIn.uRenderPoseIndex];
+    p.pos =  bSurfaceData.translation[bIn.uRenderPoseIndex].xyz;
+    return p;
+  }
+
+  // See Posef::GetPoseOffset.
+  Pose getPoseOffset(Pose p1, Pose p2) {
+    Pose p;
+    p.quat = quatMul(quatInvert(p2.quat), p1.quat);
+    // TODO(jbates) Consider enabling positional EDS when it is better
+    //              tested.
+    // p.pos = p2.pos - p1.pos;
+    p.pos = vec3(0.0);
+    return p;
+  }
+
+  void edsLateLatch() {
+    Pose pose1 = getPoseFromApp();
+    Pose correction;
+    // Ignore the texture pose if the quat is not unit-length.
+    float tex_quat_length = length(pose1.quat);
+    uint poseIndex = (gl_LocalInvocationIndex & uint(1));
+    if (abs(tex_quat_length - 1.0) < 0.001)
+      correction = getPoseOffset(pose1, sharedPose[poseIndex]);
+    else
+      correction = Pose(vec4(0, 0, 0, 1), vec3(0, 0, 0));
+    mat4 eye_old_from_eye_new_matrix = getInverseMatrix(correction);
+    bOut.viewProjMatrix[gl_LocalInvocationIndex] =
+        bIn.uEdsMat1[gl_LocalInvocationIndex] *
+        eye_old_from_eye_new_matrix * bIn.uEdsMat2[gl_LocalInvocationIndex];
+    // Currently unused, except for debugging:
+    bOut.viewMatrix[gl_LocalInvocationIndex] = eye_old_from_eye_new_matrix;
+  }
+
+  // One thread per surface view.
+  layout (local_size_x = kSurfaceViewMaxCount, local_size_y = 1,
+          local_size_z = 1) in;
+
+  void main() {
+    // First, thread 0 late latches pose and stores it into various places.
+    if (gl_LocalInvocationIndex == uint(0)) {
+      sharedPose[0].quat = bPose.data[bIn.uPoseIndex].orientation;
+      sharedPose[0].pos =  bPose.data[bIn.uPoseIndex].translation.xyz;
+      sharedPose[1].quat = bPose.data[bIn.uPoseIndex].right_orientation;
+      sharedPose[1].pos =  bPose.data[bIn.uPoseIndex].right_translation.xyz;
+      if (IS_APP_LATE_LATCH) {
+        bSurfaceData.orientation[bIn.uRenderPoseIndex] = sharedPose[0].quat;
+        bSurfaceData.translation[bIn.uRenderPoseIndex] = vec4(sharedPose[0].pos, 0.0);
+        // TODO(jbates) implement app late-latch support for separate eye poses.
+        // App late latch currently uses the same pose for both eye views.
+        sharedPose[1] = sharedPose[0];
+      }
+      bOut.quaternion = sharedPose[0].quat;
+      bOut.translation = vec4(sharedPose[0].pos, 0.0);
+    }
+
+    // Memory barrier to make sure all threads can see prior writes.
+    memoryBarrierShared();
+
+    // Execution barrier to block all threads here until all threads have
+    // reached this point -- ensures the late latching is done.
+    barrier();
+
+    if (IS_APP_LATE_LATCH)
+      appLateLatch();
+    else
+      edsLateLatch();
+  }
+)";
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+LateLatch::LateLatch(bool is_app_late_latch)
+    : LateLatch(is_app_late_latch, LocalHandle()) {}
+
+LateLatch::LateLatch(bool is_app_late_latch,
+                     LocalHandle&& surface_metadata_fd)
+    : is_app_late_latch_(is_app_late_latch),
+      app_late_latch_output_(NULL),
+      eds_late_latch_output_(NULL) {
+  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_) {
+    ALOGE("LateLatch Error: failed to create pose client");
+  } else {
+    int ret = privateDvrPoseGetRingBufferFd(pose_client_, &pose_buffer_fd);
+    if (ret < 0) {
+      ALOGE("LateLatch Error: failed to get pose ring buffer");
+    }
+  }
+
+  glGenBuffers(1, &pose_buffer_object_);
+  glGenBuffers(1, &metadata_buffer_id_);
+  if (!glBindSharedBufferQCOM) {
+    ALOGE("Error: Missing gralloc buffer extension, no pose data");
+  } else {
+    if (pose_buffer_fd) {
+      glBindBuffer(GL_SHADER_STORAGE_BUFFER, pose_buffer_object_);
+      glBindSharedBufferQCOM(GL_SHADER_STORAGE_BUFFER,
+                             kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync),
+                             pose_buffer_fd.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 {
+  LOG_ALWAYS_FATAL_IF(!is_app_late_latch_);
+  CHECK_GL();
+  late_latch_program_.Use();
+
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING,
+                   metadata_buffer_id_);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, pose_buffer_object_);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, output_buffer_id_);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer_id_);
+  LateLatchInput* adata = (LateLatchInput*)glMapBufferRange(
+      GL_SHADER_STORAGE_BUFFER, 0, sizeof(LateLatchInput),
+      GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+  if (adata)
+    *adata = data;
+  else
+    ALOGE("Error: LateLatchInput gl mapping is null");
+  glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, input_buffer_id_);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+  CHECK_GL();
+
+  // The output buffer is going to be written but it may be read by
+  // earlier shaders, so we need a shader storage memory barrier.
+  glMemoryBarrier(GL_SHADER_STORAGE_BUFFER);
+
+  glDispatchCompute(1, 1, 1);
+  CHECK_GL();
+
+  // The transform feedback buffer is going to be read as a uniform by the app,
+  // so we need a uniform memory barrier.
+  glMemoryBarrier(GL_UNIFORM_BARRIER_BIT);
+
+  if (app_late_latch_output_) {
+    // Capture the output data:
+    CaptureOutputData(app_late_latch_output_);
+  }
+#if PRINT_MATRIX
+  // Print the composed matrix to stderr:
+  LateLatchOutput out_data;
+  CaptureOutputData(&out_data);
+  CHECK_GL();
+  PE("LL APP slot:%d\n", data.render_pose_index);
+  PM4(data.proj_mat[0]);
+  PM4(out_data.view_proj_matrix[0]);
+  PM4(out_data.view_proj_matrix[1]);
+  PM4(out_data.view_proj_matrix[2]);
+  PM4(out_data.view_proj_matrix[3]);
+  PM4(out_data.view_matrix[0]);
+  PM4(out_data.view_matrix[1]);
+  PM4(out_data.view_matrix[2]);
+  PM4(out_data.view_matrix[3]);
+  PV4(out_data.pose_quaternion);
+  PV4(out_data.pose_translation);
+#endif
+
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, 0);
+  glUseProgram(0);
+}
+
+void LateLatch::AddEdsLateLatch(const LateLatchInput& data,
+                                GLuint render_pose_buffer_object) const {
+  LOG_ALWAYS_FATAL_IF(is_app_late_latch_);
+  late_latch_program_.Use();
+
+  // Fall back on internal buffer when none is provided.
+  if (!render_pose_buffer_object)
+    render_pose_buffer_object = metadata_buffer_id_;
+
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING,
+                   render_pose_buffer_object);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, pose_buffer_object_);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, output_buffer_id_);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, input_buffer_id_);
+  LateLatchInput* adata = (LateLatchInput*)glMapBufferRange(
+      GL_SHADER_STORAGE_BUFFER, 0, sizeof(LateLatchInput),
+      GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+  *adata = data;
+  glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
+  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, input_buffer_id_);
+  CHECK_GL();
+
+  glDispatchCompute(1, 1, 1);
+  CHECK_GL();
+
+  if (eds_late_latch_output_) {
+    // Capture the output data:
+    CaptureOutputData(eds_late_latch_output_);
+  }
+#if PRINT_MATRIX
+  // Print the composed matrix to stderr:
+  LateLatchOutput out_data;
+  CaptureOutputData(&out_data);
+  CHECK_GL();
+  PE("LL EDS\n");
+  PM4(out_data.view_proj_matrix[0]);
+  PM4(out_data.view_matrix[0]);
+  PV4(out_data.pose_quaternion);
+  PV4(out_data.pose_translation);
+#endif
+
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_POSE_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, POSE_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, OUTPUT_BINDING, 0);
+  glBindBufferBase(GL_SHADER_STORAGE_BUFFER, INPUT_BINDING, 0);
+  glUseProgram(0);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/native_buffer_queue.cpp b/libs/vr/libdisplay/native_buffer_queue.cpp
new file mode 100644
index 0000000..8dd0ee0
--- /dev/null
+++ b/libs/vr/libdisplay/native_buffer_queue.cpp
@@ -0,0 +1,151 @@
+#include "include/private/dvr/native_buffer_queue.h"
+
+#include <log/log.h>
+#include <sys/epoll.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <array>
+
+#include <private/dvr/display_types.h>
+
+namespace android {
+namespace dvr {
+
+NativeBufferQueue::NativeBufferQueue(
+    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) {
+  LOG_ALWAYS_FATAL_IF(!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.
+  LOG_ALWAYS_FATAL_IF(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..24ecd8a
--- /dev/null
+++ b/libs/vr/libdisplay/native_window.cpp
@@ -0,0 +1,458 @@
+#include <EGL/egl.h>
+
+#include <android/native_window.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 <log/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.
+    LOG_FATAL_IF(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.
+    LOG_FATAL_IF(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..3ad0c68
--- /dev/null
+++ b/libs/vr/libdisplay/screenshot_client.cpp
@@ -0,0 +1,66 @@
+#include "include/private/dvr/screenshot_client.h"
+
+#include <log/log.h>
+
+#include <mutex>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/display_rpc.h>
+
+using android::pdx::Transaction;
+using android::pdx::rpc::ClientPayload;
+using android::pdx::rpc::MessageBuffer;
+using android::pdx::rpc::ReplyBuffer;
+
+namespace android {
+namespace dvr {
+
+namespace {
+// Maximum supported pixels for screenshot capture. If the actual target buffer
+// is more than this, an error will be reported.
+constexpr int kMaxScreenshotPixels = 6000 * 4000;
+}  // namespace
+
+int ScreenshotClient::Take(std::vector<uint8_t>* out_image, int index,
+                           int* return_width, int* return_height) {
+  if (format_ != HAL_PIXEL_FORMAT_RGB_888) {
+    ALOGE("ScreenshotClient::Take: Unsupported layout format: format=%d",
+          format_);
+    return -ENOSYS;
+  }
+
+  // TODO(eieio): Make a cleaner way to ensure enough capacity for send or
+  // receive buffers. This method assumes TLS buffers that will maintain
+  // capacity across calls within the same thread.
+  MessageBuffer<ReplyBuffer>::Reserve(kMaxScreenshotPixels * 3);
+  auto status = InvokeRemoteMethod<DisplayScreenshotRPC::TakeScreenshot>(index);
+  if (!status) {
+    ALOGE("ScreenshotClient::Take: Failed to take screenshot: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *return_width = status.get().width;
+  *return_height = status.get().height;
+  *out_image = std::move(status.take().buffer);
+  return 0;
+}
+
+ScreenshotClient::ScreenshotClient()
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DisplayScreenshotRPC::kClientPath)) {
+  auto status = InvokeRemoteMethod<DisplayScreenshotRPC::GetFormat>();
+  if (!status) {
+    ALOGE(
+        "ScreenshotClient::ScreenshotClient: Failed to retrieve screenshot "
+        "layout: %s",
+        status.GetErrorMessage().c_str());
+
+    Close(status.error());
+  } else {
+    format_ = status.get();
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libdisplay/system/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libdisplay/tests/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..c928a08
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_client.cpp
@@ -0,0 +1,75 @@
+#include "include/private/dvr/vsync_client.h"
+
+#include <log/log.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/display_rpc.h>
+
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+VSyncClient::VSyncClient(long timeout_ms)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+               DisplayVSyncRPC::kClientPath),
+           timeout_ms) {}
+
+VSyncClient::VSyncClient()
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DisplayVSyncRPC::kClientPath)) {}
+
+int VSyncClient::Wait(int64_t* timestamp_ns) {
+  auto status = InvokeRemoteMethod<DisplayVSyncRPC::Wait>();
+  if (!status) {
+    ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  if (timestamp_ns != nullptr) {
+    *timestamp_ns = status.get();
+  }
+  return 0;
+}
+
+int VSyncClient::GetFd() { return event_fd(); }
+
+int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) {
+  auto status = InvokeRemoteMethod<DisplayVSyncRPC::GetLastTimestamp>();
+  if (!status) {
+    ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+  *timestamp_ns = status.get();
+  return 0;
+}
+
+int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns,
+                              uint32_t* next_vsync_count) {
+  if (!vsync_period_ns || !timestamp_ns || !next_vsync_count)
+    return -EINVAL;
+
+  auto status = InvokeRemoteMethod<DisplayVSyncRPC::GetSchedInfo>();
+  if (!status) {
+    ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s",
+          status.GetErrorMessage().c_str());
+    return -status.error();
+  }
+
+  *vsync_period_ns = status.get().vsync_period_ns;
+  *timestamp_ns = status.get().timestamp_ns;
+  *next_vsync_count = status.get().next_vsync_count;
+  return 0;
+}
+
+int VSyncClient::Acknowledge() {
+  auto status = InvokeRemoteMethod<DisplayVSyncRPC::Acknowledge>();
+  ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s",
+           status.GetErrorMessage().c_str());
+  return ReturnStatusOrError(status);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/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..80eb3a6
--- /dev/null
+++ b/libs/vr/libdvrcommon/Android.mk
@@ -0,0 +1,80 @@
+# 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 := \
+	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..d4718a9
--- /dev/null
+++ b/libs/vr/libdvrcommon/frame_time_history.cpp
@@ -0,0 +1,37 @@
+#include <private/dvr/frame_time_history.h>
+
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+void FrameTimeHistory::AddSample(int64_t frame_time) {
+  if (size_ == frame_times_.size()) {
+    int64_t expired_frame_time = frame_times_[start_];
+    frame_times_[start_] = frame_time;
+    start_ = (start_ + 1) % frame_times_.size();
+    total_frame_time_ -= expired_frame_time;
+  } else {
+    frame_times_[(start_ + size_) % frame_times_.size()] = frame_time;
+    size_++;
+  }
+  total_frame_time_ += frame_time;
+}
+
+int FrameTimeHistory::GetSampleCount() const { return size_; }
+
+int64_t FrameTimeHistory::GetAverage() const {
+  LOG_ALWAYS_FATAL_IF(size_ == 0);
+  return total_frame_time_ / size_;
+}
+
+void FrameTimeHistory::ResetWithSeed(int64_t frame_time_seed) {
+  start_ = 0;
+  size_ = frame_times_.size();
+  for (size_t i = 0; i < size_; ++i)
+    frame_times_[i] = frame_time_seed;
+  total_frame_time_ = frame_time_seed * size_;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrcommon/include/private/dvr/benchmark.h b/libs/vr/libdvrcommon/include/private/dvr/benchmark.h
new file mode 100644
index 0000000..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..c31a385
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/debug.h
@@ -0,0 +1,37 @@
+#ifndef ANDROID_DVR_DEBUG_H_
+#define ANDROID_DVR_DEBUG_H_
+
+#include <GLES3/gl3.h>
+#include <math.h>
+
+#include <log/log.h>
+
+#ifndef NDEBUG
+#define CHECK_GL()                   \
+  do {                               \
+    const GLenum err = glGetError(); \
+    if (err != GL_NO_ERROR) {        \
+      ALOGE("OpenGL error %d", err); \
+    }                                \
+  } while (0)
+
+#define CHECK_GL_FBO()                                        \
+  do {                                                        \
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); \
+    switch (status) {                                         \
+      case GL_FRAMEBUFFER_COMPLETE:                           \
+        break;                                                \
+      case GL_FRAMEBUFFER_UNSUPPORTED:                        \
+        ALOGE("GL_FRAMEBUFFER_UNSUPPORTED");                  \
+        break;                                                \
+      default:                                                \
+        ALOGE("FBO user error: %d", status);                  \
+        break;                                                \
+    }                                                         \
+  } while (0)
+#else
+#define CHECK_GL()
+#define CHECK_GL_FBO()
+#endif
+
+#endif  // ANDROID_DVR_DEBUG_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/eigen.h b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
new file mode 100644
index 0000000..defaf58
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/eigen.h
@@ -0,0 +1,57 @@
+#ifndef ANDROID_DVR_EIGEN_H_
+#define ANDROID_DVR_EIGEN_H_
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+namespace Eigen {
+
+// Eigen doesn't take advantage of C++ template typedefs, but we can
+template <class T, int N>
+using Vector = Matrix<T, N, 1>;
+
+template <class T>
+using Vector2 = Vector<T, 2>;
+
+template <class T>
+using Vector3 = Vector<T, 3>;
+
+template <class T>
+using Vector4 = Vector<T, 4>;
+
+template <class T, int N>
+using RowVector = Matrix<T, 1, N>;
+
+template <class T>
+using RowVector2 = RowVector<T, 2>;
+
+template <class T>
+using RowVector3 = RowVector<T, 3>;
+
+template <class T>
+using RowVector4 = RowVector<T, 4>;
+
+// In Eigen, the type you should be using for transformation matrices is the
+// `Transform` class, instead of a raw `Matrix`.
+// The `Projective` option means this will not make any assumptions about the
+// last row of the object, making this suitable for use as general OpenGL
+// projection matrices (which is the most common use-case). The one caveat
+// is that in order to apply this transformation to non-homogeneous vectors
+// (e.g., vec3), you must use the `.linear()` method to get the affine part of
+// the matrix.
+//
+// Example:
+//   mat4 transform;
+//   vec3 position;
+//   vec3 transformed = transform.linear() * position;
+//
+// Note, the use of N-1 is because the parameter passed to Eigen is the ambient
+// dimension of the transformation, not the size of the matrix iself.
+// However graphics programmers sometimes get upset when they see a 3 next
+// to a matrix when they expect a 4, so I'm hoping this will avoid that.
+template <class T, int N>
+using AffineMatrix = Transform<T, N-1, Projective>;
+
+}  // namespace Eigen
+
+#endif  // ANDROID_DVR_EIGEN_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
new file mode 100644
index 0000000..91e12c5
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/epoll_file_descriptor.h
@@ -0,0 +1,62 @@
+#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+
+namespace android {
+namespace dvr {
+
+class EpollFileDescriptor {
+ public:
+  static const int CTL_ADD = EPOLL_CTL_ADD;
+  static const int CTL_MOD = EPOLL_CTL_MOD;
+  static const int CTL_DEL = EPOLL_CTL_DEL;
+
+  EpollFileDescriptor() : fd_(-1) {}
+
+  // Constructs an EpollFileDescriptor from an integer file descriptor and
+  // takes ownership.
+  explicit EpollFileDescriptor(int fd) : fd_(fd) {}
+
+  bool IsValid() const { return fd_.get() >= 0; }
+
+  int Create() {
+    if (IsValid()) {
+      ALOGW("epoll fd has already been created.");
+      return -EALREADY;
+    }
+
+    fd_.reset(epoll_create(64));
+
+    if (fd_.get() < 0)
+      return -errno;
+    else
+      return 0;
+  }
+
+  int Control(int op, int target_fd, epoll_event* ev) {
+    if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
+      return -errno;
+    else
+      return 0;
+  }
+
+  int Wait(epoll_event* events, int maxevents, int timeout) {
+    int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
+
+    if (ret < 0)
+      return -errno;
+    else
+      return ret;
+  }
+
+ private:
+  base::unique_fd fd_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
new file mode 100644
index 0000000..d0ee69c
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/field_of_view.h
@@ -0,0 +1,95 @@
+#ifndef ANDROID_DVR_FIELD_OF_VIEW_H_
+#define ANDROID_DVR_FIELD_OF_VIEW_H_
+
+#include <cmath>
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+// Encapsulates a generalized, asymmetric field of view with four half angles.
+// Each half angle denotes the angle between the corresponding frustum plane.
+// Together with a near and far plane, a FieldOfView forms the frustum of an
+// off-axis perspective projection.
+class FieldOfView {
+ public:
+  // The default constructor sets an angle of 0 (in any unit) for all four
+  // half-angles.
+  FieldOfView() : left_(0.0f), right_(0.0f), bottom_(0.0f), top_(0.0f) {}
+
+  // Constructs a FieldOfView from four angles.
+  FieldOfView(float left, float right, float bottom, float top)
+      : left_(left), right_(right), bottom_(bottom), top_(top) {}
+
+  explicit FieldOfView(const float* fov)
+      : FieldOfView(fov[0], fov[1], fov[2], fov[3]) {}
+
+  // Accessors for all four half-angles.
+  float GetLeft() const { return left_; }
+  float GetRight() const { return right_; }
+  float GetBottom() const { return bottom_; }
+  float GetTop() const { return top_; }
+
+  // Setters for all four half-angles.
+  void SetLeft(float left) { left_ = left; }
+  void SetRight(float right) { right_ = right; }
+  void SetBottom(float bottom) { bottom_ = bottom; }
+  void SetTop(float top) { top_ = top; }
+
+  Eigen::AffineMatrix<float, 4> GetProjectionMatrix(float z_near,
+                                                    float z_far) const {
+    float x_left = -std::tan(left_) * z_near;
+    float x_right = std::tan(right_) * z_near;
+    float y_bottom = -std::tan(bottom_) * z_near;
+    float y_top = std::tan(top_) * z_near;
+
+    float zero = 0.0f;
+    if (x_left == x_right || y_bottom == y_top || z_near == z_far ||
+        z_near <= zero || z_far <= zero) {
+      return Eigen::AffineMatrix<float, 4>::Identity();
+    }
+
+    float x = (2 * z_near) / (x_right - x_left);
+    float y = (2 * z_near) / (y_top - y_bottom);
+    float a = (x_right + x_left) / (x_right - x_left);
+    float b = (y_top + y_bottom) / (y_top - y_bottom);
+    float c = (z_near + z_far) / (z_near - z_far);
+    float d = (2 * z_near * z_far) / (z_near - z_far);
+
+    // Note: Eigen matrix initialization syntax is always 'column-major'
+    // even if the storage is row-major. Or in other words, just write the
+    // matrix like you'd see in a math textbook.
+    Eigen::AffineMatrix<float, 4> result;
+    result.matrix() << x,  0,  a,  0,
+                       0,  y,  b,  0,
+                       0,  0,  c,  d,
+                       0,  0, -1,  0;
+    return result;
+  }
+
+  static FieldOfView FromProjectionMatrix(
+      const Eigen::AffineMatrix<float, 4>& m) {
+    // Compute tangents.
+    float tan_vert_fov = 1.0f / m(1, 1);
+    float tan_horz_fov = 1.0f / m(0, 0);
+    float t = (m(1, 2) + 1.0f) * tan_vert_fov;
+    float b = (m(1, 2) - 1.0f) * tan_vert_fov;
+    float l = (m(0, 2) - 1.0f) * tan_horz_fov;
+    float r = (m(0, 2) + 1.0f) * tan_horz_fov;
+
+    return FieldOfView(std::atan(-l), std::atan(r), std::atan(-b),
+                       std::atan(t));
+  }
+
+ private:
+  float left_;
+  float right_;
+  float bottom_;
+  float top_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_FIELD_OF_VIEW_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h b/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h
new file mode 100644
index 0000000..008c636
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/frame_time_history.h
@@ -0,0 +1,33 @@
+#ifndef ANDROID_DVR_FRAME_TIME_HISTORY_H_
+#define ANDROID_DVR_FRAME_TIME_HISTORY_H_
+
+#include <stdint.h>
+
+#include <array>
+
+namespace android {
+namespace dvr {
+
+// Maintains frame time history and provides averaging utility methods.
+class FrameTimeHistory {
+ public:
+  void AddSample(int64_t frame_time);
+  int GetSampleCount() const;
+  int64_t GetAverage() const;
+  float GetAverageFps() const {
+    return 1000000000.0f / static_cast<float>(GetAverage());
+  }
+  void ResetWithSeed(int64_t frame_time_seed);
+
+ private:
+  static constexpr int kFrameTimeHistoryNumSamples = 30;
+  std::array<int64_t, kFrameTimeHistoryNumSamples> frame_times_;
+  int start_ = 0;
+  size_t size_ = 0;
+  int64_t total_frame_time_ = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_FRAME_TIME_HISTORY_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
new file mode 100644
index 0000000..12ef622
--- /dev/null
+++ b/libs/vr/libdvrcommon/include/private/dvr/log_helpers.h
@@ -0,0 +1,64 @@
+#ifndef ANDROID_DVR_LOG_HELPERS_H_
+#define ANDROID_DVR_LOG_HELPERS_H_
+
+#include <iomanip>
+#include <ostream>
+
+#include <private/dvr/eigen.h>
+#include <private/dvr/field_of_view.h>
+
+namespace android {
+namespace dvr {
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 2>& vec) {
+  return out << "vec2(" << vec.x() << ',' << vec.y() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 3>& vec) {
+  return out << "vec3(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::Vector<T, 4>& vec) {
+  return out << "vec4(" << vec.x() << ',' << vec.y() << ',' << vec.z() << ','
+             << vec.w() << ')';
+}
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& out,
+                                const Eigen::AffineMatrix<T, 4>& mat) {
+  out << std::setfill(' ') << std::setprecision(4) << std::fixed
+      << std::showpos;
+  out << "\nmat4[";
+  out << std::setw(10) << mat(0, 0) << " " << std::setw(10) << mat(0, 1) << " "
+      << std::setw(10) << mat(0, 2) << " " << std::setw(10) << mat(0, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(1, 0) << " " << std::setw(10) << mat(1, 1) << " "
+      << std::setw(10) << mat(1, 2) << " " << std::setw(10) << mat(1, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(2, 0) << " " << std::setw(10) << mat(2, 1) << " "
+      << std::setw(10) << mat(2, 2) << " " << std::setw(10) << mat(2, 3);
+  out << "]\n    [";
+  out << std::setw(10) << mat(3, 0) << " " << std::setw(10) << mat(3, 1) << " "
+      << std::setw(10) << mat(3, 2) << " " << std::setw(10) << mat(3, 3);
+  out << "]\n";
+
+  return out;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const FieldOfView& fov) {
+  return out << "fov(" << (fov.GetLeft() * 180.0f / M_PI) << ','
+             << (fov.GetRight() * 180.0f / M_PI) << ','
+             << (fov.GetBottom() * 180.0f / M_PI) << ','
+             << (fov.GetTop() * 180.0f / M_PI) << ')';
+}
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_LOG_HELPERS_H_
diff --git a/libs/vr/libdvrcommon/include/private/dvr/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..7925f65
--- /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 <log/log.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) {
+    ALOGE("Could not open '%s' to get product revision: %s",
+          dvr_product_revision_file_path(), strerror(errno));
+    global_product_revision_processed = true;
+    return;
+  }
+
+  read_rc = read(fd, global_product_revision_str, kProductRevisionStringSize);
+  if (read_rc <= 0) {
+    ALOGE("Could not read from '%s': %s", dvr_product_revision_file_path(),
+          strerror(errno));
+    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 {
+    ALOGE("Unable to match '%s' to a product/revision.",
+          global_product_revision_str);
+  }
+
+  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..a6a2182
--- /dev/null
+++ b/libs/vr/libdvrcommon/tests/numeric_test.cpp
@@ -0,0 +1,68 @@
+#include <gtest/gtest.h>
+
+#define EIGEN_NO_STATIC_ASSERT
+#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..b9e601c
--- /dev/null
+++ b/libs/vr/libdvrgraphics/Android.mk
@@ -0,0 +1,37 @@
+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 := \
+	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)
+# Rather than add this header-file-only library to all users of libdvrgraphics,
+# include it here.
+LOCAL_WHOLE_STATIC_LIBRARIES := libarect
+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..90e271e
--- /dev/null
+++ b/libs/vr/libdvrgraphics/blur.cpp
@@ -0,0 +1,246 @@
+#include "include/private/dvr/graphics/blur.h"
+
+// clang-format off
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+// clang-format on
+#include <hardware/gralloc.h>
+
+#include <string>
+
+#include <log/log.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/egl_image.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+#define POSITION_ATTR 0
+#define OFFSET_BINDING 0
+#define SAMPLER_BINDING 1
+
+namespace {
+
+std::string screen_space_vert_shader = SHADER0([]() {  // NOLINT
+  layout(location = 0) in vec4 position_uv;
+  out vec2 texCoords;
+
+  void main() {
+    gl_Position = vec4(position_uv.xy, 0.0, 1.0);
+    texCoords = position_uv.zw;
+  }
+});
+
+std::string kawase_blur_frag_shader = SHADER0([]() {  // NOLINT
+  precision mediump float;
+  layout(location = 0) uniform vec2 uSampleOffsets[4];
+  layout(binding = 1) uniform APP_SAMPLER_2D uTexture;
+  in vec2 texCoords;
+  out vec4 color;
+
+  void main() {
+    vec2 tc = texCoords;
+    color = texture(uTexture, tc + uSampleOffsets[0]);
+    color += texture(uTexture, tc + uSampleOffsets[1]);
+    color += texture(uTexture, tc + uSampleOffsets[2]);
+    color += texture(uTexture, tc + uSampleOffsets[3]);
+    color *= (1.0 / 4.0);
+  }
+});
+
+constexpr int g_num_samples = 4;
+
+// Modified kernel patterns originally based on:
+// https://software.intel.com/en-us/blogs/2014/07/15/an-investigation-of-fast-real-time-gpu-based-image-blur-algorithms
+// The modification is left and right rotations of the 3rd and 4th patterns.
+const android::dvr::vec2 g_blur_samples[][g_num_samples] = {
+    {{0.5f, 0.5f}, {-0.5f, 0.5f}, {0.5f, -0.5f}, {-0.5f, -0.5f}},
+    {{1.5f, 1.5f}, {-1.5f, 1.5f}, {1.5f, -1.5f}, {-1.5f, -1.5f}},
+    {{2.5f, 1.5f}, {-1.5f, 2.5f}, {1.5f, -2.5f}, {-2.5f, -1.5f}},
+    {{2.5f, 3.5f}, {-3.5f, 2.5f}, {3.5f, -2.5f}, {-2.5f, -3.5f}},
+    // Last pass disabled, because it is more blur than we need.
+    // {{3.5f, 3.5f}, {-3.5f, 3.5f}, {3.5f, -3.5f}, {-3.5f, -3.5f}},
+};
+
+}  // namespace
+
+namespace android {
+namespace dvr {
+
+Blur::Blur(int w, int h, GLuint source_texture, GLint source_texture_target,
+           GLint target_texture_target, bool is_external, EGLDisplay display,
+           int num_blur_outputs)
+    : display_(display),
+      target_texture_target_(target_texture_target),
+      width_(w),
+      height_(h),
+      fbo_q_free_(1 + num_blur_outputs) {
+  LOG_ALWAYS_FATAL_IF(num_blur_outputs <= 0);
+  source_fbo_ =
+      CreateFbo(w, h, source_texture, source_texture_target, is_external);
+  fbo_half_ = CreateFbo(w / 2, h / 2, 0, target_texture_target, is_external);
+  // Create the quarter res fbos.
+  for (size_t i = 0; i < fbo_q_free_.GetCapacity(); ++i)
+    fbo_q_.push_back(
+        CreateFbo(w / 4, h / 4, 0, target_texture_target, is_external));
+  scale_ = 1.0f;
+}
+
+Blur::~Blur() {
+  glFinish();
+  glDeleteFramebuffers(1, &source_fbo_.fbo);
+  glDeleteFramebuffers(1, &fbo_half_.fbo);
+  // Note: source_fbo_.texture is not deleted because it was created externally.
+  glDeleteTextures(1, &fbo_half_.texture);
+  if (fbo_half_.egl_image)
+    eglDestroyImageKHR(display_, fbo_half_.egl_image);
+  for (const auto& fbo : fbo_q_) {
+    glDeleteFramebuffers(1, &fbo.fbo);
+    glDeleteTextures(1, &fbo.texture);
+    if (fbo.egl_image)
+      eglDestroyImageKHR(display_, fbo.egl_image);
+  }
+  CHECK_GL();
+}
+
+void Blur::StartFrame() {
+  fbo_q_free_.Clear();
+  for (const auto& fbo : fbo_q_)
+    fbo_q_free_.Append(fbo);
+}
+
+GLuint Blur::DrawBlur(GLuint source_texture) {
+  LOG_ALWAYS_FATAL_IF(fbo_q_free_.GetSize() < 2);
+
+  // Downsample to half w x half h.
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, source_fbo_.fbo);
+  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_half_.fbo);
+  glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                         target_texture_target_, source_texture, 0);
+  glBlitFramebuffer(0, 0, width_, height_, 0, 0, width_ / 2, height_ / 2,
+                    GL_COLOR_BUFFER_BIT, GL_LINEAR);
+  CHECK_GL();
+
+  // Downsample to quarter w x quarter h.
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_half_.fbo);
+  Fbo fbo_out = fbo_q_free_.Front();
+  fbo_q_free_.PopFront();
+  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_out.fbo);
+  glBlitFramebuffer(0, 0, width_ / 2, height_ / 2, 0, 0, width_ / 4,
+                    height_ / 4, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+  CHECK_GL();
+
+  // Blur shader is initialized statically to share between multiple blur
+  // instances.
+  static ShaderProgram kawase_prog[2];
+  int prog_index = (target_texture_target_ == GL_TEXTURE_EXTERNAL_OES) ? 1 : 0;
+  if (!kawase_prog[prog_index].IsUsable()) {
+    std::string prefix = "#version 310 es\n";
+    if (target_texture_target_ == GL_TEXTURE_EXTERNAL_OES) {
+      prefix += "#extension GL_OES_EGL_image_external_essl3 : require\n";
+      prefix += "#define APP_SAMPLER_2D samplerExternalOES\n";
+    } else {
+      prefix += "#define APP_SAMPLER_2D sampler2D\n";
+    }
+    std::string vert = prefix + screen_space_vert_shader;
+    std::string frag = prefix + kawase_blur_frag_shader;
+    kawase_prog[prog_index].Link(vert, frag);
+    CHECK_GL();
+  }
+
+  int blur_w = width_ / 4;
+  int blur_h = height_ / 4;
+  float pix_w = 1.0f / static_cast<float>(blur_w);
+  float pix_h = 1.0f / static_cast<float>(blur_h);
+  vec2 pixel_size(pix_w, pix_h);
+  constexpr int num_passes = sizeof(g_blur_samples) / sizeof(g_blur_samples[0]);
+  vec2 blur_offsets[num_passes][g_num_samples];
+  for (int i = 0; i < num_passes; ++i) {
+    for (int dir = 0; dir < g_num_samples; ++dir) {
+      blur_offsets[i][dir] = pixel_size.array() *
+          g_blur_samples[i][dir].array() * scale_;
+    }
+  }
+
+  kawase_prog[prog_index].Use();
+
+  vec4 screen_tri_strip[4] = {vec4(-1, 1, 0, 1), vec4(-1, -1, 0, 0),
+                              vec4(1, 1, 1, 1), vec4(1, -1, 1, 0)};
+
+  glViewport(0, 0, blur_w, blur_h);
+  glVertexAttribPointer(POSITION_ATTR, 4, GL_FLOAT, GL_FALSE, sizeof(vec4),
+                        screen_tri_strip);
+  glEnableVertexAttribArray(POSITION_ATTR);
+  CHECK_GL();
+
+  // Ping-pong between fbos from fbo_q_free_ to compute the passes.
+  Fbo fbo_in = fbo_out;
+  for (int i = 0; i < num_passes; ++i) {
+    fbo_out = fbo_q_free_.Front();
+    fbo_q_free_.PopFront();
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_out.fbo);
+    glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING);
+    glBindTexture(target_texture_target_, fbo_in.texture);
+    glUniform2fv(OFFSET_BINDING, 4, &blur_offsets[i][0][0]);
+    glClear(GL_COLOR_BUFFER_BIT);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    CHECK_GL();
+    // Put fbo_in back into the free fbo pool.
+    fbo_q_free_.Append(fbo_in);
+    // Next iteration's in buffer is this iteration's out buffer.
+    fbo_in = fbo_out;
+  }
+  glDisableVertexAttribArray(POSITION_ATTR);
+  glBindTexture(target_texture_target_, 0);
+  glUseProgram(0);
+  glActiveTexture(GL_TEXTURE0);
+  CHECK_GL();
+  // fbo_out remains out of the fbo_q_free_ list, since the application will be
+  // using it as a texture.
+  return fbo_out.texture;
+}
+
+Blur::Fbo Blur::CreateFbo(int w, int h, GLuint source_texture, GLint tex_target,
+                          bool is_external) {
+  Fbo fbo;
+  glGenFramebuffers(1, &fbo.fbo);
+  if (source_texture) {
+    fbo.texture = source_texture;
+  } else {
+    glGenTextures(1, &fbo.texture);
+  }
+
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
+  CHECK_GL();
+
+  if (!source_texture) {
+    glBindTexture(tex_target, fbo.texture);
+    glTexParameteri(tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    if (is_external) {
+      fbo.egl_image =
+          CreateEglImage(display_, w, h, HAL_PIXEL_FORMAT_RGBA_8888,
+                         GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER);
+      glEGLImageTargetTexture2DOES(tex_target, fbo.egl_image);
+    } else {
+      glTexImage2D(tex_target, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                   nullptr);
+    }
+  }
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_target,
+                         fbo.texture, 0);
+  CHECK_GL();
+  CHECK_GL_FBO();
+
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  return fbo;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/debug_text.cpp b/libs/vr/libdvrgraphics/debug_text.cpp
new file mode 100644
index 0000000..1875b14
--- /dev/null
+++ b/libs/vr/libdvrgraphics/debug_text.cpp
@@ -0,0 +1,186 @@
+#include "include/private/dvr/graphics/debug_text.h"
+
+#include <algorithm>
+
+#include <private/dvr/debug.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+// 658x11 alpha texture with ascii characters starting with !: "!"#$%&'("...
+// Each character is 7x11 pixels, monospace.
+// clang-format off
+const uint8_t ascii_texture[] = {
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0x92,0x49,0x92,0x00,0x00,0x24,0xdb,0xff,0xff,0xdb,0x00,0x49,0xff,0x49,0x00,0x24,0xb6,0x00,0x00,0x6d,0xff,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0xff,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0x24,0xb6,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x6d,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0x24,0xdb,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0x6d,0x00,0x00,0x00,0x6d,0xff,0xff,0x92,0x00,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x24,0xb6,0xff,0xff,0xb6,0x00,0x00,0xff,0xff,0xdb,0x6d,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x24,0xb6,0xff,0xff,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x6d,0x6d,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x49,0xff,0x00,0x00,0xff,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x24,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x24,0xb6,0xff,0xff,0xdb,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x92,0x6d,0x00,0x00,0xdb,0x6d,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0xff,0x24,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xb6,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x24,0xb6,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0xff,0x49,0x00,0x49,0xff,0x00,0x00,0xb6,0x6d,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x92,0x00,0x24,0xff,0x00,0x00,0x24,0xdb,0x24,0x00,0xdb,0x6d,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x49,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xb6,0x00,0x92,0xff,0x00,0x00,0xff,0xdb,0x00,0x00,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x49,0xff,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xdb,0x24,0x00,0x00,0xdb,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0x6d,0x92,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0xb6,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x49,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x24,0xb6,0x00,0x00,0x00,0xb6,0x49,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0x92,0xb6,0x00,0x00,0x00,0x00,0xdb,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x24,0xb6,0xb6,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x24,0xb6,0xb6,0x24,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0xb6,0x6d,0x6d,0xff,0x92,0xdb,0x00,0x00,0x00,0xb6,0x92,0xb6,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x6d,0x6d,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xb6,0x49,0xb6,0xff,0x00,0x00,0xff,0xb6,0x49,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x92,0x00,0x92,0x6d,0x00,0x00,0x92,0x24,0x00,0x00,0xdb,0x00,0x00,0x00,0xb6,0x6d,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0xff,0xff,0xdb,0x49,0x00,0x00,0xff,0x6d,0xff,0xdb,0x24,0x00,0x00,0x00,0x92,0xff,0xff,0xdb,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xb6,0xff,0xdb,0x24,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x49,0xff,0xdb,0xb6,0xff,0x00,0x00,0xff,0x00,0x92,0xb6,0x24,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xb6,0x6d,0xdb,0x49,0x00,0x00,0xff,0x00,0x92,0xb6,0x24,0x00,0x00,0x00,0xb6,0xff,0xb6,0x00,0x00,0x00,0xff,0x6d,0xff,0xdb,0x24,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xff,0x49,0xff,0xdb,0x00,0x00,0x49,0xdb,0xff,0xff,0xdb,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x24,0xdb,0xff,0x49,0x00,0x00,0x49,0xff,0x49,0xb6,0x24,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0x49,0xff,0x49,0xb6,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0xb6,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xdb,0x24,0x00,0x00,0xff,0x92,0xff,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xdb,0xff,0xdb,0x00,0x00,0x00,0xdb,0x6d,0x00,0x92,0xff,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xff,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0xdb,0x00,0xdb,0xdb,0x49,0xff,0x00,0x00,0x00,0xff,0x24,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x24,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x6d,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x6d,0xff,0x6d,0xff,0x00,0x00,0xff,0x24,0xdb,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0x92,0x00,0x00,0x00,0x24,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0xdb,0x24,0x00,0x00,0x6d,0x6d,0xff,0x24,0xb6,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x92,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0xff,0x92,0x00,0x49,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x24,0xff,0x00,0x00,0x00,0xff,0xb6,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x49,0xff,0x49,0xdb,0x00,0x00,0xff,0xb6,0x00,0x49,0xdb,0x00,0x00,0xb6,0x92,0x00,0x92,0xb6,0x00,0x00,0xff,0x6d,0x00,0x92,0xb6,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0xb6,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x6d,0x00,0x6d,0x92,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0x00,0xdb,0x24,0xdb,0x00,0x00,0x00,0x6d,0x6d,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xb6,0x24,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0xdb,0x49,0x00,0x00,0x00,0x24,0xb6,0x49,0xff,0x49,0x49,0xdb,0x49,0xdb,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0xff,0x00,0xb6,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x24,0x92,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0xff,0x92,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0xb6,0x6d,0x00,0x6d,0xb6,0x00,0x00,0x24,0xdb,0xff,0x92,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0xb6,0x24,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x24,0xb6,0xdb,0x49,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0xff,0x00,0xff,0xdb,0x24,0xff,0x00,0x00,0x24,0xdb,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x92,0xb6,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x6d,0xb6,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0xff,0x24,0xff,0x00,0x00,0xff,0x00,0xb6,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x24,0xff,0x00,0x00,0x00,0x49,0xb6,0xff,0x6d,0x92,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x92,0xdb,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x24,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0xdb,0x24,0x00,0x00,0xb6,0x49,0x92,0x49,0xb6,0x00,0x00,0x00,0x49,0xff,0x49,0x00,0x00,0x00,0x00,0xdb,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x49,0xb6,0x92,0x24,0x00,0x92,0x00
+  ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0xb6,0x24,0xb6,0x24,0xb6,0xff,0x00,0x00,0x24,0xb6,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0xff,0x6d,0xb6,0x00,0x00,0x6d,0xff,0xff,0xff,0x6d,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x49,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x49,0xb6,0xff,0x00,0x00,0xdb,0x24,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xb6,0xb6,0x00,0x00,0x00,0x00,0xdb,0xdb,0xdb,0x6d,0x00,0x00,0x00,0xb6,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x92,0x24,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x92,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x6d,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x49,0xdb,0x00,0x00,0x00,0x92,0x92,0xff,0xdb,0x92,0x00,0x00,0x00,0x49,0xff,0x49,0x00,0x00,0x00,0x00,0xb6,0x49,0xdb,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x00,0x92,0x00,0x24,0x92,0xb6,0x49,0x00
+  ,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0xb6,0x49,0x00,0x00,0x00,0x00,0x00,0xff,0x6d,0xdb,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0xb6,0xdb,0x6d,0x00,0x24,0xdb,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x49,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x6d,0xb6,0x00,0x00,0x6d,0x92,0x00,0x24,0xdb,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0x24,0xdb,0x49,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xdb,0x49,0xb6,0xdb,0xdb,0x00,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0xff,0x00,0x00,0x92,0xdb,0x00,0x00,0xdb,0x6d,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x49,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x24,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xdb,0x00,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0xdb,0xff,0x00,0x00,0x92,0x6d,0x00,0x6d,0x92,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x6d,0xb6,0x00,0x00,0xff,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x6d,0xdb,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xdb,0x6d,0x00,0x6d,0xdb,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x00,0xff,0x92,0xff,0x49,0x00,0x00,0x24,0xb6,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x92,0xff,0x00,0x00,0xff,0x92,0x00,0x49,0xb6,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x92,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xb6,0x49,0x00,0x6d,0xb6,0x00,0x00,0xff,0x6d,0x00,0x92,0xb6,0x00,0x00,0xb6,0x92,0x00,0x6d,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0xff,0x49,0x00,0xb6,0xff,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x6d,0xff,0xb6,0xff,0x49,0x00,0x00,0x00,0xdb,0x24,0xdb,0x00,0x00,0x00,0x00,0x6d,0xff,0x6d,0x00,0x00,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0xb6,0x24,0x00,0x49,0xff,0x49,0x24,0xb6,0xff,0xdb,0x49,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xdb,0x00,0x00,0x00,0x00,0xdb,0xdb,0xff,0x49,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0xff,0xff,0xff,0x6d,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x00,0x00,0x00,0x00,0x49,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xdb,0x00,0x00,0xff,0xff,0xff,0xb6,0x00,0x00,0x00,0x24,0xb6,0xff,0xff,0xb6,0x00,0x00,0xff,0xff,0xdb,0x6d,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0x92,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x00,0xdb,0xff,0xdb,0x49,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x49,0x92,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x6d,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0xdb,0xff,0x24,0x00,0x00,0xff,0x00,0x00,0x00,0x6d,0x92,0x00,0xff,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x24,0xdb,0xff,0xdb,0x24,0x00,0x00,0x00,0x24,0xff,0x24,0x00,0x00,0x00,0x00,0xdb,0x49,0xff,0x49,0x00,0x00,0xb6,0x24,0x00,0x24,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0xdb,0x6d,0xff,0x00,0x00,0xff,0x6d,0xff,0xdb,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x92,0xff,0xff,0xff,0xb6,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xb6,0xdb,0xb6,0x00,0x00,0x00,0xff,0x6d,0xff,0xdb,0x00,0x00,0x00,0x24,0xdb,0xff,0x6d,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xdb,0x49,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0x00,0x00,0x49,0xb6,0x92,0x24,0xff,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0xff,0x24,0x00,0x00,0xb6,0x49,0x00,0x49,0xb6,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x49,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x92,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x24,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x24,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+  ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x00,0x92,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x24,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0xff,0xb6,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdb,0xdb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};
+// clang-format on
+
+constexpr char kTextureFirstChar = '!';
+constexpr char kTextureLastChar = '~';
+constexpr int kTextureTotalChars = kTextureLastChar - kTextureFirstChar + 1;
+constexpr int kCharWidth = 7;
+constexpr int kCharHeight = 11;
+constexpr int kTextureWidth = kTextureTotalChars * kCharWidth;
+constexpr int kTextureHeight = kCharHeight;
+
+std::string vert_shader = SHADER0([]() {  // NOLINT
+  layout(location = 0) in vec2 position;
+  layout(location = 1) in vec2 uv;
+
+  out vec2 texCoords;
+
+  void main() {
+    gl_Position = vec4(position, 0.0, 1.0f);
+    texCoords = vec2(uv.x, 1.0 - uv.y);
+  }
+});
+
+// Uniform locations used in the following shader
+#define UNIFORM_LOCATION_DIGITS 0
+#define UNIFORM_LOCATION_COLOR 1
+
+std::string frag_shader = SHADER0([]() {  // NOLINT
+  precision mediump float;
+
+  out vec4 color;
+  in vec2 texCoords;
+  layout(location = 0) uniform sampler2D digitsTexture;
+  layout(location = 1) uniform vec4 mixColor;
+
+  void main() {
+    float alpha = texture(digitsTexture, texCoords).r;
+    color = vec4(mixColor.rgb, alpha * mixColor.a);
+  }
+});
+
+}  // anonymous namespace
+
+void DebugText::SetViewportSize(int viewport_width, int viewport_height) {
+  pixel_size_screen_space_ = vec2(2.0f / static_cast<float>(viewport_width),
+                                  2.0f / static_cast<float>(viewport_height));
+}
+
+DebugText::DebugText(int max_digits, int viewport_width, int viewport_height) {
+  max_digits_ = max_digits;
+  SetViewportSize(viewport_width, viewport_height);
+
+  shader_.Link(vert_shader, frag_shader);
+  shader_.Use();
+  glUniform1i(UNIFORM_LOCATION_DIGITS, 0);
+
+  // Num quads * 6 vertices per quad.
+  mesh_.SetVertices(max_digits * 6, nullptr, GL_TRIANGLES, GL_DYNAMIC_DRAW);
+
+  glGenTextures(1, &texture_);
+  glBindTexture(GL_TEXTURE_2D, texture_);
+  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, kTextureWidth, kTextureHeight, 0,
+               GL_RED, GL_UNSIGNED_BYTE, ascii_texture);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  glBindTexture(GL_TEXTURE_2D, 0);
+  CHECK_GL();
+}
+
+DebugText::~DebugText() {}
+
+void DebugText::Draw(float x, float y, float scale, float r, float g, float b,
+                     float a, const char* str, float stereo_offset,
+                     uint8_t axis) {
+  assert(axis < 2);
+  shader_.Use();
+  glUniform4f(UNIFORM_LOCATION_COLOR, r, g, b, a);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, texture_);
+  CHECK_GL();
+
+  float px = x;
+  float py = y;
+  float x_advance = scale * static_cast<float>(kCharWidth);
+  float y_advance = 0.0f;
+  float x_height = 0.0f;
+  float y_height = scale * static_cast<float>(kCharHeight);
+  if (axis) {
+    std::swap(x_advance, y_advance);
+    std::swap(x_height, y_height);
+  }
+  x_advance *= pixel_size_screen_space_[0];
+  x_height *= pixel_size_screen_space_[0];
+  y_advance *= pixel_size_screen_space_[1];
+  y_height *= pixel_size_screen_space_[1];
+  int max_digits = stereo_offset != 0.0f ? max_digits_ / 2 : max_digits_;
+  int len = std::min(max_digits, static_cast<int>(strlen(str)));
+
+  int num_quads = stereo_offset != 0.0f ? len * 2 : len;
+  std::tuple<vec2, vec2>* vbo =
+      mesh_.Map(GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT, num_quads * 6);
+
+  int v = 0;
+  for (int i = 0; i < len; ++i) {
+    char digit = str[i];
+    if (digit == '\n') {
+      if (axis == 0) {
+        py += y_height;
+        px = x;
+      } else {
+        px -= x_height;
+        py = y;
+      }
+      continue;
+    }
+    if (digit < kTextureFirstChar || digit > kTextureLastChar) {
+      px += x_advance;
+      py += y_advance;
+      continue;
+    }
+
+    int tex_digit = digit - kTextureFirstChar;
+
+    // Add screenspace tri vertices in CCW order starting with bottom left.
+    float tx =
+        static_cast<float>(tex_digit) / static_cast<float>(kTextureTotalChars);
+    float tx2 = tx + 1.0f / static_cast<float>(kTextureTotalChars);
+    vbo[v * 6 + 0] = {vec2(px, py), vec2(tx, 0.0f)};
+    vbo[v * 6 + 1] = {vec2(px + x_advance, py + y_advance), vec2(tx2, 0.0f)};
+    vbo[v * 6 + 2] = {
+        vec2(px + x_advance + x_height, py + y_advance + y_height),
+        vec2(tx2, 1.0f)};
+    vbo[v * 6 + 3] = vbo[v * 6 + 0];
+    vbo[v * 6 + 4] = vbo[v * 6 + 2];
+    vbo[v * 6 + 5] = {vec2(px + x_height, py + y_height), vec2(tx, 1.0f)};
+    px += x_advance;
+    py += y_advance;
+    ++v;
+  }
+
+  if (stereo_offset != 0.0f) {
+    int num_chars = v;
+    for (int i = 0; i < num_chars; ++i) {
+      for (int j = 0; j < 6; ++j) {
+        vbo[v * 6 + j] = vbo[i * 6 + j];
+        // The 0th tuple element is the vertex position vec2.
+        std::get<0>(vbo[v * 6 + j])[axis] += stereo_offset;
+      }
+      ++v;
+    }
+  }
+
+  mesh_.Unmap();
+
+  mesh_.Draw(v * 6);
+  glBindTexture(GL_TEXTURE_2D, texture_);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/egl_image.cpp b/libs/vr/libdvrgraphics/egl_image.cpp
new file mode 100644
index 0000000..26d68cd
--- /dev/null
+++ b/libs/vr/libdvrgraphics/egl_image.cpp
@@ -0,0 +1,22 @@
+#include "include/private/dvr/graphics/egl_image.h"
+
+#include <hardware/gralloc.h>
+
+#include <memory>
+
+#include <private/dvr/native_buffer.h>
+
+namespace android {
+namespace dvr {
+
+EGLImageKHR CreateEglImage(EGLDisplay dpy, int width, int height, int format,
+                           int usage) {
+  auto image = std::make_shared<IonBuffer>(width, height, format, usage);
+
+  return eglCreateImageKHR(
+      dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(new NativeBuffer(image)), nullptr);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/gpu_profiler.cpp b/libs/vr/libdvrgraphics/gpu_profiler.cpp
new file mode 100644
index 0000000..49c515f
--- /dev/null
+++ b/libs/vr/libdvrgraphics/gpu_profiler.cpp
@@ -0,0 +1,248 @@
+#include "include/private/dvr/graphics/gpu_profiler.h"
+
+#include <log/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..2d36600
--- /dev/null
+++ b/libs/vr/libdvrgraphics/shader_program.cpp
@@ -0,0 +1,163 @@
+#include "include/private/dvr/graphics/shader_program.h"
+
+#include <regex>
+#include <sstream>
+
+#include <log/log.h>
+
+namespace {
+
+static bool CompileShader(GLuint shader, const std::string& shader_string) {
+  std::string prefix;
+  const std::string kVersion = "#version";
+  if (shader_string.substr(0, kVersion.size()) != kVersion) {
+    prefix = "#version 310 es\n";
+  }
+  std::string string_with_prefix = prefix + shader_string;
+  const char* shader_str[] = {string_with_prefix.data()};
+  glShaderSource(shader, 1, shader_str, nullptr);
+  glCompileShader(shader);
+
+  GLint success;
+  glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
+  if (!success) {
+    GLchar infoLog[512];
+    glGetShaderInfoLog(shader, 512, nullptr, infoLog);
+    ALOGE("Shader Failed to compile: %s -- %s", *shader_str, infoLog);
+    return false;
+  }
+  return true;
+}
+
+static bool LinkProgram(GLuint program, GLuint vertex_shader,
+                        GLuint fragment_shader) {
+  glAttachShader(program, vertex_shader);
+  glAttachShader(program, fragment_shader);
+  glLinkProgram(program);
+
+  // Check for linking errors
+  GLint success;
+  glGetProgramiv(program, GL_LINK_STATUS, &success);
+  if (!success) {
+    GLchar infoLog[512];
+    glGetProgramInfoLog(program, 512, nullptr, infoLog);
+    ALOGE("Shader failed to link: %s", infoLog);
+    return false;
+  }
+
+  return true;
+}
+
+static bool LinkProgram(GLuint program, GLuint compute_shader) {
+  glAttachShader(program, compute_shader);
+  glLinkProgram(program);
+
+  // Check for linking errors
+  GLint success;
+  glGetProgramiv(program, GL_LINK_STATUS, &success);
+  if (!success) {
+    GLchar infoLog[512];
+    glGetProgramInfoLog(program, 512, nullptr, infoLog);
+    ALOGE("Shader failed to link: %s", infoLog);
+    return false;
+  }
+
+  return true;
+}
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+ShaderProgram::ShaderProgram() : program_(0) {}
+
+ShaderProgram::ShaderProgram(const std::string& vertext_source,
+                             const std::string& fragment_source)
+    : program_(0) {
+  Link(vertext_source, fragment_source);
+}
+
+ShaderProgram::ShaderProgram(ShaderProgram&& to_move) {
+  std::swap(program_, to_move.program_);
+}
+
+ShaderProgram::~ShaderProgram() {
+  if (program_)
+    glDeleteProgram(program_);
+}
+
+ShaderProgram& ShaderProgram::operator=(ShaderProgram&& to_move) {
+  std::swap(program_, to_move.program_);
+  return *this;
+}
+
+void ShaderProgram::Link(const std::string& vertext_source,
+                         const std::string& fragment_source) {
+  if (program_)
+    glDeleteProgram(program_);
+  program_ = glCreateProgram();
+  GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+  GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+
+  bool success = CompileShader(vertex_shader, vertext_source) &&
+                 CompileShader(fragment_shader, fragment_source) &&
+                 LinkProgram(program_, vertex_shader, fragment_shader);
+
+  glDeleteShader(vertex_shader);
+  glDeleteShader(fragment_shader);
+
+  if (!success) {
+    glDeleteProgram(program_);
+    program_ = 0;
+  }
+}
+
+void ShaderProgram::Link(const std::string& compute_source) {
+  if (program_)
+    glDeleteProgram(program_);
+  program_ = glCreateProgram();
+  GLuint shader = glCreateShader(GL_COMPUTE_SHADER);
+
+  bool success =
+      CompileShader(shader, compute_source) && LinkProgram(program_, shader);
+
+  glDeleteShader(shader);
+
+  if (!success) {
+    glDeleteProgram(program_);
+    program_ = 0;
+  }
+}
+
+void ShaderProgram::Use() const { glUseProgram(program_); }
+
+std::string ComposeShader(const std::string& shader_code,
+                          const std::vector<std::string>& variables) {
+  std::stringstream result_stream;
+  std::regex expression("%([0-9]*)");
+  using reg_iter = std::regex_token_iterator<std::string::const_iterator>;
+  reg_iter rend;
+  // match the string and number (drop the %)
+  std::vector<int> submatches = {-1, 1};
+  reg_iter reg(shader_code.begin(), shader_code.end(), expression, submatches);
+  bool is_even = true;
+  while (reg != rend) {
+    if (is_even) {
+      // even entries is the code between the %n's
+      result_stream << *reg;
+    } else {
+      // odd entries are the index into the passed in variables.
+      size_t i = static_cast<size_t>(std::stoi(*reg));
+      if (i < variables.size()) {
+        result_stream << variables[i];
+      }
+    }
+    is_even = !is_even;
+    ++reg;
+  }
+  return result_stream.str();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/timer_query.cpp b/libs/vr/libdvrgraphics/timer_query.cpp
new file mode 100644
index 0000000..23d2b7c
--- /dev/null
+++ b/libs/vr/libdvrgraphics/timer_query.cpp
@@ -0,0 +1,65 @@
+#include "include/private/dvr/graphics/timer_query.h"
+
+#include <GLES2/gl2ext.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+TimerQuery::TimerQuery() {}
+
+TimerQuery::~TimerQuery() { Delete(); }
+
+void TimerQuery::Init() { glGenQueriesEXT(1, &query_); }
+
+void TimerQuery::Delete() {
+  if (query_) {
+    glDeleteQueriesEXT(1, &query_);
+    query_ = 0;
+  }
+}
+
+void TimerQuery::Begin() {
+  if (query_ == 0) {
+    Init();
+  }
+  glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query_);
+}
+
+void TimerQuery::End() { glEndQueryEXT(GL_TIME_ELAPSED_EXT); }
+
+double TimerQuery::GetTimeInMS() {
+  GLuint64 elapsed_time = 0;
+  glGetQueryObjectui64vEXT(query_, GL_QUERY_RESULT, &elapsed_time);
+  return static_cast<double>(elapsed_time) / 1000000.0;
+}
+
+SyncTimerQuery::SyncTimerQuery() { timer_.Begin(); }
+
+double SyncTimerQuery::FlushAndGetTimeInMS() {
+  if (timer_.query_ == 0) {
+    ALOGE("Error: Only call FlushAndGetTimeInMS() once.");
+    return 0.0;
+  }
+  timer_.End();
+  glFlush();
+  GLint done = 0;
+  while (!done) {
+    glGetQueryObjectivEXT(timer_.query_, GL_QUERY_RESULT_AVAILABLE, &done);
+  }
+
+  GLint disjoint_occurred = 0;
+  glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint_occurred);
+  if (disjoint_occurred) {
+    ALOGE("Disjoint occurred.");
+    timer_.Delete();
+    return 0.0;
+  }
+
+  double elapsed_time = timer_.GetTimeInMS();
+  timer_.Delete();
+  return elapsed_time;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvrgraphics/vr_gl_extensions.cpp b/libs/vr/libdvrgraphics/vr_gl_extensions.cpp
new file mode 100644
index 0000000..2c5a698
--- /dev/null
+++ b/libs/vr/libdvrgraphics/vr_gl_extensions.cpp
@@ -0,0 +1,44 @@
+#include "include/private/dvr/graphics/vr_gl_extensions.h"
+
+PFNGLGETQUERYOBJECTI64VEXTPROC glGetQueryObjecti64v = NULL;
+PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectiv = NULL;
+PFNGLQUERYCOUNTEREXTPROC glQueryCounter = NULL;
+PFNGLBUFFERSTORAGEEXTPROC glBufferStorage = NULL;
+PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR glFramebufferTextureMultiview = NULL;
+PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR
+glFramebufferTextureMultisampleMultiview = NULL;
+
+PFNGLSHAREDBUFFERCREATEQCOM glCreateSharedBufferQCOM = NULL;
+PFNGLSHAREDBUFFERDESTROYQCOM glDestroySharedBufferQCOM = NULL;
+PFNGLSHAREDBUFFERBINDQCOM glBindSharedBufferQCOM = NULL;
+PFNGLGRALLOCBUFFERDATAQCOM glGrallocBufferDataQCOM = NULL;
+
+extern "C" void load_gl_extensions() {
+  if (glGetQueryObjecti64v) {
+    return;
+  }
+  glGetQueryObjecti64v = reinterpret_cast<PFNGLGETQUERYOBJECTI64VEXTPROC>(
+      eglGetProcAddress("glGetQueryObjecti64vEXT"));
+  glGetQueryObjectiv = reinterpret_cast<PFNGLGETQUERYOBJECTIVEXTPROC>(
+      eglGetProcAddress("glGetQueryObjectivEXT"));
+  glQueryCounter = reinterpret_cast<PFNGLQUERYCOUNTEREXTPROC>(
+      eglGetProcAddress("glQueryCounterEXT"));
+  glBufferStorage = reinterpret_cast<PFNGLBUFFERSTORAGEEXTPROC>(
+      eglGetProcAddress("glBufferStorageEXT"));
+
+  glFramebufferTextureMultiview =
+      reinterpret_cast<PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVR>(
+          eglGetProcAddress("glFramebufferTextureMultiviewOVR"));
+  glFramebufferTextureMultisampleMultiview =
+      reinterpret_cast<PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVR>(
+          eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"));
+
+  glGrallocBufferDataQCOM = reinterpret_cast<PFNGLGRALLOCBUFFERDATAQCOM>(
+      eglGetProcAddress("glGrallocBufferDataQCOM"));
+  glCreateSharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERCREATEQCOM>(
+      eglGetProcAddress("glCreateSharedBufferQCOM"));
+  glBindSharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERBINDQCOM>(
+      eglGetProcAddress("glBindSharedBufferQCOM"));
+  glDestroySharedBufferQCOM = reinterpret_cast<PFNGLSHAREDBUFFERDESTROYQCOM>(
+      eglGetProcAddress("glDestroySharedBufferQCOM"));
+}
diff --git a/libs/vr/libeds/Android.mk b/libs/vr/libeds/Android.mk
new file mode 100644
index 0000000..d2e6526
--- /dev/null
+++ b/libs/vr/libeds/Android.mk
@@ -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.
+
+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 := \
+	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..d6bf164
--- /dev/null
+++ b/libs/vr/libeds/composite_hmd.cpp
@@ -0,0 +1,257 @@
+#include "include/private/dvr/composite_hmd.h"
+
+#include <log/log.h>
+
+#include <private/dvr/numeric.h>
+
+namespace android {
+namespace dvr {
+
+CompositeHmd::CompositeHmd(const HeadMountMetrics& head_mount_metrics,
+                           const DisplayMetrics& display_metrics)
+    : head_mount_metrics_(head_mount_metrics),
+      display_metrics_(display_metrics) {
+  MetricsChanged();
+}
+
+float CompositeHmd::GetTargetFrameDuration() const {
+  return display_metrics_.GetFrameDurationSeconds();
+}
+
+vec2 CompositeHmd::ComputeDistortedPoint(EyeType eye, vec2 position,
+                                         RgbColorChannel channel) const {
+  position = TransformPoint(eye_tan_angle_from_norm_screen_matrix_[eye], position);
+  vec2 distorted =
+      head_mount_metrics_.GetColorChannelDistortion(channel).Distort(position);
+  return TransformPoint(eye_norm_texture_from_tan_angle_matrix_[eye], distorted);
+}
+
+vec2 CompositeHmd::ComputeInverseDistortedPoint(EyeType eye, vec2 position,
+                                                RgbColorChannel channel) const {
+  position = TransformPoint(eye_norm_texture_from_tan_angle_inv_matrix_[eye], position);
+  vec2 distorted =
+      head_mount_metrics_.GetColorChannelDistortion(channel).DistortInverse(
+          position);
+  return TransformPoint(eye_tan_angle_from_norm_screen_inv_matrix_[eye], distorted);
+}
+
+void CompositeHmd::ComputeDistortedVertex(EyeType eye, vec2 uv_in,
+                                          vec2* vertex_out,
+                                          vec2* uv_out) const {
+  // The mesh vertices holds the shape of the distortion.
+  vec2 vertex_position = ComputeInverseDistortedPoint(eye, uv_in, kRed);
+  *vertex_out = vec2(vertex_position.x() - 0.5f, vertex_position.y() - 0.5f);
+
+  if (uv_out) {
+    // Compute the texture coordinate for each vertex coordinate.
+    // Red's is the inverse of the inverse, skip the calculation and use uv_in.
+    uv_out[kRed] = uv_in;
+    uv_out[kGreen] = ComputeDistortedPoint(eye, vertex_position, kGreen);
+    uv_out[kBlue] = ComputeDistortedPoint(eye, vertex_position, kBlue);
+  }
+}
+
+vec2i CompositeHmd::GetRecommendedRenderTargetSize() const {
+  return recommended_render_target_size_;
+}
+
+Range2i CompositeHmd::GetDisplayRange() const { return display_range_; }
+
+mat4 CompositeHmd::GetEyeFromHeadMatrix(EyeType eye) const {
+  return eye_from_head_matrix_[eye];
+}
+
+FieldOfView CompositeHmd::GetEyeFov(EyeType eye) const { return eye_fov_[eye]; }
+
+Range2i CompositeHmd::GetEyeViewportBounds(EyeType eye) const {
+  return eye_viewport_range_[eye];
+}
+
+void CompositeHmd::SetHeadMountMetrics(
+    const HeadMountMetrics& head_mount_metrics) {
+  // Use the assignement operator to do memberwise copy.
+  head_mount_metrics_ = head_mount_metrics;
+  MetricsChanged();
+}
+
+const HeadMountMetrics& CompositeHmd::GetHeadMountMetrics() const {
+  return head_mount_metrics_;
+}
+
+void CompositeHmd::SetDisplayMetrics(const DisplayMetrics& display_metrics) {
+  // Use the assignment operator to do memberwise copy.
+  display_metrics_ = display_metrics;
+  MetricsChanged();
+}
+
+const DisplayMetrics& CompositeHmd::GetDisplayMetrics() const {
+  return display_metrics_;
+}
+
+void CompositeHmd::MetricsChanged() {
+  // Abbreviations in variable names:
+  //   "vp": viewport
+  //   "ta": tan-angle
+  const HeadMountMetrics& mount = head_mount_metrics_;
+  DisplayMetrics display = display_metrics_;
+
+  if (display.IsPortrait()) {
+    // If we're in portrait mode, toggle the orientation so that all
+    // calculations are done in landscape mode.
+    display.ToggleOrientation();
+  }
+
+  float display_width_meters = display.GetSizeMeters()[0];
+  float display_height_meters = display.GetSizeMeters()[1];
+
+  vec2 pixels_per_meter = vec2(1.0f / display.GetMetersPerPixel()[0],
+                               1.0f / display.GetMetersPerPixel()[1]);
+
+  // virtual_eye_to_screen_dist is the distance from the screen to the eye
+  // after it has been projected through the lens.  This would normally be
+  // slightly different from the distance to the actual eye.
+  float virtual_eye_to_screen_dist = mount.GetVirtualEyeToScreenDistance();
+  float meters_per_tan_angle = virtual_eye_to_screen_dist;
+  vec2 pixels_per_tan_angle = pixels_per_meter * meters_per_tan_angle;
+
+  LOG_ALWAYS_FATAL_IF(0.0f == display_width_meters);
+  LOG_ALWAYS_FATAL_IF(0.0f == display_height_meters);
+  LOG_ALWAYS_FATAL_IF(0.0f == virtual_eye_to_screen_dist);
+
+  // Height of lenses from the bottom of the screen.
+  float lens_y_center = 0;
+  float bottom_dist = 0;
+  float top_dist = 0;
+
+  // bottom_display_dist and top_display_dist represent the distance from the
+  // lens center to the edge of the display.
+  float bottom_display_dist = 0;
+  float top_display_dist = 0;
+  switch (mount.GetVerticalAlignment()) {
+    case HeadMountMetrics::kBottom:
+      lens_y_center =
+          mount.GetTrayToLensDistance() - display.GetBorderSizeMeters();
+      bottom_dist = lens_y_center;
+      top_dist = lens_y_center;
+      bottom_display_dist = lens_y_center;
+      top_display_dist = display_height_meters - lens_y_center;
+      break;
+    case HeadMountMetrics::kCenter:
+      // TODO(hendrikw): This should respect the border size, but since we
+      //                 currently hard code the border size, it would break
+      //                 the distortion on some devices.  Revisit when border
+      //                 size is fixed.
+      lens_y_center = display_height_meters * 0.5f;
+      bottom_dist = lens_y_center;
+      top_dist = lens_y_center;
+      bottom_display_dist = lens_y_center;
+      top_display_dist = lens_y_center;
+      break;
+    case HeadMountMetrics::kTop:
+      lens_y_center = display_height_meters - (mount.GetTrayToLensDistance() -
+                                               display.GetBorderSizeMeters());
+      bottom_dist =
+          mount.GetTrayToLensDistance() - display.GetBorderSizeMeters();
+      top_dist = bottom_dist;
+      bottom_display_dist = lens_y_center;
+      top_display_dist = display_height_meters - lens_y_center;
+      break;
+  }
+
+  float inner_dist = mount.GetScreenCenterToLensDistance();
+  float outer_dist = display_width_meters * 0.5f - inner_dist;
+
+  // We don't take chromatic aberration into account yet for computing FOV,
+  // viewport, etc, so we only use the green channel for now. Note the actual
+  // Distort function *does* implement chromatic aberration.
+  const ColorChannelDistortion& distortion =
+      mount.GetColorChannelDistortion(kGreen);
+
+  vec2 outer_point(outer_dist / virtual_eye_to_screen_dist, 0.0f);
+  vec2 inner_point(inner_dist / virtual_eye_to_screen_dist, 0.0f);
+  vec2 bottom_point(0.0f, bottom_dist / virtual_eye_to_screen_dist);
+  vec2 top_point(0.0f, top_dist / virtual_eye_to_screen_dist);
+
+  float outer_angle = atanf(distortion.Distort(outer_point)[0]);
+  float inner_angle = atanf(distortion.Distort(inner_point)[0]);
+  float bottom_angle = atanf(distortion.Distort(bottom_point)[1]);
+  float top_angle = atanf(distortion.Distort(top_point)[1]);
+
+  for (EyeType eye : {kLeftEye, kRightEye}) {
+    const FieldOfView max_fov = mount.GetEyeMaxFov(eye);
+    float left_angle = (eye == kLeftEye) ? outer_angle : inner_angle;
+    float right_angle = (eye == kLeftEye) ? inner_angle : outer_angle;
+
+    eye_fov_[eye] = FieldOfView(std::min(left_angle, max_fov.GetLeft()),
+                                std::min(right_angle, max_fov.GetRight()),
+                                std::min(bottom_angle, max_fov.GetBottom()),
+                                std::min(top_angle, max_fov.GetTop()));
+
+    vec2 texture_vp_ta_p1 =
+        vec2(-tanf(eye_fov_[eye].GetLeft()), -tanf(eye_fov_[eye].GetBottom()));
+    vec2 texture_vp_ta_p2 =
+        vec2(tanf(eye_fov_[eye].GetRight()), tanf(eye_fov_[eye].GetTop()));
+    vec2 texture_vp_size_ta = texture_vp_ta_p2 - texture_vp_ta_p1;
+
+    vec2 texture_vp_sizef_pixels =
+        texture_vp_size_ta.array() * pixels_per_tan_angle.array();
+
+    vec2i texture_vp_size_pixels =
+        vec2i(static_cast<int32_t>(roundf(texture_vp_sizef_pixels[0])),
+              static_cast<int32_t>(roundf(texture_vp_sizef_pixels[1])));
+    int vp_start_x =
+        (eye == kLeftEye) ? 0 : eye_viewport_range_[kLeftEye].p2[0];
+
+    eye_viewport_range_[eye] =
+        Range2i::FromSize(vec2i(vp_start_x, 0), texture_vp_size_pixels);
+    float left_dist = (eye == kLeftEye) ? outer_dist : inner_dist;
+    float right_dist = (eye == kLeftEye) ? inner_dist : outer_dist;
+    vec2 screen_ta_p1(-left_dist / virtual_eye_to_screen_dist,
+                      -bottom_display_dist / virtual_eye_to_screen_dist);
+    vec2 screen_ta_p2(right_dist / virtual_eye_to_screen_dist,
+                      top_display_dist / virtual_eye_to_screen_dist);
+    vec2 screen_ta_size = screen_ta_p2 - screen_ta_p1;
+
+    // Align the tan angle coordinates to the nearest pixel.  This will ensure
+    // that the optical center doesn't straddle multiple pixels.
+    // TODO(hendrikw): verify that this works correctly for Daydream View.
+    vec2 tan_angle_per_pixel(screen_ta_size.array() /
+                             texture_vp_size_pixels.cast<float>().array());
+    vec2 pixel_p1(screen_ta_p1.array() / tan_angle_per_pixel.array());
+    vec2 pixel_shift(roundf(pixel_p1.x()) - pixel_p1.x(),
+                     roundf(pixel_p1.y()) - pixel_p1.y());
+    screen_ta_p1 +=
+        (tan_angle_per_pixel.array() * pixel_shift.array()).matrix();
+    screen_ta_p2 +=
+        (tan_angle_per_pixel.array() * pixel_shift.array()).matrix();
+
+    // Calculate the transformations needed for the distortions.
+    eye_tan_angle_from_norm_screen_matrix_[eye] =
+        TranslationMatrix(vec2(screen_ta_p1)) *
+        ScaleMatrix(screen_ta_size);
+    eye_tan_angle_from_norm_screen_inv_matrix_[eye] =
+        eye_tan_angle_from_norm_screen_matrix_[eye].inverse();
+
+    eye_norm_texture_from_tan_angle_inv_matrix_[eye] =
+        TranslationMatrix(texture_vp_ta_p1) *
+        ScaleMatrix(texture_vp_size_ta);
+    eye_norm_texture_from_tan_angle_matrix_[eye] =
+        eye_norm_texture_from_tan_angle_inv_matrix_[eye].inverse();
+  }
+  vec2i left_vp_size = eye_viewport_range_[kLeftEye].GetSize();
+  vec2i right_vp_size = eye_viewport_range_[kRightEye].GetSize();
+
+  recommended_render_target_size_ =
+      vec2i(left_vp_size[0] + right_vp_size[0],
+            std::max(left_vp_size[1], right_vp_size[1]));
+
+  display_range_ = Range2i::FromSize(vec2i(0, 0), display.GetSizePixels());
+
+  eye_from_head_matrix_[kLeftEye] = Eigen::Translation3f(
+      vec3(mount.GetScreenCenterToLensDistance(), 0.0f, 0.0f));
+  eye_from_head_matrix_[kRightEye] = Eigen::Translation3f(
+      vec3(-mount.GetScreenCenterToLensDistance(), 0.0f, 0.0f));
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/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..13090ca
--- /dev/null
+++ b/libs/vr/libeds/distortion_renderer.cpp
@@ -0,0 +1,792 @@
+#include "include/private/dvr/distortion_renderer.h"
+
+#include <float.h>
+
+#include <string>
+
+#include <utils/Log.h>
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+
+#include <log/log.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/ortho.h>
+#include <private/dvr/sensor_constants.h>
+
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+
+#define POSITION_ATTR 0
+#define VIEWPORT_COORD_R_ATTR 1
+#define VIEWPORT_COORD_G_ATTR 2
+#define VIEWPORT_COORD_B_ATTR 3
+
+// Pose data uniform buffer bindings. Must be sequential.
+#define POSE_BINDING 0
+#define POSE_BINDING2 1
+
+// Texture unit bindings. Must be sequential.
+// Things break if we start at binding 0 (samples come back black).
+#define SAMPLER_BINDING 1
+#define SAMPLER_BINDING2 2
+
+#define GLSL_VIGNETTE_FUNC                                       \
+  "float vignette(vec2 texCoords) {\n"                           \
+  "  const float fadeDist = 0.01;\n"                             \
+  "  const float fadeDistInv = 1.0 / fadeDist;\n"                \
+  "  const float inset = 0.02;\n"                                \
+  "  vec2 lowEdge = vec2(inset - fadeDist);\n"                   \
+  "  vec2 highEdge = vec2(1.0 - inset + fadeDist);\n"            \
+  "  vec2 vignetteMin = "                                        \
+  "    clamp(-fadeDistInv * (lowEdge - texCoords), 0.0, 1.0);\n" \
+  "  vec2 vignetteMax = "                                        \
+  "    clamp(fadeDistInv * (highEdge - texCoords), 0.0, 1.0);\n" \
+  "  vec2 vignette = vignetteMin * vignetteMax;\n"               \
+  "  return vignette.x * vignette.y;\n"                          \
+  "}\n"
+
+namespace {
+
+// If enabled, the pixel shader will blend by reading back the current pixel
+// from the framebuffer.
+// TODO(jbates) With framebuffer read coherency disabled, this seems to perform
+//   well enough. That requires a GL extension, so for now we disable this path.
+constexpr bool kUseFramebufferReadback = false;
+
+static const char* kVertexShaderChromaticAberrationString =
+    "uniform mat4 uProjectionMatrix;\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+    "uniform LateLatchData {\n"
+    "  mat4 uTexFromRecommendedViewportMatrix;\n"
+    "};\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING2) ", std140)\n"
+    "uniform LateLatchData2 {\n"
+    "  mat4 uTexFromRecommendedViewportMatrix2;\n"
+    "};\n"
+    "#endif\n"
+    "uniform vec4 uTexXMinMax;\n"
+    "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_R_ATTR)
+           ") in vec2 aViewportCoordsR;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+           ") in vec2 aViewportCoordsG;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_B_ATTR)
+           ") in vec2 aViewportCoordsB;\n"
+    "mediump out vec4 vTexCoordsRG;\n"
+    "mediump out vec2 vTexCoordsB;\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "mediump out vec4 vTexCoordsRG2;\n"
+    "mediump out vec2 vTexCoordsB2;\n"
+    "#endif\n"
+    "mediump out vec3 vVignette;\n"
+    "\n" GLSL_VIGNETTE_FUNC
+    "void main(void) {\n"
+    "  vVignette.r = vignette(aViewportCoordsR);\n"
+    "  vVignette.g = vignette(aViewportCoordsG);\n"
+    "  vVignette.b = vignette(aViewportCoordsB);\n"
+    "  vec4 redTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+    "                       vec4(aViewportCoordsR, 0., 1.));\n"
+    "  vec4 greenTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+    "                         vec4(aViewportCoordsG, 0., 1.));\n"
+    "  vec4 blueTexCoords = (uTexFromRecommendedViewportMatrix * \n"
+    "                        vec4(aViewportCoordsB, 0., 1.));\n"
+    "  vTexCoordsRG.xy = redTexCoords.xy / redTexCoords.w;\n"
+    "  vTexCoordsRG.zw = greenTexCoords.xy / greenTexCoords.w;\n"
+    "  vTexCoordsB = blueTexCoords.xy / blueTexCoords.w;\n"
+    "  vTexCoordsRG.x = clamp(vTexCoordsRG.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+    "  vTexCoordsRG.z = clamp(vTexCoordsRG.z, uTexXMinMax.x, uTexXMinMax.y);\n"
+    "  vTexCoordsB.x = clamp(vTexCoordsB.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "  redTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+    "                  vec4(aViewportCoordsR, 0., 1.));\n"
+    "  greenTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+    "                    vec4(aViewportCoordsG, 0., 1.));\n"
+    "  blueTexCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+    "                   vec4(aViewportCoordsB, 0., 1.));\n"
+    "  vTexCoordsRG2.xy = redTexCoords.xy / redTexCoords.w;\n"
+    "  vTexCoordsRG2.zw = greenTexCoords.xy / greenTexCoords.w;\n"
+    "  vTexCoordsB2 = blueTexCoords.xy / blueTexCoords.w;\n"
+    "  vTexCoordsRG2.x = clamp(vTexCoordsRG2.x,\n"
+    "                          uTexXMinMax.z, uTexXMinMax.w);\n"
+    "  vTexCoordsRG2.z = clamp(vTexCoordsRG2.z, uTexXMinMax.z,\n"
+    "                          uTexXMinMax.w);\n"
+    "  vTexCoordsB2.x = clamp(vTexCoordsB2.x, uTexXMinMax.z, uTexXMinMax.w);\n"
+    "#endif\n"
+    "  gl_Position = uProjectionMatrix * vec4(aPosition, 0., 1.);\n"
+    "}\n";
+
+static const char* kFragmentShaderChromaticAberrationString =
+    "#ifdef GL_ES\n"
+    "precision mediump float;\n"
+    "#endif\n"
+    " \n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+    "uniform sampler2D uDistortionTexture; \n"
+    "mediump in vec4 vTexCoordsRG;\n"
+    "mediump in vec2 vTexCoordsB;\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING2) ")\n"
+    "uniform sampler2D uDistortionTexture2; \n"
+    "mediump in vec4 vTexCoordsRG2;\n"
+    "mediump in vec2 vTexCoordsB2;\n"
+    "#endif\n"
+    "mediump in vec3 vVignette;\n"
+    "#ifdef BLEND_WITH_PREVIOUS_LAYER \n"
+    "inout vec4 fragColor; \n"
+    "#else \n"
+    "out vec4 fragColor; \n"
+    "#endif \n"
+    " \n"
+    "void main(void) { \n"
+    "  vec4 ra = texture(uDistortionTexture, vTexCoordsRG.xy); \n"
+    "  vec4 ga = texture(uDistortionTexture, vTexCoordsRG.zw); \n"
+    "  vec4 ba = texture(uDistortionTexture, vTexCoordsB); \n"
+    "#ifdef BLEND_WITH_PREVIOUS_LAYER \n"
+    "  vec3 alpha1 = vec3(ra.a, ga.a, ba.a); \n"
+    "  vec3 color = (vec3(1.0) - alpha1) * fragColor.rgb + \n"
+    "               alpha1 * vec3(ra.r, ga.g, ba.b); \n"
+    "#else // BLEND_WITH_PREVIOUS_LAYER \n"
+    "  vec3 color = vec3(ra.r, ga.g, ba.b); \n"
+    "#endif // BLEND_WITH_PREVIOUS_LAYER \n"
+    "#ifdef COMPOSITE_LAYER_2 \n"
+    "  // Alpha blend layer 2 onto layer 1. \n"
+    "  vec4 ra2 = texture(uDistortionTexture2, vTexCoordsRG2.xy); \n"
+    "  vec4 ga2 = texture(uDistortionTexture2, vTexCoordsRG2.zw); \n"
+    "  vec4 ba2 = texture(uDistortionTexture2, vTexCoordsB2); \n"
+    "  vec3 color2 = vec3(ra2.r, ga2.g, ba2.b); \n"
+    "  vec3 alpha2 = vec3(ra2.a, ga2.a, ba2.a); \n"
+    "  color = (vec3(1.0) - alpha2) * color + alpha2 * color2; \n"
+    "#endif \n"
+    "#ifdef ALPHA_VIGNETTE\n"
+    "  fragColor = vec4(color, vVignette.b * ga.a); \n"
+    "#else // ALPHA_VIGNETTE\n"
+    "  fragColor = vec4(vVignette.rgb * color, ga.a); \n"
+    "#endif // ALPHA_VIGNETTE\n"
+    "} \n";
+
+static const char* kVertexShaderNoChromaticAberrationString =
+    "uniform mat4 uProjectionMatrix;\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+    "uniform LateLatchData {\n"
+    "  mat4 uTexFromRecommendedViewportMatrix;\n"
+    "};\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING2) ", std140)\n"
+    "uniform LateLatchData2 {\n"
+    "  mat4 uTexFromRecommendedViewportMatrix2;\n"
+    "};\n"
+    "#endif\n"
+    "uniform vec4 uTexXMinMax;\n"
+    "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+           ") in vec2 aViewportCoords;\n"
+    "mediump out vec2 vTexCoords;\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "mediump out vec2 vTexCoords2;\n"
+    "#endif\n"
+    "mediump out vec3 vVignette;\n"
+    "\n" GLSL_VIGNETTE_FUNC
+    "void main(void) {\n"
+    "  float fVignette = vignette(aViewportCoords);\n"
+    "  vVignette = vec3(fVignette, fVignette, fVignette);\n"
+    "  vec4 texCoords = (uTexFromRecommendedViewportMatrix * \n"
+    "                    vec4(aViewportCoords, 0., 1.));\n"
+    "  vTexCoords = texCoords.xy / texCoords.w;\n"
+    "  vTexCoords.x = clamp(vTexCoords.x, uTexXMinMax.x, uTexXMinMax.y);\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "  texCoords = (uTexFromRecommendedViewportMatrix2 * \n"
+    "               vec4(aViewportCoords, 0., 1.));\n"
+    "  vTexCoords2 = texCoords.xy / texCoords.w;\n"
+    "  vTexCoords2.x = clamp(vTexCoords2.x, uTexXMinMax.z, uTexXMinMax.w);\n"
+    "#endif\n"
+    "  gl_Position = uProjectionMatrix * vec4(aPosition, 0., 1.);\n"
+    "}\n";
+
+static const char* kFragmentShaderNoChromaticAberrationString =
+    "#ifdef GL_ES\n"
+    "precision mediump float;\n"
+    "#endif\n"
+    " \n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+    "uniform sampler2D uDistortionTexture; \n"
+    "mediump in vec2 vTexCoords;\n"
+    "#ifdef COMPOSITE_LAYER_2\n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING2) ")\n"
+    "uniform sampler2D uDistortionTexture2; \n"
+    "mediump in vec2 vTexCoords2;\n"
+    "#endif\n"
+    "mediump in vec3 vVignette;\n"
+    "out vec4 fragColor;\n"
+    " \n"
+    "void main(void) { \n"
+    "  vec4 color = texture(uDistortionTexture, vTexCoords); \n"
+    "#ifdef COMPOSITE_LAYER_2 \n"
+    "  // Alpha blend layer 2 onto layer 1. \n"
+    "  vec4 color2 = texture(uDistortionTexture2, vTexCoords2); \n"
+    "  float alpha2 = color2.a; \n"
+    "  color.rgb = (1.0 - alpha2) * color.rgb + alpha2 * color2.rgb; \n"
+    "#endif \n"
+    "  fragColor = vec4(vVignette * color.rgb, color.a); \n"
+    "} \n";
+
+static const char* kVertexShaderSimpleVideoQuadString =
+    "uniform mat4 uProjectionMatrix;\n"
+    "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+    "uniform LateLatchData {\n"
+    "  mat4 uEdsCorrection;\n"
+    "};\n"
+    "uniform mat4 uTexFromEyeMatrix;\n"
+    "uniform mat4 uEyeFromViewportMatrix;\n"
+    "layout(location = " STRINGIFY(POSITION_ATTR) ") in vec2 aPosition;\n"
+    "layout(location = " STRINGIFY(VIEWPORT_COORD_G_ATTR)
+           ") in vec2 aViewportCoords;\n"
+    "mediump out vec2 vTexCoords;\n"
+    "void main(void) {\n"
+    "  mat4 m = uTexFromEyeMatrix * inverse(uEdsCorrection) * uEyeFromViewportMatrix;\n"
+    "  mat3 uTexFromViewportMatrix = inverse(mat3(m[0].xyw, m[1].xyw, m[3].xyw)); \n"
+    "  vec3 texCoords = uTexFromViewportMatrix * vec3(aViewportCoords, 1.0);\n"
+    "  vTexCoords = texCoords.xy / texCoords.z;\n"
+    "  gl_Position = uProjectionMatrix * vec4(aPosition, 0.0, 1.0);\n"
+    "}\n";
+
+static const char* kFragmentShaderSimpleVideoQuadString =
+    "#extension GL_OES_EGL_image_external_essl3 : enable\n"
+    " \n"
+    "#ifdef GL_ES\n"
+    "precision mediump float;\n"
+    "#endif\n"
+    " \n"
+    "layout(binding = " STRINGIFY(SAMPLER_BINDING) ")\n"
+    "uniform samplerExternalOES uDistortionTexture; \n"
+    "mediump in vec2 vTexCoords;\n"
+    "out vec4 fragColor;\n"
+    " \n"
+    "void main(void) { \n"
+    "  if (clamp(vTexCoords, 0.0, 1.0) != vTexCoords) { \n"
+    "    fragColor = vec4(0.0, 0.0, 0.0, 0.0); \n"
+    "  } else { \n"
+    "    fragColor = texture(uDistortionTexture, vTexCoords); \n"
+    "  } \n"
+    "} \n";
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// Note that converting from Clip Space ([-1,1]^3) to Viewport Space
+// for one eye ([0,1]x[0,1]) requires dividing by 2 in x and y.
+const mat4 DistortionRenderer::kViewportFromClipMatrix =
+    Eigen::Translation3f(vec3(0.5f, 0.5f, 0)) *
+    Eigen::DiagonalMatrix<float, 3>(vec3(0.5f, 0.5f, 1.0f));
+
+const mat4 DistortionRenderer::kClipFromViewportMatrix =
+    Eigen::DiagonalMatrix<float, 3>(vec3(2.0f, 2.0f, 1.0f)) *
+    Eigen::Translation3f(vec3(-0.5f, -0.5f, 0));
+
+void DistortionRenderer::EdsShader::load(const char* vertex,
+                                         const char* fragment, int num_layers,
+                                         bool use_alpha_vignette,
+                                         float rotation, bool flip_vertical,
+                                         bool blend_with_previous_layer) {
+  std::string vert_builder = "#version 310 es\n";
+  std::string frag_builder = "#version 310 es\n";
+  if (blend_with_previous_layer && kUseFramebufferReadback) {
+    frag_builder += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
+  }
+
+  if (num_layers == 2) {
+    vert_builder += "#define COMPOSITE_LAYER_2\n";
+    frag_builder += "#define COMPOSITE_LAYER_2\n";
+  } else {
+    LOG_ALWAYS_FATAL_IF(num_layers != 1);
+  }
+  if (blend_with_previous_layer) {
+    // Check for unsupported shader combinations:
+    LOG_ALWAYS_FATAL_IF(num_layers != 1);
+    LOG_ALWAYS_FATAL_IF(use_alpha_vignette);
+    if (kUseFramebufferReadback)
+      frag_builder += "#define BLEND_WITH_PREVIOUS_LAYER\n";
+  }
+  if (use_alpha_vignette) {
+    vert_builder += "#define ALPHA_VIGNETTE\n";
+    frag_builder += "#define ALPHA_VIGNETTE\n";
+  }
+
+  vert_builder += vertex;
+  frag_builder += fragment;
+  pgm.Link(vert_builder, frag_builder);
+  LOG_ALWAYS_FATAL_IF(!pgm.IsUsable());
+
+  pgm.Use();
+
+  uProjectionMatrix =
+      glGetUniformLocation(pgm.GetProgram(), "uProjectionMatrix");
+  uTexFromEyeMatrix =
+      glGetUniformLocation(pgm.GetProgram(), "uTexFromEyeMatrix");
+  uEyeFromViewportMatrix =
+      glGetUniformLocation(pgm.GetProgram(), "uEyeFromViewportMatrix");
+  uTexXMinMax = glGetUniformLocation(pgm.GetProgram(), "uTexXMinMax");
+  CHECK_GL();
+
+  float vertical_multiply = flip_vertical ? -1.0 : 1.0;
+  mat4 projectionMatrix = OrthoMatrix(-0.5f, 0.5f, vertical_multiply * -0.5f,
+                                      vertical_multiply * 0.5f, -1.0f, 1.0f);
+
+  // Rotate the mesh into the screen's orientation.
+  // TODO(hendrikw): Once the display is finalized, and perhaps not portrait,
+  //                 look into removing this matrix altogether.
+  projectionMatrix =
+      projectionMatrix * Eigen::AngleAxisf(rotation, vec3::UnitZ());
+
+  LOG_ALWAYS_FATAL_IF(sizeof(mat4) != 4 * 4 * 4);
+  glUniformMatrix4fv(uProjectionMatrix, 1, false, projectionMatrix.data());
+}
+
+DistortionRenderer::DistortionRenderer(
+    const CompositeHmd& hmd, vec2i display_size, int distortion_mesh_resolution,
+    bool flip_texture_horizontally, bool flip_texture_vertically,
+    bool separated_eye_buffers, bool eds_enabled, bool late_latch_enabled)
+    : shader_type_(kChromaticAberrationCorrection),
+      eds_enabled_(eds_enabled),
+      chromatic_aberration_correction_enabled_(true),
+      use_alpha_vignette_(false),
+      distortion_mesh_resolution_(distortion_mesh_resolution),
+      last_distortion_texture_id_(0),
+      app_texture_target_(GL_TEXTURE_2D),
+      display_size_(display_size),
+      separated_eye_buffers_(separated_eye_buffers) {
+  ATRACE_NAME("DistortionRenderer::DistortionRenderer");
+
+  float device_rotation = 0.0;
+
+  if (eds_enabled_) {
+    // Late latch must be on if eds_enabled_ is true.
+    if (!late_latch_enabled) {
+      ALOGE("Cannot enable EDS without late latch. Force enabling late latch.");
+      late_latch_enabled = true;
+    }
+  }
+
+  // TODO(hendrikw): Look into moving this logic into DisplayMetrics.
+  if (hmd.GetDisplayMetrics().IsPortrait()) {
+    device_rotation = -M_PI / 2.0f;
+  }
+
+  // Create shader programs.
+  shaders_[kNoChromaticAberrationCorrection].load(
+      kVertexShaderNoChromaticAberrationString,
+      kFragmentShaderNoChromaticAberrationString, 1, false, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kNoChromaticAberrationCorrectionTwoLayers].load(
+      kVertexShaderNoChromaticAberrationString,
+      kFragmentShaderNoChromaticAberrationString, 2, false, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrection].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 1, false, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrectionTwoLayers].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 2, false, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrectionAlphaVignette].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 1, true, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrectionAlphaVignetteTwoLayers].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 2, true, device_rotation,
+      flip_texture_horizontally, false);
+  shaders_[kChromaticAberrationCorrectionWithBlend].load(
+      kVertexShaderChromaticAberrationString,
+      kFragmentShaderChromaticAberrationString, 1, false, device_rotation,
+      flip_texture_horizontally, true);
+  shaders_[kSimpleVideoQuad].load(
+      kVertexShaderSimpleVideoQuadString,
+      kFragmentShaderSimpleVideoQuadString, 1, false, device_rotation,
+      flip_texture_horizontally, true);
+  CHECK_GL();
+
+  mat4 tex_from_recommended_viewport_matrix[2][2][2];
+  for (int eye = 0; eye < 2; ++eye) {
+    // Near and far plane don't actually matter for the clip_from_eye_matrix
+    // below since it is only used (for EDS) to transform coordinates for
+    // which the Z has been dropped.
+    static const float kNear = 0.1f, kFar = 100.0f;
+    const FieldOfView& fov =
+        (eye == kLeftEye ? hmd.GetEyeFov(kLeftEye) : hmd.GetEyeFov(kRightEye));
+    mat4 c_clip_from_eye_matrix = fov.GetProjectionMatrix(kNear, kFar);
+    mat4 c_eye_from_clip_matrix = c_clip_from_eye_matrix.inverse();
+
+    // Compute tex_from_recommended_viewport_matrix.
+
+    // flip_texture_vertically defines the default flip behavior.
+    // do_flip[0] should be the default, while do_flip[1] should be the
+    // inverse of the default.
+    int do_flip[2] = {flip_texture_vertically ? 1 : 0,
+                      flip_texture_vertically ? 0 : 1};
+    for (int flip = 0; flip < 2; ++flip) {
+      vec2 flip_scale(1.0f, do_flip[flip] ? -1.0f : 1.0f);
+      vec2 flip_offset(0.0f, do_flip[flip] ? 1.0f : 0.0f);
+
+      for (int separate_eye = 0; separate_eye < 2; ++separate_eye) {
+        vec2 viewport_corner_offset = (eye == kLeftEye || separate_eye)
+                                          ? vec2(0.0f, 0.0f)
+                                          : vec2(0.5f, 0.0f);
+        const vec2 txy = viewport_corner_offset + flip_offset;
+        const vec2 scalexy = vec2(separate_eye ? 1.0f : 0.5f, 1.0f);
+        tex_from_recommended_viewport_matrix[eye][flip][separate_eye] =
+            Eigen::Translation3f(vec3(txy.x(), txy.y(), 0.0f)) *
+            Eigen::DiagonalMatrix<float, 3>(vec3(flip_scale.x() * scalexy.x(),
+                                                 flip_scale.y(), scalexy.y()));
+
+        tex_from_eye_matrix_[eye][flip][separate_eye] =
+            tex_from_recommended_viewport_matrix[eye][flip][separate_eye] *
+            kViewportFromClipMatrix * c_clip_from_eye_matrix;
+      }
+    }
+
+    eye_from_viewport_matrix_[eye] =
+        c_eye_from_clip_matrix * kClipFromViewportMatrix;
+  }
+
+  // Create UBO for setting the EDS matrix to identity when EDS is disabled.
+  glGenBuffers(2 * 2 * 2, &uTexFromRecommendedViewportMatrix[0][0][0]);
+  for (int eye = 0; eye < 2; ++eye) {
+    for (int flip = 0; flip < 2; ++flip) {
+      for (int separate_eye = 0; separate_eye < 2; ++separate_eye) {
+        glBindBuffer(
+            GL_UNIFORM_BUFFER,
+            uTexFromRecommendedViewportMatrix[eye][flip][separate_eye]);
+        glBufferData(GL_UNIFORM_BUFFER, sizeof(mat4), 0, GL_STATIC_DRAW);
+        CHECK_GL();
+        mat4* mat = static_cast<mat4*>(glMapBufferRange(
+            GL_UNIFORM_BUFFER, 0, sizeof(mat4), GL_MAP_WRITE_BIT));
+        CHECK_GL();
+        *mat = tex_from_recommended_viewport_matrix[eye][flip][separate_eye];
+        glUnmapBuffer(GL_UNIFORM_BUFFER);
+      }
+    }
+  }
+  glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+  // Create distortion meshes and associated GL resources.
+  glGenBuffers(2, mesh_vbo_);
+  glGenVertexArrays(2, mesh_vao_);
+  glGenBuffers(2, mesh_ibo_);
+  RecomputeDistortion(hmd);
+
+  SetDisplaySize(display_size);
+
+  if (hmd.GetDisplayMetrics().IsPortrait()) {
+    eye_viewport_origin_[0] =
+        vec2i(0, flip_texture_horizontally ? 0 : display_size_[1] / 2);
+    eye_viewport_origin_[1] =
+        vec2i(0, flip_texture_horizontally ? display_size_[1] / 2 : 0);
+    eye_viewport_size_ = vec2i(display_size_[0], display_size_[1] / 2);
+  } else {
+    eye_viewport_origin_[0] = vec2i(0, 0);
+    eye_viewport_origin_[1] = vec2i(display_size_[0] / 2, 0);
+    eye_viewport_size_ = vec2i(display_size_[0] / 2, display_size_[1]);
+  }
+
+  CHECK_GL();
+}
+
+DistortionRenderer::~DistortionRenderer() {
+  glDeleteBuffers(2 * 2 * 2, &uTexFromRecommendedViewportMatrix[0][0][0]);
+  glDeleteBuffers(2, mesh_vbo_);
+  glDeleteVertexArrays(2, mesh_vao_);
+  glDeleteBuffers(2, mesh_ibo_);
+}
+
+void DistortionRenderer::ApplyDistortionCorrectionToTexture(
+    EyeType eye, const GLuint* texture_ids, const bool* vertical_flip,
+    const bool* separate_eye, const int* late_latch_layer, int num_textures,
+    bool blend_with_previous_layer, bool do_gl_state_prep) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+
+  bool use_gl_blend = use_alpha_vignette_ ||
+                      (blend_with_previous_layer && !kUseFramebufferReadback);
+  if (use_gl_blend) {
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  }
+  DrawEye(eye, texture_ids, vertical_flip, separate_eye, late_latch_layer,
+          num_textures, blend_with_previous_layer, do_gl_state_prep);
+  if (use_gl_blend) {
+    glDisable(GL_BLEND);
+  }
+  CHECK_GL();
+}
+
+void DistortionRenderer::DrawVideoQuad(EyeType eye, int layer_i,
+                                       GLuint texture_id,
+                                       const mat4& transform) {
+  shaders_[kSimpleVideoQuad].use();
+
+  shaders_[kSimpleVideoQuad].SetTexFromEyeTransform(
+      tex_from_eye_matrix_[eye][0][1]);
+  shaders_[kSimpleVideoQuad].SetEyeFromViewportTransform(
+      transform * kClipFromViewportMatrix);
+
+  if (eds_enabled_) {
+    // Bind late latch view-projection UBO that is produced by AddEdsLateLatch.
+    late_latch_[layer_i]->BindUniformBuffer(
+        POSE_BINDING, LateLatch::kViewMatrix, eye);
+    CHECK_GL();
+  } else {
+    // When EDS is disabled we just set the matrix here with no pose offset.
+    glBindBufferBase(GL_UNIFORM_BUFFER, POSE_BINDING + layer_i,
+                     uTexFromRecommendedViewportMatrix[eye][0][1]);
+    CHECK_GL();
+  }
+
+  glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING);
+  glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
+  CHECK_GL();
+
+  glDrawElements(GL_TRIANGLE_STRIP, mesh_node_[eye].indices.size(),
+                 GL_UNSIGNED_SHORT, nullptr);
+
+  CHECK_GL();
+}
+
+void DistortionRenderer::DoLateLatch(uint32_t target_vsync_count,
+                                     const uint32_t* render_buffer_index,
+                                     const GLuint* render_pose_buffer_objects,
+                                     const bool* vertical_flip,
+                                     const bool* separate_eye,
+                                     int num_textures) {
+  if (eds_enabled_) {
+    LateLatchInput data;
+    memset(&data, 0, sizeof(data));
+    for (int ti = 0; ti < num_textures; ++ti) {
+      if (late_latch_[ti] == nullptr)
+        late_latch_[ti].reset(new LateLatch(false));
+
+      int flip_index = vertical_flip[ti] ? 1 : 0;
+      int separate_eye_i = separate_eye[ti] ? 1 : 0;
+      // Copy data into late latch input struct.
+      for (int eye = 0; eye < 2; ++eye) {
+        data.eds_mat1[eye] =
+            tex_from_eye_matrix_[eye][flip_index][separate_eye_i];
+        data.eds_mat2[eye] = eye_from_viewport_matrix_[eye];
+      }
+      data.pose_index = target_vsync_count & kPoseAsyncBufferIndexMask;
+      data.render_pose_index = render_buffer_index[ti];
+
+      late_latch_[ti]->AddEdsLateLatch(data, render_pose_buffer_objects[ti]);
+    }
+  }
+}
+
+void DistortionRenderer::PrepGlState(EyeType eye) {
+  glViewport(eye_viewport_origin_[eye].x(), eye_viewport_origin_[eye].y(),
+             eye_viewport_size_.x(), eye_viewport_size_.y());
+
+  glBindVertexArray(mesh_vao_[eye]);
+  CHECK_GL();
+
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_[eye]);
+  CHECK_GL();
+
+  if (!eds_enabled_) {
+    glMemoryBarrier(GL_UNIFORM_BARRIER_BIT);
+  }
+}
+
+void DistortionRenderer::ResetGlState(int num_textures) {
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+  glBindBuffer(GL_ARRAY_BUFFER, 0);
+  glBindVertexArray(0);
+  if (eds_enabled_) {
+    for (int ti = 0; ti < num_textures; ++ti)
+      glBindBufferBase(GL_UNIFORM_BUFFER, POSE_BINDING + ti, 0);
+  } else {
+    glBindBuffer(GL_UNIFORM_BUFFER, 0);
+  }
+
+  CHECK_GL();
+
+  // Unbind all texture inputs.
+  for (int ti = 0; ti < num_textures; ++ti) {
+    glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING + ti);
+    glBindTexture(app_texture_target_, 0);
+  }
+  glActiveTexture(GL_TEXTURE0);
+}
+
+void DistortionRenderer::DrawEye(EyeType eye, const GLuint* texture_ids,
+                                 const bool* vertical_flip,
+                                 const bool* separate_eye,
+                                 const int* late_latch_layer, int num_textures,
+                                 bool blend_with_previous_layer,
+                                 bool do_gl_state_prep) {
+  if (do_gl_state_prep)
+    PrepGlState(eye);
+
+  if (num_textures > kMaxLayers) {
+    ALOGE("Too many textures for DistortionRenderer");
+    num_textures = kMaxLayers;
+  }
+
+  LOG_ALWAYS_FATAL_IF(num_textures != 1 && num_textures != 2);
+
+  if (num_textures == 2) {
+    if (chromatic_aberration_correction_enabled_) {
+      if (use_alpha_vignette_) {
+        shader_type_ = kChromaticAberrationCorrectionAlphaVignetteTwoLayers;
+      } else {
+        shader_type_ = kChromaticAberrationCorrectionTwoLayers;
+      }
+    } else {
+      shader_type_ = kNoChromaticAberrationCorrectionTwoLayers;
+    }
+  } else {
+    if (chromatic_aberration_correction_enabled_) {
+      if (blend_with_previous_layer) {
+        shader_type_ = kChromaticAberrationCorrectionWithBlend;
+      } else if (use_alpha_vignette_) {
+        shader_type_ = kChromaticAberrationCorrectionAlphaVignette;
+      } else {
+        shader_type_ = kChromaticAberrationCorrection;
+      }
+    } else {
+      shader_type_ = kNoChromaticAberrationCorrection;
+    }
+  }
+  shaders_[shader_type_].use();
+
+  for (int ti = 0; ti < num_textures; ++ti) {
+    int flip_index = vertical_flip[ti] ? 1 : 0;
+    if (eds_enabled_) {
+      // Bind late latch view-projection UBO that is produced by
+      // AddEdsLateLatch.
+      late_latch_[late_latch_layer[ti]]->BindUniformBuffer(
+          POSE_BINDING + ti, LateLatch::kViewProjMatrix, eye);
+      CHECK_GL();
+    } else {
+      // When EDS is disabled we just set the matrix here with no pose offset.
+      // With app late-latching, we can't know the pose that the app used
+      // because it's in the app's framebuffer.
+      int separate_eye_i = separate_eye[ti] ? 1 : 0;
+      glBindBufferBase(
+          GL_UNIFORM_BUFFER, POSE_BINDING + ti,
+          uTexFromRecommendedViewportMatrix[eye][flip_index][separate_eye_i]);
+      CHECK_GL();
+    }
+
+    glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING + ti);
+    glBindTexture(app_texture_target_, texture_ids[ti]);
+    CHECK_GL();
+  }
+
+  // Prevents left eye data from bleeding into right eye and vice-versa.
+  vec2 layer_min_max[kMaxLayers];
+  for (int i = 0; i < kMaxLayers; ++i)
+    layer_min_max[i] = vec2(0.0f, 0.0f);
+  for (int ti = 0; ti < num_textures; ++ti) {
+    if (separate_eye[ti]) {
+      layer_min_max[ti] = vec2(0.0f, 1.0f);  // Use the whole texture.
+    } else if (eye == kLeftEye) {
+      layer_min_max[ti] = vec2(0.0f, 0.499f);
+    } else {
+      layer_min_max[ti] = vec2(0.501f, 1.0f);
+    }
+  }
+  // The second layer stores its x min and max in the z,w slots of the vec4.
+  vec4 xTexMinMax(layer_min_max[0].x(), layer_min_max[0].y(),
+                  layer_min_max[1].x(), layer_min_max[1].y());
+
+  glUniform4fv(shaders_[shader_type_].uTexXMinMax, 1, &xTexMinMax[0]);
+  CHECK_GL();
+
+  glDrawElements(GL_TRIANGLE_STRIP, mesh_node_[eye].indices.size(),
+                 GL_UNSIGNED_SHORT, nullptr);
+  CHECK_GL();
+  if (do_gl_state_prep)
+    ResetGlState(num_textures);
+}
+
+void DistortionRenderer::SetDisplaySize(vec2i display_size) {
+  display_size_ = display_size;
+}
+
+void DistortionRenderer::SetEdsEnabled(bool enabled) { eds_enabled_ = enabled; }
+
+void DistortionRenderer::RecomputeDistortion(const CompositeHmd& hmd) {
+  using std::placeholders::_1;
+  using std::placeholders::_2;
+  using std::placeholders::_3;
+  using std::placeholders::_4;
+  DistortionFunction distortion_function =
+      std::bind(&CompositeHmd::ComputeDistortedVertex, &hmd, _1, _2, _3, _4);
+
+  for (int i = 0; i < 2; ++i) {
+    mesh_node_[i] =
+        BuildDistortionMesh(static_cast<EyeType>(i),
+                            distortion_mesh_resolution_, distortion_function);
+
+    glBindVertexArray(mesh_vao_[i]);
+
+    glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo_[i]);
+    glBufferData(GL_ARRAY_BUFFER,
+                 sizeof(EdsVertex) * mesh_node_[i].vertices.size(),
+                 &mesh_node_[i].vertices.front(), GL_STATIC_DRAW);
+
+    glEnableVertexAttribArray(POSITION_ATTR);
+    glEnableVertexAttribArray(VIEWPORT_COORD_R_ATTR);
+    glEnableVertexAttribArray(VIEWPORT_COORD_G_ATTR);
+    glEnableVertexAttribArray(VIEWPORT_COORD_B_ATTR);
+
+    glVertexAttribPointer(
+        POSITION_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+        reinterpret_cast<void*>(offsetof(EdsVertex, position)));
+
+    glVertexAttribPointer(
+        VIEWPORT_COORD_R_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+        reinterpret_cast<void*>(offsetof(EdsVertex, red_viewport_coords)));
+
+    glVertexAttribPointer(
+        VIEWPORT_COORD_G_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+        reinterpret_cast<void*>(offsetof(EdsVertex, green_viewport_coords)));
+
+    glVertexAttribPointer(
+        VIEWPORT_COORD_B_ATTR, 2, GL_FLOAT, GL_FALSE, sizeof(EdsVertex),
+        reinterpret_cast<void*>(offsetof(EdsVertex, blue_viewport_coords)));
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_ibo_[i]);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+                 sizeof(uint16_t) * mesh_node_[i].indices.size(),
+                 &mesh_node_[i].indices.front(), GL_STATIC_DRAW);
+    CHECK_GL();
+  }
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+  glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+  glBindVertexArray(0);
+}
+
+bool DistortionRenderer::GetLastEdsPose(LateLatchOutput* out_data, int layer_id) const {
+  if (layer_id >= kMaxLayers) {
+    ALOGE("Accessing invalid layer %d", layer_id);
+    return false;
+  }
+
+  if (late_latch_[layer_id] != nullptr) {
+    late_latch_[layer_id]->CaptureOutputData(out_data);
+    return true;
+  } else {
+    ALOGE("Late latch shader not enabled.");
+    return false;
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/eds.cpp b/libs/vr/libeds/eds.cpp
new file mode 100644
index 0000000..8af5b27
--- /dev/null
+++ b/libs/vr/libeds/eds.cpp
@@ -0,0 +1,35 @@
+#include <dvr/eds.h>
+
+#include <private/dvr/graphics/vr_gl_extensions.h>
+#include <private/dvr/late_latch.h>
+#include <private/dvr/types.h>
+
+// TODO(jbates) delete this file and eds.h
+
+extern "C" int dvrEdsInit(bool with_late_latch) { return 0; }
+
+extern "C" void dvrEdsDeinit() {}
+
+extern "C" int dvrEdsCapturePoseAsync(int eye, uint32_t target_vsync_count,
+                                      const float* projection_matrix,
+                                      const float* eye_from_head_matrix,
+                                      const float* pose_offset_matrix) {
+  return 0;
+}
+
+extern "C" int dvrEdsBindPose(int eye, uint32_t ubo_binding, intptr_t offset,
+                              ssize_t size) {
+  return 0;
+}
+
+extern "C" int dvrEdsBlitPose(int eye, int viewport_width,
+                              int viewport_height) {
+  return 0;
+}
+
+extern "C" int dvrEdsBlitPoseFromCpu(int eye, int viewport_width,
+                                     int viewport_height,
+                                     const float* pose_quaternion,
+                                     const float* pose_position) {
+  return 0;
+}
diff --git a/libs/vr/libeds/eds_mesh.cpp b/libs/vr/libeds/eds_mesh.cpp
new file mode 100644
index 0000000..01a90cf
--- /dev/null
+++ b/libs/vr/libeds/eds_mesh.cpp
@@ -0,0 +1,136 @@
+#include "include/private/dvr/eds_mesh.h"
+
+#include <log/log.h>
+#include <math.h>
+
+#include <private/dvr/types.h>
+
+namespace {
+
+using android::dvr::EdsVertex;
+using android::dvr::EyeType;
+using android::dvr::DistortionFunction;
+using android::dvr::vec2;
+
+// Computes the vertices for a distortion mesh with resolution |resolution| and
+// distortion provided by |hmd| and stores them in |vertices|.
+static void ComputeDistortionMeshVertices(
+    EdsVertex* vertices, int resolution,
+    const DistortionFunction& distortion_function, EyeType eye) {
+  for (int row = 0; row < resolution; row++) {
+    for (int col = 0; col < resolution; col++) {
+      const float x_norm =
+          static_cast<float>(col) / (static_cast<float>(resolution - 1U));
+      const float y_norm =
+          static_cast<float>(row) / (static_cast<float>(resolution - 1U));
+
+      const vec2 xy_norm(x_norm, y_norm);
+      const size_t index = col * resolution + row;
+
+      // Evaluate distortion function to get the new coordinates for each color
+      // channel. The distortion function returns the new coordinates relative
+      // to a full viewport with 0 <= x <= 1 for each eye.
+      vec2 coords[3];
+      distortion_function(eye, xy_norm, &vertices[index].position, coords);
+
+      // Store distortion mapping in texture coordinates.
+      vertices[index].red_viewport_coords = coords[0];
+      vertices[index].green_viewport_coords = coords[1];
+      vertices[index].blue_viewport_coords = coords[2];
+    }
+  }
+}
+
+// Computes the triangle strip indices for a distortion mesh with resolution
+// |resolution| and stores them in |indices|.
+static void ComputeDistortionMeshIndices(uint16_t* indices, int resolution) {
+  // The following strip method has been used in the Cardboard SDK
+  // (java/com/google/vrtoolkit/cardboard/DistortionRenderer.java) and has
+  // originally been described at:
+  //
+  // http://dan.lecocq.us/wordpress/2009/12/25/triangle-strip-for-grids-a-construction/
+  //
+  // For a grid with 4 rows and 4 columns of vertices, the strip would
+  // look like:
+  //                             ↻
+  //         0    -    4    -    8    -   12
+  //         ↓    ↗    ↓    ↗    ↓    ↗    ↓
+  //         1    -    5    -    9    -   13
+  //         ↓    ↖    ↓    ↖    ↓    ↖    ↓
+  //         2    -    6    -   10    -   14
+  //         ↓    ↗    ↓    ↗    ↓    ↗    ↓
+  //         3    -    7    -   11    -   15
+  //                   ↺
+  //
+  // Note the little circular arrows next to 7 and 8 that indicate
+  // repeating that vertex once so as to produce degenerate triangles.
+  //
+  // To facilitate scanline racing, the vertex order is left to right.
+
+  int16_t index_offset = 0;
+  int16_t vertex_offset = 0;
+  for (int row = 0; row < resolution - 1; ++row) {
+    if (row > 0) {
+      indices[index_offset] = indices[index_offset - 1];
+      ++index_offset;
+    }
+    for (int col = 0; col < resolution; ++col) {
+      if (col > 0) {
+        if (row % 2 == 0) {
+          // Move right on even rows.
+          ++vertex_offset;
+        } else {
+          --vertex_offset;
+        }
+      }
+      // A cast to uint16_t is safe here as |vertex_offset| will not drop below
+      // zero in this loop. As col is initially equal to zero |vertex_offset| is
+      // always incremented before being decremented, is initialized to zero and
+      // is only incremented outside of the loop.
+      indices[index_offset++] = static_cast<uint16_t>(vertex_offset);
+      indices[index_offset++] = static_cast<uint16_t>(
+          vertex_offset + static_cast<int16_t>(resolution));
+    }
+    vertex_offset =
+        static_cast<int16_t>(static_cast<int>(resolution) + vertex_offset);
+  }
+}
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+// Builds a distortion mesh of resolution |resolution| using the distortion
+// provided by |hmd| for |eye|.
+EdsMesh BuildDistortionMesh(EyeType eye, int resolution,
+                            const DistortionFunction& distortion_function) {
+  LOG_ALWAYS_FATAL_IF(resolution <= 2);
+
+  // Number of indices produced by the strip method
+  // (see comment in ComputeDistortionMeshIndices):
+  //
+  //     1 vertex per triangle
+  //     2 triangles per quad, (rows - 1) * (cols - 1) quads
+  //     2 vertices at the start of each row for the first triangle
+  //     1 extra vertex per row (except first and last) for a
+  //       degenerate triangle
+  //
+  const uint16_t index_count =
+      static_cast<uint16_t>(resolution * (2 * resolution - 1U) - 2U);
+  const uint16_t vertex_count = static_cast<uint16_t>(resolution * resolution);
+
+  EdsMesh mesh;
+  mesh.vertices.resize(vertex_count);
+  mesh.indices.resize(index_count);
+
+  // Populate vertex and index buffer.
+  ComputeDistortionMeshVertices(&mesh.vertices[0], resolution,
+                                distortion_function, eye);
+  ComputeDistortionMeshIndices(&mesh.indices[0], resolution);
+
+  return mesh;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libeds/include/CPPLINT.cfg b/libs/vr/libeds/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libeds/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libeds/include/dvr/eds.h b/libs/vr/libeds/include/dvr/eds.h
new file mode 100644
index 0000000..37b1297
--- /dev/null
+++ b/libs/vr/libeds/include/dvr/eds.h
@@ -0,0 +1,150 @@
+#ifndef ANDROID_DVR_EDS_H_
+#define ANDROID_DVR_EDS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+// This struct aligns with GLSL uniform blocks with std140 layout.
+// std140 allows padding between certain types, so padding must be explicitly
+// added as struct members.
+struct __attribute__((__packed__)) DvrLateLatchData {
+  // Column-major order.
+  float view_proj_matrix[16];
+  // Column-major order.
+  float view_matrix[16];
+  float pose_quaternion[4];
+  float pose_position[4];
+};
+
+//
+// These APIs are not thread safe and must be called on a single thread with an
+// actively bound GL context corresponding to a display surface.
+//
+
+// Prepares EDS and Late Latching system. Idempotent if called more than once.
+// The target GL context must be created and bound.
+//
+// If |with_late_latch| is true, a thread will be created that asynchronously
+// updates the pose in memory.
+//
+// The following GL states are modified as follows:
+// glBindBuffer(GL_ARRAY_BUFFER, 0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+//
+// Returns 0 on success, negative error code on failure.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsInit(bool with_late_latch);
+
+// Stops and destroys the EDS Late Latching system.
+void dvrEdsDeinit();
+
+// Submits GL draw command that will capture the latest head pose into a uniform
+// buffer object. This should be called twice per frame, before the app begins
+// drawing for each eye.
+// For each eye, a later call to dvrEdsBlitPose will write this pose into
+// the application framebuffer corner so that the EDS service knows what pose
+// the frame was rendered with.
+//
+// |eye| is 0 for left eye and 1 for right eye.
+//
+// The following GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, id);
+// glDisable(GL_RASTERIZER_DISCARD);
+//
+// Returns 0 on success, negative error code on failure:
+//   EPERM - dvrEdsInit(true) was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsCapturePoseAsync(int eye, uint32_t target_vsync_count,
+                           const float* projection_matrix,
+                           const float* eye_from_head_matrix,
+                           const float* pose_offset_matrix);
+
+// Binds the late-latch output data as a GL_UNIFORM_BUFFER so that your vertex
+// shaders can use the latest head pose. For example, to bind just the
+// view_matrix from the output:
+//
+// dvrEdsBindPose(eye, BINDING,
+//                       offsetof(DvrLateLatchData, view_matrix),
+//                       sizeof(DvrLateLatchData::view_matrix));
+//
+// Or more commonly, bind the view projection matrix:
+//
+// dvrEdsBindPose(eye, BINDING,
+//                       offsetof(DvrLateLatchData, view_proj_matrix),
+//                       sizeof(DvrLateLatchData::view_proj_matrix));
+//
+// BINDING in the above examples is the binding location of the uniform
+// interface block in the GLSL shader.
+//
+// Shader example (3 would be the |ubo_binding| passed to this function):
+//  layout(binding = 3, std140) uniform LateLatchData {
+//    mat4 uViewProjection;
+//  };
+//
+// |eye| is 0 for left eye and 1 for right eye.
+//
+// The following GL states are modified as follows:
+// glBindBuffer(GL_UNIFORM_BUFFER, ...);
+// glBindBufferRange(GL_UNIFORM_BUFFER, ...);
+//
+// To clear the binding, call glBindBuffer(GL_UNIFORM_BUFFER, 0);
+//
+// Returns 0 on success, negative error code on failure:
+//   EPERM - dvrEdsInit(true) was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBindPose(int eye, uint32_t ubo_binding, intptr_t offset,
+                   ssize_t size);
+
+// DEPRECATED
+//
+// Blits the pose captured previously into the currently bound framebuffer.
+// The current framebuffer is assumed to be the default framebuffer 0, the
+// surface that will be sent to the display and have EDS and lens warp applied
+// to it.
+//
+// |eye| is 0 for left eye and 1 for right eye.
+// |viewport_width| is the width of the viewport for this eye, which is
+//                  usually half the width of the framebuffer.
+// |viewport_height| is the height of the viewport for this eye, which is
+//                   usually the height of the framebuffer.
+//
+// The following GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferRange(GL_UNIFORM_BUFFER, 23, ...);
+//
+// Returns 0 on success, negative error code on failure:
+//   EPERM - dvrEdsInit was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBlitPose(int eye, int viewport_width, int viewport_height);
+
+// DEPRECATED
+//
+// Same as dvrEdsBlitPose except that the pose is provided as an
+// parameter instead of getting it from dvrEdsBindPose. This is for
+// applications that want EDS but do not want late-latching.
+//
+// |pose_quaternion| should point to 4 floats that represent a quaternion.
+// |pose_position| should point to 3 floats that represent x,y,z position.
+//
+// GL states are modified as follows:
+// glUseProgram(0);
+// glBindBuffer(GL_UNIFORM_BUFFER, 0);
+// glBindBufferBase(GL_UNIFORM_BUFFER, 23, ...);
+//
+// Returns 0 on success, negative error code on failure:
+//   EPERM - dvrEdsInit was not called.
+// Check GL errors with glGetError for other error conditions.
+int dvrEdsBlitPoseFromCpu(int eye, int viewport_width, int viewport_height,
+                          const float* pose_quaternion,
+                          const float* pose_position);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_EDS_H_
diff --git a/libs/vr/libeds/include/private/dvr/color_channel_distortion.h b/libs/vr/libeds/include/private/dvr/color_channel_distortion.h
new file mode 100644
index 0000000..4e612cd
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/color_channel_distortion.h
@@ -0,0 +1,30 @@
+#ifndef ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
+#define ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// ColorChannelDistortion encapsulates the way one color channel (wavelength)
+// is distorted optically when an image is viewed through a lens.
+class ColorChannelDistortion {
+ public:
+  virtual ~ColorChannelDistortion() {}
+
+  // Given a 2d point p, returns the corresponding distorted point.
+  // The units of both the input and output points are tan-angle units,
+  // which can be computed as the distance on the screen divided by
+  // distance from the virtual eye to the screen.  For both the input
+  // and output points, the intersection of the optical axis of the lens
+  // with the screen defines the origin, the x axis points right, and
+  // the y axis points up.
+  virtual vec2 Distort(vec2 p) const = 0;
+
+  virtual vec2 DistortInverse(vec2 p) const = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_COLOR_CHANNEL_DISTORTION_H_
diff --git a/libs/vr/libeds/include/private/dvr/composite_hmd.h b/libs/vr/libeds/include/private/dvr/composite_hmd.h
new file mode 100644
index 0000000..70727e0
--- /dev/null
+++ b/libs/vr/libeds/include/private/dvr/composite_hmd.h
@@ -0,0 +1,89 @@
+#ifndef ANDROID_DVR_COMPOSITE_HMD_H_
+#define ANDROID_DVR_COMPOSITE_HMD_H_
+
+#include <private/dvr/display_metrics.h>
+#include <private/dvr/head_mount_metrics.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+// An intermediate structure composed of a head mount (described by
+// HeadMountMetrics) and a display (described by DisplayMetrics).
+class CompositeHmd {
+ public:
+  // Constructs a new CompositeHmd given a HeadMountMetrics and a
+  // DisplayMetrics.
+  CompositeHmd(const HeadMountMetrics& head_mount_metrics,
+               const DisplayMetrics& display_metrics);
+
+  CompositeHmd(CompositeHmd&& composite_hmd) = delete;
+  CompositeHmd(const CompositeHmd& composite_hmd) = delete;
+  CompositeHmd& operator=(CompositeHmd&& composite_hmd) = delete;
+  CompositeHmd& operator=(const CompositeHmd& composite_hmd) = delete;
+
+  // Headset metadata.
+  float GetTargetFrameDuration() const;
+  void ComputeDistortedVertex(EyeType eye, vec2 uv_in, vec2* vertex_out,
+                              vec2* uv_out) const;
+
+  // Eye-unspecific view accessors.
+  vec2i GetRecommendedRenderTargetSize() const;
+  Range2i GetDisplayRange() const;
+
+  // Eye-specific view accessors.
+  mat4 GetEyeFromHeadMatrix(EyeType eye) const;
+  FieldOfView GetEyeFov(EyeType eye) const;
+  Range2i GetEyeViewportBounds(EyeType eye) const;
+
+  // Set HeadMountMetrics and recompute everything that depends on
+  // HeadMountMetrics.
+  void SetHeadMountMetrics(const HeadMountMetrics& head_mount_metrics);
+
+  // Returns a reference to the |head_mount_metrics_| member.
+  const HeadMountMetrics& GetHeadMountMetrics() const;
+
+  // Set DisplayMetrics and recompute everything that depends on DisplayMetrics.
+  void SetDisplayMetrics(const DisplayMetrics& display_metrics);
+
+  // Returns a reference to the current display metrics.
+  const DisplayMetrics& GetDisplayMetrics() const;
+
+  // Compute the distorted point for a single channel.
+  vec2 ComputeDistortedPoint(EyeType eye, vec2 position,
+                             RgbColorChannel channel) const;
+
+  // Compute the inverse distorted point for a single channel.
+  vec2 ComputeInverseDistortedPoint(EyeType eye, vec2 position,
+                                    RgbColorChannel channel) const;
+
+ private:
+  FieldOfView eye_fov_[2];
+  Range2i eye_viewport_range_[2];
+  mat4 eye_from_head_matrix_[2];
+  Range2i display_range_;
+  vec2i recommended_render_target_size_;
+
+  // Per-eye scale and translation to convert from normalized Screen Space
+  // ([0:1]x[0:1]) to tan-angle space.
+  mat3 eye_tan_angle_from_norm_screen_matrix_[2];
+  mat3 eye_tan_angle_from_norm_screen_inv_matrix_[2];
+
+  // Per-eye scale and translation to convert from tan-angle space to normalized
+  // Texture Space ([0:1]x[0:1]).
+  mat3 eye_norm_texture_from_tan_angle_matrix_[2];
+  mat3 eye_norm_texture_from_tan_angle_inv_matrix_[2];
+
+  HeadMountMetrics head_mount_metrics_;
+  DisplayMetrics display_metrics_;
+
+  // Called by SetHeadMountMetrics/SetDisplayMetrics after metrics get changed.
+  // This function will update head_mount_metrics_/display_metrics_ based on the
+  // metrics supplied in the above two methods.
+  void MetricsChanged();
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_COMPOSITE_HMD_H_
diff --git a/libs/vr/libeds/include/private/dvr/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..5247020
--- /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 <log/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..549d864
--- /dev/null
+++ b/libs/vr/libeds/tests/eds_app_tests.cpp
@@ -0,0 +1,140 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.h>
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+namespace {
+
+#define POSE_BINDING 0
+
+#ifndef STRINGIFY
+#define STRINGIFY2(s) #s
+#define STRINGIFY(s) STRINGIFY2(s)
+#endif
+
+static const char g_vert_shader[] =
+    "layout(binding = " STRINGIFY(POSE_BINDING) ", std140)\n"
+    "uniform LateLatchData {\n"
+    "  mat4 uViewProjection;\n"
+    "};\n"
+    "void main() {\n"
+    "  vec2 verts[4];\n"
+    "  verts[0] = vec2(-1, -1);\n"
+    "  verts[1] = vec2(-1, 1);\n"
+    "  verts[2] = vec2(1, -1);\n"
+    "  verts[3] = vec2(1, 1);\n"
+    "  gl_Position = uViewProjection * vec4(verts[gl_VertexID], 0.0, 1.0);\n"
+    "}\n";
+
+static const char g_frag_shader[] =
+    "precision mediump float;\n"
+    "out vec4 outColor;\n"
+    "void main() {\n"
+    "  outColor = vec4(1.0);\n"
+    "}\n";
+
+DvrGraphicsContext* CreateContext(int* surface_width, int* surface_height) {
+  DvrGraphicsContext* context = nullptr;
+  int display_width = 0, display_height = 0;
+  float inter_lens_meters = 0.0f;
+  float left_fov[4] = {0.0f};
+  float right_fov[4] = {0.0f};
+  int disable_warp = 0;
+  int enable_late_latch = 1;
+  DvrSurfaceParameter surface_params[] = {
+      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+      DVR_SURFACE_PARAMETER_IN(ENABLE_LATE_LATCH, enable_late_latch),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, surface_width),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, surface_height),
+      DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+      DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+      DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+  dvrGraphicsContextCreate(surface_params, &context);
+  return context;
+}
+
+}  // namespace
+
+TEST(SensorAppTests, EdsWithLateLatch) {
+  int surface_width = 0, surface_height = 0;
+  DvrGraphicsContext* context = CreateContext(&surface_width, &surface_height);
+  ASSERT_NE(nullptr, context);
+
+  android::dvr::ShaderProgram shader(g_vert_shader, g_frag_shader);
+
+  for (int i = 0; i < 5; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+
+    const auto ident_mat = android::dvr::mat4::Identity();
+    const float* ident_mats[] = { ident_mat.data(), ident_mat.data() };
+    GLuint late_latch_buffer_id = 0;
+    int ret = dvrBeginRenderFrameLateLatch(context, 0, schedule.vsync_count, 2,
+                                           ident_mats, ident_mats, ident_mats,
+                                           &late_latch_buffer_id);
+    EXPECT_EQ(0, ret);
+    for (int eye = 0; eye < 2; ++eye) {
+      if (eye == 0)
+        glViewport(0, 0, surface_width / 2, surface_height);
+      else
+        glViewport(surface_width / 2, 0, surface_width / 2, surface_height);
+
+      glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+      shader.Use();
+
+      // Bind late latch pose matrix buffer.
+      glBindBufferRange(
+          GL_UNIFORM_BUFFER, POSE_BINDING, late_latch_buffer_id,
+          offsetof(DvrGraphicsLateLatchData, view_proj_matrix[eye]),
+          16 * sizeof(float));
+
+      // TODO(jbates): use transform feedback here to grab the vertex output
+      // and verify that it received late-latch pose data. Combine this with
+      // mocked pose data to verify that late-latching is working.
+      glDrawArrays(GL_POINTS, 0, 4);
+    }
+    dvrPresent(context);
+  }
+
+  glFinish();
+  dvrGraphicsContextDestroy(context);
+}
+
+TEST(SensorAppTests, EdsWithoutLateLatch) {
+  int surface_width = 0, surface_height = 0;
+  DvrGraphicsContext* context = CreateContext(&surface_width, &surface_height);
+  ASSERT_NE(nullptr, context);
+  DvrPose* client = dvrPoseCreate();
+  ASSERT_NE(nullptr, client);
+
+  for (int i = 0; i < 5; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+    DvrPoseAsync pose;
+    int ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+    ASSERT_EQ(0, ret);
+
+    dvrBeginRenderFrameEds(context, pose.orientation, pose.translation);
+    for (int eye = 0; eye < 2; ++eye) {
+      if (eye == 0)
+        glViewport(0, 0, surface_width / 2, surface_height);
+      else
+        glViewport(surface_width / 2, 0, surface_width / 2, surface_height);
+
+      glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+      EXPECT_EQ(0, ret);
+    }
+    dvrPresent(context);
+  }
+
+  dvrPoseDestroy(client);
+  dvrGraphicsContextDestroy(context);
+}
diff --git a/libs/vr/libimageio/Android.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..ac78179
--- /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 log/log.h if LOG_TO_STDERR is not defined.
+// If LOG_TO_STDERR is defined, then android logging macros (such as ALOGE)
+// would log to stderr. This is useful if the code is also being used/tested on
+// a desktop.
+
+#ifdef LOG_TO_STDERR
+#include <stdarg.h>
+#include <cstdio>
+
+#ifndef LOG_TAG
+#define LOG_TAG " "
+#endif  // LOG_TAG
+
+inline void LogToStderr(const char* severity, const char* fmt, ...) {
+  fprintf(stderr, "%s %s: ", LOG_TAG, severity);
+  va_list args;
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  va_end(args);
+  fprintf(stderr, "\n");
+  fflush(stderr);
+}
+
+#define ALOGE(fmt, ...) LogToStderr("ERROR", fmt, ##__VA_ARGS__)
+
+#define ALOGW(fmt, ...) LogToStderr("WARNING", fmt, ##__VA_ARGS__)
+
+#define ALOGI(fmt, ...) LogToStderr("INFO", fmt, ##__VA_ARGS__)
+
+#define ALOGV(fmt, ...) LogToStderr("VERBOSE", fmt, ##__VA_ARGS__)
+
+#else  // LOG_TO_STDERR
+#include <log/log.h>
+#endif  // LOG_TO_STDERR
+
+#endif  // LIB_LIBIMAGEIO_PRIVATE_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..175cedf
--- /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 <errno.h>
+#include <log/log.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "pdx/channel_handle.h"
+#include "pdx/file_handle.h"
+#include "pdx/message_reader.h"
+#include "pdx/message_writer.h"
+#include "pdx/service_endpoint.h"
+
+namespace android {
+namespace pdx {
+
+class Service;
+
+namespace opcodes {
+
+/*
+ * Reserved message opcodes used by libpdx. The reserved opcodes start at the
+ * max positive signed integer for the system and go down.
+ * In contrast, service opcodes start at zero and go up. This scheme leaves
+ * most of the positive integer space for services, a tiny fraction of the
+ * positive integer space for the framework, and the entire negative integer
+ * space for the kernel.
+ */
+#define PDX_OPCODE(name, n) name = ((-1U >> 1) - (n))  // 0x7fff..ffff - n
+
+enum {
+  // System message sent when a new client channel is open.
+  CHANNEL_OPEN = -1,
+  // System message sent when a channel is closed.
+  CHANNEL_CLOSE = -2,
+  // Request the service to reload system properties.
+  PDX_OPCODE(REPORT_SYSPROP_CHANGE, 0),
+  // Request the service to dump state and return it in a text buffer.
+  PDX_OPCODE(DUMP_STATE, 1),
+};
+
+}  // namespace opcodes
+
+/*
+ * Base class of service-side per-channel context classes.
+ */
+class Channel : public std::enable_shared_from_this<Channel> {
+ public:
+  Channel() {}
+  virtual ~Channel() {}
+
+  /*
+   * Utility to get a shared_ptr reference from the channel context pointer.
+   */
+  static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info);
+};
+
+/*
+ * Message class represents an RPC message, and implicitly the blocked sender
+ * waiting for a response. Every message should get a reply, at some point
+ * (unless the endpoint is closed), to prevent clients from blocking
+ * indefinitely. In order to enforce this and prevent leaking message ids,
+ * Message automatically replies with an error to the client on destruction,
+ * unless one of two things happens:
+ *
+ *     1. The service calls one of the reply methods before the Message is
+ *        destroyed.
+ *     2. The responsibility for the message is moved to another instance of
+ *        Message, using either move construction or move assignment.
+ *
+ * The second case is useful for services that need to delay responding to a
+ * sender until a later time. In this situation the service can move the
+ * Message to another instance in a suitable data structure for later use. The
+ * moved-to Message then takes on the same behavior and responsibilities
+ * described above.
+ */
+class Message : public OutputResourceMapper, public InputResourceMapper {
+ public:
+  Message();
+  Message(const MessageInfo& info);
+  ~Message();
+
+  /*
+   * Message objects support move construction and assignment.
+   */
+  Message(Message&& other);
+  Message& operator=(Message&& other);
+
+  /*
+   * Read/write payload, in either single buffer or iovec form.
+   */
+  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..daf9af8
--- /dev/null
+++ b/libs/vr/libpdx/service.cpp
@@ -0,0 +1,680 @@
+#define LOG_TAG "ServiceFramework"
+#include "pdx/service.h"
+
+#include <fcntl.h>
+#include <log/log.h>
+#include <utils/misc.h>
+
+#include <algorithm>
+#include <cstdint>
+
+#include <pdx/trace.h>
+#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 100755
index 0000000..2217819
--- /dev/null
+++ b/libs/vr/libposepredictor/Android.mk
@@ -0,0 +1,55 @@
+# 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 := \
+        predictor.cpp \
+        buffered_predictor.cpp \
+        linear_predictor.cpp \
+        polynomial_predictor.cpp \
+        dvr_pose_predictor.cpp \
+
+includeFiles := \
+        $(LOCAL_PATH)/include \
+        external/eigen \
+
+staticLibraries := \
+        libsensor \
+
+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 := \
+        predictor_tests.cpp \
+        linear_predictor_tests.cpp \
+        polynomial_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/buffered_predictor.cpp b/libs/vr/libposepredictor/buffered_predictor.cpp
new file mode 100644
index 0000000..f3b41dc
--- /dev/null
+++ b/libs/vr/libposepredictor/buffered_predictor.cpp
@@ -0,0 +1,38 @@
+#include <buffered_predictor.h>
+
+namespace posepredictor {
+
+BufferedPredictor::BufferedPredictor(size_t buffer_size) {
+  buffer_.resize(buffer_size);
+}
+
+void BufferedPredictor::BufferSample(const Pose& sample) {
+  const auto& prev_sample = buffer_[current_pose_index_];
+
+  // If we are updating a sample (the same time stamp), do not advance the
+  // counter.
+  if (sample.time_ns != prev_sample.time_ns) {
+    current_pose_index_ = (current_pose_index_ + 1) % buffer_.size();
+  }
+
+  buffer_[current_pose_index_] = sample;
+
+  // Make sure the subsequent orientations are the closest in quaternion space.
+  if (PrevSample(1).orientation.coeffs().dot(sample.orientation.coeffs()) < 0) {
+    // Flip the quaternion to be closest to the previous sample.
+    buffer_[current_pose_index_].orientation =
+        quat(-sample.orientation.w(), -sample.orientation.x(),
+             -sample.orientation.y(), -sample.orientation.z());
+  }
+
+  ++num_poses_added_;
+}
+
+const Pose& BufferedPredictor::PrevSample(size_t index) const {
+  // We must not request a pose too far in the past.
+  assert(index < buffer_.size());
+  return buffer_[(current_pose_index_ - index + buffer_.size()) %
+                 buffer_.size()];
+}
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/dvr_pose_predictor.cpp b/libs/vr/libposepredictor/dvr_pose_predictor.cpp
new file mode 100644
index 0000000..7f2ecc0
--- /dev/null
+++ b/libs/vr/libposepredictor/dvr_pose_predictor.cpp
@@ -0,0 +1,70 @@
+#include <private/dvr/dvr_pose_predictor.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+template <typename Vec3Type>
+float32x4_t FromVec3(const Vec3Type& from) {
+  return {static_cast<float>(from.x()), static_cast<float>(from.y()),
+          static_cast<float>(from.z()), 0};
+}
+
+template <typename QuatType>
+float32x4_t FromQuat(const QuatType& from) {
+  return {static_cast<float>(from.x()), static_cast<float>(from.y()),
+          static_cast<float>(from.z()), static_cast<float>(from.w())};
+}
+
+}  //  namespace
+
+void AddPredictorPose(posepredictor::Predictor* predictor,
+                      const posepredictor::vec3& start_t_head,
+                      const posepredictor::quat& start_q_head,
+                      int64_t pose_timestamp, DvrPoseAsync* out) {
+  // Feed the predictor.
+  predictor->Add(
+      posepredictor::Pose{pose_timestamp, start_t_head, start_q_head});
+
+  // Fill the output.
+  out->timestamp_ns = pose_timestamp;
+
+  out->translation = FromVec3(start_t_head);
+  out->orientation = FromQuat(start_q_head);
+
+  out->right_translation = out->translation;
+  out->right_orientation = out->orientation;
+
+  const auto velocity = predictor->PredictVelocity(pose_timestamp);
+
+  out->velocity = FromVec3(velocity.linear);
+  out->angular_velocity = FromVec3(velocity.angular);
+
+  out->flags = DVR_POSE_FLAG_HEAD | DVR_POSE_FLAG_VALID;
+  memset(out->pad, 0, sizeof(out->pad));
+}
+
+void PredictPose(const posepredictor::Predictor* predictor, int64_t left_ns,
+                 int64_t right_ns, DvrPoseAsync* out) {
+  const auto left_pose = predictor->Predict(left_ns);
+  const auto right_pose = predictor->Predict(right_ns);
+  const auto velocity = predictor->PredictVelocity((left_ns + right_ns) / 2);
+
+  // Fill the output.
+  out->timestamp_ns = left_ns;
+
+  out->translation = FromVec3(left_pose.position);
+  out->orientation = FromQuat(left_pose.orientation);
+
+  out->right_translation = FromVec3(right_pose.position);
+  out->right_orientation = FromQuat(right_pose.orientation);
+
+  out->velocity = FromVec3(velocity.linear);
+  out->angular_velocity = FromVec3(velocity.angular);
+
+  out->flags = DVR_POSE_FLAG_HEAD | DVR_POSE_FLAG_VALID;
+  memset(out->pad, 0, sizeof(out->pad));
+}
+
+}  //  dvr
+}  //  android
diff --git a/libs/vr/libposepredictor/include/buffered_predictor.h b/libs/vr/libposepredictor/include/buffered_predictor.h
new file mode 100644
index 0000000..eab0150
--- /dev/null
+++ b/libs/vr/libposepredictor/include/buffered_predictor.h
@@ -0,0 +1,40 @@
+#ifndef POSEPREDICTOR_BUFFERED_PREDICTOR_H_
+#define POSEPREDICTOR_BUFFERED_PREDICTOR_H_
+
+#include <vector>
+
+#include "predictor.h"
+
+namespace posepredictor {
+
+// Keeps the previous n poses around in a ring buffer.
+// The orientations are also unrolled so that a . b > 0 for two subsequent
+// quaternions a and b.
+class BufferedPredictor : public Predictor {
+ public:
+  BufferedPredictor(size_t buffer_size);
+  ~BufferedPredictor() = default;
+
+ protected:
+  // Add a pose sample into the buffer.
+  void BufferSample(const Pose& sample);
+
+  // Grab a previous sample.
+  // index = 0: last sample
+  // index = 1: the one before that
+  // ...
+  const Pose& PrevSample(size_t index) const;
+
+  // Where we keep the last n poses.
+  std::vector<Pose> buffer_;
+
+  // Where the last valid pose is in the buffer.
+  size_t current_pose_index_ = 0;
+
+  // The number of poses we have added.
+  size_t num_poses_added_ = 0;
+};
+
+}  // namespace posepredictor
+
+#endif  // POSEPREDICTOR_BUFFERED_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/linear_predictor.h b/libs/vr/libposepredictor/include/linear_predictor.h
new file mode 100644
index 0000000..0d17ec5
--- /dev/null
+++ b/libs/vr/libposepredictor/include/linear_predictor.h
@@ -0,0 +1,43 @@
+#ifndef POSEPREDICTOR_LINEAR_POSE_PREDICTOR_H_
+#define POSEPREDICTOR_LINEAR_POSE_PREDICTOR_H_
+
+#include "predictor.h"
+
+namespace posepredictor {
+
+// This class makes a linear prediction using the last two samples we received.
+class LinearPosePredictor : public Predictor {
+ public:
+  LinearPosePredictor() = default;
+
+  // Add a new sample.
+  void Add(const Pose& sample) override;
+
+  // Predict using the last two samples.
+  Pose Predict(int64_t time_ns) const override;
+
+  // Just copy the velocity over.
+  Velocity PredictVelocity(int64_t time_ns) const override;
+
+ private:
+  // The index of the last sample we received.
+  size_t current_index_ = 0;
+
+  // The previous two samples.
+  Pose samples_[2];
+
+  // Experimental
+  bool forward_predict_angular_speed_ = false;
+
+  // Transient variables updated when a sample is added.
+  vec3 velocity_ = vec3::Zero();
+  vec3 rotational_velocity_ = vec3::Zero();
+  vec3 rotational_axis_ = vec3::Zero();
+  real last_angular_speed_ = 0;
+  real angular_speed_ = 0;
+  real angular_accel_ = 0;
+};
+
+}  // namespace posepredictor
+
+#endif  // POSEPREDICTOR_LINEAR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/polynomial_predictor.h b/libs/vr/libposepredictor/include/polynomial_predictor.h
new file mode 100644
index 0000000..762afd3
--- /dev/null
+++ b/libs/vr/libposepredictor/include/polynomial_predictor.h
@@ -0,0 +1,168 @@
+#ifndef POSEPREDICTOR_POLYNOMIAL_POSE_PREDICTOR_H_
+#define POSEPREDICTOR_POLYNOMIAL_POSE_PREDICTOR_H_
+
+#include <vector>
+
+#include <Eigen/Dense>
+
+#include "buffered_predictor.h"
+
+namespace posepredictor {
+
+// Make a polynomial prediction of the form
+// y = coefficients_[0] + coefficients_[1] * t + coefficients_[2] * t^2 + ...
+// where t is time and y is the position and orientation.
+// We recompute the coefficients whenever we add a new sample using
+// training_window previous samples.
+template <size_t PolynomialDegree, size_t TrainingWindow>
+class PolynomialPosePredictor : public BufferedPredictor {
+ public:
+  PolynomialPosePredictor(real regularization = 1e-9)
+      : BufferedPredictor(TrainingWindow), regularization_(regularization) {
+    static_assert(PolynomialDegree + 1 >= TrainingWindow,
+                  "Underconstrained polynomial regressor");
+  }
+
+  ~PolynomialPosePredictor() = default;
+
+  // We convert pose samples into a vector for matrix arithmetic using this
+  // mapping.
+  enum Components {
+    kPositionX = 0,
+    kPositionY,
+    kPositionZ,
+    kOrientationX,
+    kOrientationY,
+    kOrientationZ,
+    kOrientationW,
+    kNumComponents
+  };
+
+  // Add a new sample.
+  void Add(const Pose& sample) override {
+    // Add the sample to the ring buffer.
+    BufferedPredictor::BufferSample(sample);
+
+    Eigen::Matrix<real, TrainingWindow, kNumComponents> values;
+
+    // Get the pose samples into matrices for fitting.
+    real t_vector[TrainingWindow];
+    for (size_t i = 0; i < TrainingWindow; ++i) {
+      const auto& prev_sample = PrevSample(i);
+
+      t_vector[i] = NsToT(prev_sample.time_ns);
+
+      // Save the values we will be fitting to at each sample time.
+      values(i, kPositionX) = prev_sample.position.x();
+      values(i, kPositionY) = prev_sample.position.y();
+      values(i, kPositionZ) = prev_sample.position.z();
+      values(i, kOrientationX) = prev_sample.orientation.x();
+      values(i, kOrientationY) = prev_sample.orientation.y();
+      values(i, kOrientationZ) = prev_sample.orientation.z();
+      values(i, kOrientationW) = prev_sample.orientation.w();
+    }
+
+    // Some transient matrices for solving for coefficient matrix.
+    Eigen::Matrix<real, PolynomialDegree + 1, PolynomialDegree + 1> M;
+    Eigen::Matrix<real, PolynomialDegree + 1, 1> d;
+    Eigen::Matrix<real, PolynomialDegree + 1, 1> p;
+
+    // Create a polynomial fit for each component.
+    for (size_t component = 0; component < kNumComponents; ++component) {
+      // A = [ 1 t t^2 ... ]'
+      // x = [ coefficients[0] coefficients[1] .... ]'
+      // b = [ position.x ]'
+      // We would like to solve A' x + regularization * I = b'
+      // given the samples we have in our training window.
+      //
+      // The loop below will compute:
+      // M = A' * A
+      // d = A' * b
+      // so we can solve M * coefficients + regularization * I = b
+
+      M.setIdentity();
+      d.setZero();
+      p[0] = 1;
+
+      // M = regularization * I
+      M = M * regularization_;
+
+      // Accumulate the poses in the training window.
+      for (size_t i = 0; i < TrainingWindow; ++i) {
+        // Compute the polynomial at this sample.
+        for (size_t j = 1; j <= PolynomialDegree; ++j) {
+          p[j] = p[j - 1] * t_vector[i];
+        }
+
+        // Accumulate the left and right hand sides.
+        M = M + p * p.transpose();
+        d = d + p * values(i, component);
+      }
+
+      // M is symmetric, positive semi-definite.
+      // Note: This is not the most accurate solver out there but is fast.
+      coefficients_.row(component) = Eigen::LLT<Eigen::MatrixXd>(M).solve(d);
+    }
+  }
+
+  // Predict using the polynomial coefficients.
+  Pose Predict(int64_t time_ns) const override {
+    // Predict the left side.
+    const auto components = SamplePolynomial(time_ns);
+
+    return {time_ns,
+            vec3(components[kPositionX], components[kPositionY],
+                 components[kPositionZ]),
+            quat(components[kOrientationW], components[kOrientationX],
+                 components[kOrientationY], components[kOrientationZ])
+                .normalized()};
+  }
+
+ private:
+  // Evaluate the polynomial at a particular time.
+  Eigen::Matrix<real, kNumComponents, 1> SamplePolynomial(
+      int64_t time_ns) const {
+    const auto t = NsToT(time_ns);
+    Eigen::Matrix<real, PolynomialDegree + 1, 1> polynomial;
+    real current_polynomial = t;
+
+    // Compute polynomial = [ 1 t t^2 ... ]
+    polynomial[0] = 1;
+    for (size_t degree = 1; degree <= PolynomialDegree;
+         ++degree, current_polynomial *= t) {
+      polynomial[degree] = polynomial[degree - 1] * t;
+    }
+
+    // The coefficients_ = [ numComponents x (polynomial degree + 1) ].
+    return coefficients_ * polynomial;
+  }
+
+  // Convert a time in nanoseconds to t.
+  // We could use the seconds as t but this would create make it more difficult
+  // to tweak the regularization amount. So we subtract the last sample time so
+  // the scale of the regularization constant doesn't change as a function of
+  // time.
+  real NsToT(int64_t time_ns) const {
+    return NsToSeconds(time_ns - buffer_[current_pose_index_].time_ns);
+  }
+
+  // The ridge regularization constant.
+  real regularization_;
+
+  // This is where we store the polynomial coefficients.
+  Eigen::Matrix<real, kNumComponents, PolynomialDegree + 1> coefficients_;
+};
+
+// Some common polynomial types.
+extern template class PolynomialPosePredictor<1, 2>;
+extern template class PolynomialPosePredictor<2, 3>;
+extern template class PolynomialPosePredictor<3, 4>;
+extern template class PolynomialPosePredictor<4, 5>;
+
+using QuadricPosePredictor = PolynomialPosePredictor<2, 3>;
+using CubicPosePredictor = PolynomialPosePredictor<3, 4>;
+using QuarticPosePredictor = PolynomialPosePredictor<4, 5>;
+
+}  // namespace posepredictor
+
+#endif  // POSEPREDICTOR_POLYNOMIAL_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/predictor.h b/libs/vr/libposepredictor/include/predictor.h
new file mode 100644
index 0000000..78db272
--- /dev/null
+++ b/libs/vr/libposepredictor/include/predictor.h
@@ -0,0 +1,73 @@
+#ifndef POSEPREDICTOR_POSE_PREDICTOR_H_
+#define POSEPREDICTOR_POSE_PREDICTOR_H_
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+// This is the only file you need to include for pose prediction.
+
+namespace posepredictor {
+
+// The precision for the predictor.
+// TODO(okana): double precision is probably not necessary.
+typedef double real;
+
+using vec3 = Eigen::Matrix<real, 3, 1>;
+using quat = Eigen::Quaternion<real>;
+
+// Encapsulates a pose sample.
+struct Pose {
+  int64_t time_ns = 0;
+  vec3 position = vec3::Zero();
+  quat orientation = quat::Identity();
+};
+
+// Encapsulates the derivative at a time.
+struct Velocity {
+  vec3 linear = vec3::Zero();
+  vec3 angular = vec3::Zero();
+};
+
+// The preset types we support.
+enum class PredictorType { Linear, Quadric, Cubic };
+
+// This is an abstract base class for prediction 6dof pose given
+// a set of samples.
+class Predictor {
+ public:
+  Predictor() = default;
+  virtual ~Predictor() = default;
+
+  // The nanoseconds to use for finite differencing.
+  static constexpr int64_t kFiniteDifferenceNs = 100;
+
+  // Instantiate a new pose predictor for a type.
+  static std::unique_ptr<Predictor> Create(PredictorType type);
+
+  // Compute the angular velocity from orientation start_orientation to
+  // end_orientation in delta_time.
+  static vec3 AngularVelocity(const quat& start_orientation,
+                              const quat& end_orientation, real delta_time);
+
+  // Add a pose sample coming from the sensors.
+  virtual void Add(const Pose& sample) = 0;
+
+  // Make a pose prediction for at specific time.
+  virtual Pose Predict(int64_t time_ns) const = 0;
+
+  // Evaluate velocity at a particular time.
+  // The default implementation uses finite differencing.
+  virtual Velocity PredictVelocity(int64_t time_ns) const;
+
+  // Helpers
+  static real NsToSeconds(int64_t time_ns) {
+    return static_cast<real>(time_ns / 1e9);
+  }
+  static int64_t SecondsToNs(real seconds) {
+    return static_cast<int64_t>(seconds * 1e9);
+  }
+};
+
+}  // namespace posepredictor
+
+#endif  // POSEPREDICTOR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/include/private/dvr/dvr_pose_predictor.h b/libs/vr/libposepredictor/include/private/dvr/dvr_pose_predictor.h
new file mode 100644
index 0000000..bd2dcbc
--- /dev/null
+++ b/libs/vr/libposepredictor/include/private/dvr/dvr_pose_predictor.h
@@ -0,0 +1,25 @@
+#ifndef ANDROID_DVR_POSE_PREDICTOR_H_
+#define ANDROID_DVR_POSE_PREDICTOR_H_
+
+#include <dvr/pose_client.h>
+#include <predictor.h>
+
+// Some shim functions for connecting dvr to pose predictor.
+
+namespace android {
+namespace dvr {
+
+// Feed a pose to the predictor.
+void AddPredictorPose(posepredictor::Predictor* predictor,
+                      const posepredictor::vec3& start_t_head,
+                      const posepredictor::quat& start_q_head,
+                      int64_t pose_timestamp, DvrPoseAsync* out);
+
+// Make a prediction for left and right eyes.
+void PredictPose(const posepredictor::Predictor* predictor, int64_t left_ns,
+                 int64_t right_ns, DvrPoseAsync* out);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_POSE_PREDICTOR_H_
diff --git a/libs/vr/libposepredictor/linear_predictor.cpp b/libs/vr/libposepredictor/linear_predictor.cpp
new file mode 100644
index 0000000..6f924dc
--- /dev/null
+++ b/libs/vr/libposepredictor/linear_predictor.cpp
@@ -0,0 +1,70 @@
+#include <linear_predictor.h>
+
+namespace posepredictor {
+
+using AngleAxis = Eigen::AngleAxis<real>;
+
+void LinearPosePredictor::Add(const Pose& sample) {
+  // If we are receiving a new sample, move the index to the next item.
+  // If the time stamp is the same as the last frame, we will just overwrite
+  // it with the new data.
+  if (sample.time_ns != samples_[current_index_].time_ns) {
+    current_index_ ^= 1;
+  }
+
+  // Save the sample.
+  samples_[current_index_] = sample;
+
+  // The previous sample we received.
+  const auto& previous_sample = samples_[current_index_ ^ 1];
+
+  // Ready to compute velocities.
+  const auto pose_delta_time =
+      NsToSeconds(sample.time_ns - previous_sample.time_ns);
+
+  if (pose_delta_time > 0.0) {
+    velocity_ = (sample.position - previous_sample.position) / pose_delta_time;
+    rotational_velocity_ = Predictor::AngularVelocity(
+        previous_sample.orientation, sample.orientation, pose_delta_time);
+  } else {
+    velocity_ = vec3::Zero();
+    rotational_velocity_ = vec3::Zero();
+  }
+
+  // Temporary experiment with acceleration estimate.
+  angular_speed_ = rotational_velocity_.norm();
+  angular_accel_ = 0.0;
+  if (forward_predict_angular_speed_) {
+    angular_accel_ =
+        pose_delta_time > 0.0
+            ? (angular_speed_ - last_angular_speed_) / pose_delta_time
+            : 0.0;
+  }
+  last_angular_speed_ = angular_speed_;
+
+  rotational_axis_ = vec3(0.0, 1.0, 0.0);
+  if (angular_speed_ > 0.0) {
+    rotational_axis_ = rotational_velocity_ / angular_speed_;
+  }
+}
+
+Pose LinearPosePredictor::Predict(int64_t time_ns) const {
+  const auto& sample = samples_[current_index_];
+
+  const auto dt = NsToSeconds(time_ns - sample.time_ns);
+
+  // Temporary forward prediction code.
+  auto angle = angular_speed_ * dt;
+  if (__builtin_expect(forward_predict_angular_speed_, 0)) {
+    angle += 0.5 * angular_accel_ * dt * dt;
+  }
+
+  return {time_ns, sample.position + velocity_ * dt,
+          sample.orientation * quat(AngleAxis(angle, rotational_axis_))};
+}
+
+Velocity LinearPosePredictor::PredictVelocity(int64_t /* time_ns */) const {
+  return {velocity_, rotational_velocity_};
+}
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/linear_predictor_tests.cpp b/libs/vr/libposepredictor/linear_predictor_tests.cpp
new file mode 100644
index 0000000..d94aa2d
--- /dev/null
+++ b/libs/vr/libposepredictor/linear_predictor_tests.cpp
@@ -0,0 +1,170 @@
+#include <gtest/gtest.h>
+
+#include <linear_predictor.h>
+
+namespace posepredictor {
+
+namespace {
+
+// For comparing expected and actual.
+constexpr real kAbsErrorTolerance = 1e-5;
+
+// The default rotation axis we will be using.
+const vec3 kRotationAxis = vec3(1, 4, 3).normalized();
+
+// Linearly interpolate between a and b.
+vec3 lerp(const vec3& a, const vec3& b, real t) { return (b - a) * t + a; }
+
+// Linearly interpolate between two angles and return the resulting rotation as
+// a quaternion (around the kRotationAxis).
+quat qlerp(real angle1, real angle2, real t) {
+  return quat(
+      Eigen::AngleAxis<real>((angle2 - angle1) * t + angle1, kRotationAxis));
+}
+
+// Compare two positions.
+void TestPosition(const vec3& expected, const vec3& actual) {
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_NEAR(expected[i], actual[i], kAbsErrorTolerance);
+  }
+}
+
+// Compare two orientations.
+void TestOrientation(const quat& expected, const quat& actual) {
+  // abs(expected.dot(actual)) > 1-eps
+  EXPECT_GE(std::abs(actual.coeffs().dot(expected.coeffs())), 0.99);
+}
+}
+
+// Test the extrapolation from two samples.
+TEST(LinearPosePredictorTest, Extrapolation) {
+  LinearPosePredictor predictor;
+
+  // We wil extrapolate linearly from [position|orientation] 1 -> 2.
+  const vec3 position1(0, 0, 0);
+  const vec3 position2(1, 2, 3);
+  const real angle1 = M_PI * 0.3;
+  const real angle2 = M_PI * 0.5;
+  const quat orientation1(Eigen::AngleAxis<real>(angle1, kRotationAxis));
+  const quat orientation2(Eigen::AngleAxis<real>(angle2, kRotationAxis));
+  const int64_t t1_ns = 0;           //< First sample time stamp
+  const int64_t t2_ns = 10;          //< The second sample time stamp
+  const int64_t eval_left_ns = 23;   //< The eval time for left
+  const int64_t eval_right_ns = 31;  //< The eval time for right
+  Pose start_pose, end_pose, extrapolated_pose;
+
+  predictor.Add(Pose{
+      .position = position1, .orientation = orientation1, .time_ns = t1_ns});
+
+  predictor.Add(Pose{
+      .position = position2, .orientation = orientation2, .time_ns = t2_ns});
+
+  // Extrapolate from t1 - t2 to eval_[left/right].
+  extrapolated_pose = predictor.Predict(eval_left_ns);
+
+  // The interpolation factors for left and right.
+  const auto left_t = (eval_left_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+  EXPECT_EQ(2.3, left_t);
+
+  TestPosition(lerp(position1, position2, left_t), extrapolated_pose.position);
+
+  TestOrientation(qlerp(angle1, angle2, left_t), extrapolated_pose.orientation);
+
+  extrapolated_pose = predictor.Predict(eval_right_ns);
+
+  const auto right_t =
+      (eval_right_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+  EXPECT_EQ(3.1, right_t);
+
+  TestPosition(lerp(position1, position2, right_t), extrapolated_pose.position);
+
+  TestOrientation(qlerp(angle1, angle2, right_t),
+                  extrapolated_pose.orientation);
+}
+
+// Test three samples, where the last two samples have the same timestamp.
+TEST(LinearPosePredictorTest, DuplicateSamples) {
+  LinearPosePredictor predictor;
+
+  const vec3 position1(0, 0, 0);
+  const vec3 position2(1, 2, 3);
+  const vec3 position3(2, 2, 3);
+  const real angle1 = M_PI * 0.3;
+  const real angle2 = M_PI * 0.5;
+  const real angle3 = M_PI * 0.65;
+  const quat orientation1(Eigen::AngleAxis<real>(angle1, kRotationAxis));
+  const quat orientation2(Eigen::AngleAxis<real>(angle2, kRotationAxis));
+  const quat orientation3(Eigen::AngleAxis<real>(angle3, kRotationAxis));
+  const int64_t t1_ns = 0;
+  const int64_t t2_ns = 10;
+  const int64_t eval_left_ns = 27;
+  const int64_t eval_right_ns = 31;
+  Pose extrapolated_pose;
+
+  predictor.Add(Pose{
+      .position = position1, .orientation = orientation1, .time_ns = t1_ns});
+
+  predictor.Add(Pose{
+      .position = position2, .orientation = orientation2, .time_ns = t2_ns});
+
+  {
+    // Extrapolate from t1 - t2 to eval_[left/right].
+    extrapolated_pose = predictor.Predict(eval_left_ns);
+
+    // The interpolation factors for left and right.
+    const auto left_t =
+        (eval_left_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+
+    // Test the result.
+    TestPosition(lerp(position1, position2, left_t),
+                 extrapolated_pose.position);
+
+    TestOrientation(qlerp(angle1, angle2, left_t),
+                    extrapolated_pose.orientation);
+
+    extrapolated_pose = predictor.Predict(eval_right_ns);
+
+    const auto right_t =
+        (eval_right_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+
+    TestPosition(lerp(position1, position2, right_t),
+                 extrapolated_pose.position);
+
+    TestOrientation(qlerp(angle1, angle2, right_t),
+                    extrapolated_pose.orientation);
+  }
+
+  // Sending a duplicate sample here.
+  predictor.Add(Pose{
+      .position = position3, .orientation = orientation3, .time_ns = t2_ns});
+
+  {
+    // Extrapolate from t1 - t2 to eval_[left/right].
+    extrapolated_pose = predictor.Predict(eval_left_ns);
+
+    // The interpolation factors for left and right.
+    const auto left_t =
+        (eval_left_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+
+    TestPosition(lerp(position1, position3, left_t),
+                 extrapolated_pose.position);
+
+    TestOrientation(qlerp(angle1, angle3, left_t),
+                    extrapolated_pose.orientation);
+
+    extrapolated_pose = predictor.Predict(eval_right_ns);
+
+    const auto right_t =
+        (eval_right_ns - t1_ns) / static_cast<real>(t2_ns - t1_ns);
+
+    // Test the result.
+
+    TestPosition(lerp(position1, position3, right_t),
+                 extrapolated_pose.position);
+
+    TestOrientation(qlerp(angle1, angle3, right_t),
+                    extrapolated_pose.orientation);
+  }
+}
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/polynomial_predictor.cpp b/libs/vr/libposepredictor/polynomial_predictor.cpp
new file mode 100644
index 0000000..98fd28a
--- /dev/null
+++ b/libs/vr/libposepredictor/polynomial_predictor.cpp
@@ -0,0 +1,11 @@
+#include <polynomial_predictor.h>
+
+namespace posepredictor {
+
+// Instantiate the common polynomial types.
+template class PolynomialPosePredictor<1, 2>;
+template class PolynomialPosePredictor<2, 3>;
+template class PolynomialPosePredictor<3, 4>;
+template class PolynomialPosePredictor<4, 5>;
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/polynomial_predictor_tests.cpp b/libs/vr/libposepredictor/polynomial_predictor_tests.cpp
new file mode 100644
index 0000000..88cb2b9
--- /dev/null
+++ b/libs/vr/libposepredictor/polynomial_predictor_tests.cpp
@@ -0,0 +1,120 @@
+#include <gtest/gtest.h>
+
+#include <polynomial_predictor.h>
+
+namespace posepredictor {
+
+namespace {
+
+// For comparing expected and actual.
+constexpr real kAbsErrorTolerance = 1e-5;
+
+// Test the linear extrapolation from two samples.
+TEST(PolynomialPosePredictor, Linear) {
+  // Degree = 1, simple line, passing through two points.
+  // Note the regularization is 0 so we expect the exact fit.
+  PolynomialPosePredictor<1, 2> predictor(0);
+
+  // Add two samples.
+  predictor.Add(
+      Pose{.position = {0, 0, 0}, .orientation = {0, 0, 0, 1}, .time_ns = 0});
+
+  predictor.Add(
+      Pose{.position = {1, 2, 3}, .orientation = {0, 0, 0, 1}, .time_ns = 10});
+
+  Pose predicted_pose;
+
+  predicted_pose = predictor.Predict(20);
+
+  // Check the x,y,z components for the expected translation.
+  EXPECT_NEAR(predicted_pose.position[0], 2, kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[1], 4, kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[2], 6, kAbsErrorTolerance);
+
+  predicted_pose = predictor.Predict(30);
+  EXPECT_NEAR(predicted_pose.position[0], 3, kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[1], 6, kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[2], 9, kAbsErrorTolerance);
+}
+
+// Test the degree two polynomial fit.
+TEST(PolynomialPosePredictor, Quadric) {
+  // Degree = 2, need three samples to fit a polynomial.
+  // Note the regularization is 0 so we expect the exact fit.
+  PolynomialPosePredictor<2, 3> predictor(0);
+
+  // Add three samples.
+  predictor.Add(
+      Pose{.position = {1, 2, 3}, .orientation = {0, 0, 0, 1}, .time_ns = 0});
+
+  predictor.Add(
+      Pose{.position = {0, 0, 0}, .orientation = {0, 0, 0, 1}, .time_ns = 10});
+
+  predictor.Add(
+      Pose{.position = {1, 2, 3}, .orientation = {0, 0, 0, 1}, .time_ns = 20});
+
+  // The expected polynomials for x/y/z.
+
+  // x:  0.01 * t^2 - 0.2 * t + 1
+  const auto x = [](auto t) { return 0.01 * t * t - 0.2 * t + 1; };
+
+  // y:  0.02 * t^2 - 0.4 * t + 2
+  const auto y = [](auto t) { return 0.02 * t * t - 0.4 * t + 2; };
+
+  // z:  0.03 * t^2 - 0.6 * t + 3
+  const auto z = [](auto t) { return 0.03 * t * t - 0.6 * t + 3; };
+
+  Pose predicted_pose;
+  predicted_pose = predictor.Predict(40);
+
+  // Check the x,y,z components for the expected translation.
+  EXPECT_NEAR(predicted_pose.position[0], x(40), kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[1], y(40), kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[2], z(40), kAbsErrorTolerance);
+
+  predicted_pose = predictor.Predict(50);
+  EXPECT_NEAR(predicted_pose.position[0], x(50), kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[1], y(50), kAbsErrorTolerance);
+  EXPECT_NEAR(predicted_pose.position[2], z(50), kAbsErrorTolerance);
+}
+
+// Test the degree two polynomial fit with degenerate input.
+//
+// The input samples all lie in a line which would normally make our system
+// degenerate. We will rely on the regularization term to recover the linear
+// solution in a quadric predictor.
+TEST(PolynomialPosePredictor, QuadricDegenate) {
+  // Degree = 2, need three samples to fit a polynomial.
+  // Note that we are using the default regularization term here.
+  // We cannot use 0 regularizer since the input is degenerate.
+  PolynomialPosePredictor<2, 3> predictor(1e-20);
+
+  // Add three samples.
+  predictor.Add(
+      Pose{.position = {0, 0, 0}, .orientation = {0, 0, 0, 1}, .time_ns = 0});
+
+  predictor.Add(
+      Pose{.position = {1, 2, 3}, .orientation = {0, 0, 0, 1}, .time_ns = 10});
+
+  predictor.Add(
+      Pose{.position = {2, 4, 6}, .orientation = {0, 0, 0, 1}, .time_ns = 20});
+
+  Pose predicted_pose;
+
+  predicted_pose = predictor.Predict(30);
+
+  // Check the x,y,z components for the expected translation.
+  // We are using a higher error threshold since this is now approximate.
+  EXPECT_NEAR(predicted_pose.position[0], 3, 0.001);
+  EXPECT_NEAR(predicted_pose.position[1], 6, 0.001);
+  EXPECT_NEAR(predicted_pose.position[2], 9, 0.001);
+
+  predicted_pose = predictor.Predict(40);
+  EXPECT_NEAR(predicted_pose.position[0], 4, 0.001);
+  EXPECT_NEAR(predicted_pose.position[1], 8, 0.001);
+  EXPECT_NEAR(predicted_pose.position[2], 12, 0.001);
+}
+
+}  // namespace
+
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/predictor.cpp b/libs/vr/libposepredictor/predictor.cpp
new file mode 100644
index 0000000..4d2eafd
--- /dev/null
+++ b/libs/vr/libposepredictor/predictor.cpp
@@ -0,0 +1,34 @@
+#include <linear_predictor.h>
+#include <polynomial_predictor.h>
+#include <predictor.h>
+
+namespace posepredictor {
+
+vec3 Predictor::AngularVelocity(const quat& a, const quat& b, real delta_time) {
+  const auto delta_q = b.inverse() * a;
+  // Check that delta_q.w() == 1, Eigen doesn't respect this convention. If
+  // delta_q.w() == -1, we'll get the opposite velocity.
+  return 2.0 * (delta_q.w() < 0 ? static_cast<vec3>(-delta_q.vec()) : delta_q.vec()) / delta_time;
+}
+
+Velocity Predictor::PredictVelocity(int64_t time_ns) const {
+  const auto a = Predict(time_ns - kFiniteDifferenceNs);
+  const auto b = Predict(time_ns + kFiniteDifferenceNs);
+  const auto delta_time = NsToSeconds(2 * kFiniteDifferenceNs);
+
+  return {(b.position - a.position) / delta_time,
+          AngularVelocity(a.orientation, b.orientation, delta_time)};
+}
+
+// The factory method.
+std::unique_ptr<Predictor> Predictor::Create(PredictorType type) {
+  switch (type) {
+    case PredictorType::Linear:
+      return std::make_unique<LinearPosePredictor>();
+    case PredictorType::Quadric:
+      return std::make_unique<QuadricPosePredictor>();
+    case PredictorType::Cubic:
+      return std::make_unique<CubicPosePredictor>();
+  }
+}
+}  // namespace posepredictor
diff --git a/libs/vr/libposepredictor/predictor_tests.cpp b/libs/vr/libposepredictor/predictor_tests.cpp
new file mode 100644
index 0000000..e84a93a
--- /dev/null
+++ b/libs/vr/libposepredictor/predictor_tests.cpp
@@ -0,0 +1,50 @@
+#include <gtest/gtest.h>
+
+#include <predictor.h>
+
+namespace posepredictor {
+
+namespace {
+
+// For comparing expected and actual.
+constexpr real kAbsErrorTolerance = 1e-4;
+
+// Test the angular velocity computation from two orientations.
+TEST(PosePredictor, AngularVelocity) {
+  // Some random rotation axis we will rotate around.
+  const vec3 kRotationAxis = vec3(1, 2, 3).normalized();
+
+  // Some random angle we will be rotating by.
+  const real kRotationAngle = M_PI / 30;
+
+  // Random start orientation we are currently at.
+  const quat kStartOrientation = quat(5, 3, 4, 1).normalized();
+
+  // The orientation we will end up at.
+  const quat kEndOrientation =
+      kStartOrientation *
+      quat(Eigen::AngleAxis<real>(kRotationAngle, kRotationAxis));
+
+  // The delta time for going from start orientation to end.
+  const real kDeltaTime = 1.0;
+
+  // Compute the angular velocity from start orientation to end.
+  const auto angularVelocity = Predictor::AngularVelocity(
+      kStartOrientation, kEndOrientation, kDeltaTime);
+
+  // Extract the axis and the angular speed.
+  const auto angularSpeed = angularVelocity.norm();
+  const auto rotationAxis = angularVelocity.normalized();
+
+  // The speed must match.
+  EXPECT_NEAR(angularSpeed, kRotationAngle / kDeltaTime, kAbsErrorTolerance);
+
+  // The rotation axis must match.
+  EXPECT_NEAR(rotationAxis[0], kRotationAxis[0], kAbsErrorTolerance);
+  EXPECT_NEAR(rotationAxis[1], kRotationAxis[1], kAbsErrorTolerance);
+  EXPECT_NEAR(rotationAxis[2], kRotationAxis[2], kAbsErrorTolerance);
+}
+
+}  // namespace
+
+}  // namespace posepredictor
diff --git a/libs/vr/libsensor/Android.mk b/libs/vr/libsensor/Android.mk
new file mode 100644
index 0000000..8c7ad43
--- /dev/null
+++ b/libs/vr/libsensor/Android.mk
@@ -0,0 +1,76 @@
+# 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 \
+	libdvrcommon \
+	libpdx_default_transport \
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	libhardware \
+	liblog \
+	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 := 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..0616d46
--- /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/vr/pose"
+#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
+
+enum {
+  DVR_POSE_POLL = 0,
+  DVR_POSE_FREEZE,
+  DVR_POSE_SET_MODE,
+  DVR_POSE_GET_RING_BUFFER,
+  DVR_POSE_NOTIFY_VSYNC,
+  DVR_POSE_GET_MODE,
+  DVR_POSE_GET_CONTROLLER_RING_BUFFER,
+  DVR_POSE_LOG_CONTROLLER,
+};
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_DVR_POSE_IPC_H_
diff --git a/libs/vr/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..b2ebd95
--- /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/vr/sensors"
+
+#define DVR_SENSOR_SERVICE_CLIENT (DVR_SENSOR_SERVICE_BASE "/client")
+
+/*
+ * Endpoint ops
+ */
+enum {
+  DVR_SENSOR_START = 0,
+  DVR_SENSOR_STOP,
+  DVR_SENSOR_POLL,
+};
+
+#endif  // ANDROID_DVR_SENSOR_IPC_H_
diff --git a/libs/vr/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..9eae3aa
--- /dev/null
+++ b/libs/vr/libsensor/pose_client.cpp
@@ -0,0 +1,338 @@
+#define LOG_TAG "PoseClient"
+#include <dvr/pose_client.h>
+
+#include <stdint.h>
+
+#include <log/log.h>
+#include <pdx/client.h>
+#include <pdx/default_transport/client_channel_factory.h>
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sensor_constants.h>
+
+using android::pdx::LocalHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Status;
+using android::pdx::Transaction;
+
+#define arraysize(x) (static_cast<int32_t>(std::extent<decltype(x)>::value))
+
+namespace android {
+namespace dvr {
+
+// PoseClient is a remote interface to the pose service in sensord.
+class PoseClient : public pdx::ClientBase<PoseClient> {
+ public:
+  ~PoseClient() override {}
+
+  // Casts C handle into an instance of this class.
+  static PoseClient* FromC(DvrPose* client) {
+    return reinterpret_cast<PoseClient*>(client);
+  }
+
+  // Polls the pose service for the current state and stores it in *state.
+  // Returns zero on success, a negative error code otherwise.
+  int Poll(DvrPoseState* state) {
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_POLL, nullptr, 0, state, sizeof(*state));
+    ALOGE_IF(!status, "Pose poll() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
+    if (!mapped_pose_buffer_) {
+      int ret = GetRingBuffer(nullptr);
+      if (ret < 0)
+        return ret;
+    }
+    *out_pose =
+        mapped_pose_buffer_->ring[vsync_count & kPoseAsyncBufferIndexMask];
+    return 0;
+  }
+
+  uint32_t GetVsyncCount() {
+    if (!mapped_pose_buffer_) {
+      int ret = GetRingBuffer(nullptr);
+      if (ret < 0)
+        return 0;
+    }
+    return mapped_pose_buffer_->vsync_count;
+  }
+
+  int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
+                        DvrPoseAsync* out_pose) {
+    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+      return -EINVAL;
+    }
+    if (!controllers_[controller_id].mapped_pose_buffer) {
+      int ret = GetControllerRingBuffer(controller_id);
+      if (ret < 0)
+        return ret;
+    }
+    *out_pose =
+        controllers_[controller_id]
+            .mapped_pose_buffer[vsync_count & kPoseAsyncBufferIndexMask];
+    return 0;
+  }
+
+  int LogController(bool enable) {
+    Transaction trans{*this};
+    Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
+                                         sizeof(enable), nullptr, 0);
+    ALOGE_IF(!status, "Pose LogController() failed because: %s",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Freezes the pose to the provided state. Future poll operations will return
+  // this state until a different state is frozen or SetMode() is called with a
+  // different mode.
+  // Returns zero on success, a negative error code otherwise.
+  int Freeze(const DvrPoseState& frozen_state) {
+    Transaction trans{*this};
+    Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
+                                         sizeof(frozen_state), nullptr, 0);
+    ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Sets the data mode for the pose service.
+  int SetMode(DvrPoseMode mode) {
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
+    ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  // Gets the data mode for the pose service.
+  int GetMode(DvrPoseMode* out_mode) {
+    int mode;
+    Transaction trans{*this};
+    Status<int> status =
+        trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
+    ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
+             status.GetErrorMessage().c_str());
+    if (status)
+      *out_mode = DvrPoseMode(mode);
+    return ReturnStatusOrError(status);
+  }
+
+  int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
+    if (pose_buffer_.get()) {
+      if (out_info) {
+        GetPoseRingBufferInfo(out_info);
+      }
+      return 0;
+    }
+
+    Transaction trans{*this};
+    Status<LocalChannelHandle> status =
+        trans.Send<LocalChannelHandle>(DVR_POSE_GET_RING_BUFFER);
+    if (!status) {
+      ALOGE("Pose GetRingBuffer() failed because: %s",
+            status.GetErrorMessage().c_str());
+      return -status.error();
+    }
+
+    auto buffer = BufferConsumer::Import(status.take());
+    if (!buffer) {
+      ALOGE("Pose failed to import ring buffer");
+      return -EIO;
+    }
+    void* addr = nullptr;
+    int ret = buffer->GetBlobReadOnlyPointer(sizeof(DvrPoseRingBuffer), &addr);
+    if (ret < 0 || !addr) {
+      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+      return -EIO;
+    }
+    pose_buffer_.swap(buffer);
+    mapped_pose_buffer_ = static_cast<const DvrPoseRingBuffer*>(addr);
+    ALOGI("Mapped pose data translation %f,%f,%f quat %f,%f,%f,%f",
+          mapped_pose_buffer_->ring[0].translation[0],
+          mapped_pose_buffer_->ring[0].translation[1],
+          mapped_pose_buffer_->ring[0].translation[2],
+          mapped_pose_buffer_->ring[0].orientation[0],
+          mapped_pose_buffer_->ring[0].orientation[1],
+          mapped_pose_buffer_->ring[0].orientation[2],
+          mapped_pose_buffer_->ring[0].orientation[3]);
+    if (out_info) {
+      GetPoseRingBufferInfo(out_info);
+    }
+    return 0;
+  }
+
+  int GetControllerRingBuffer(int32_t controller_id) {
+    if (controller_id < 0 || controller_id >= arraysize(controllers_)) {
+      return -EINVAL;
+    }
+    ControllerClientState& client_state = controllers_[controller_id];
+    if (client_state.pose_buffer.get()) {
+      return 0;
+    }
+
+    Transaction trans{*this};
+    Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
+        DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
+        sizeof(controller_id), nullptr, 0);
+    if (!status) {
+      return -status.error();
+    }
+
+    auto buffer = BufferConsumer::Import(status.take());
+    if (!buffer) {
+      ALOGE("Pose failed to import ring buffer");
+      return -EIO;
+    }
+    constexpr size_t size = kPoseAsyncBufferTotalCount * sizeof(DvrPoseAsync);
+    void* addr = nullptr;
+    int ret = buffer->GetBlobReadOnlyPointer(size, &addr);
+    if (ret < 0 || !addr) {
+      ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
+      return -EIO;
+    }
+    client_state.pose_buffer.swap(buffer);
+    client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
+    ALOGI(
+        "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
+        controller_id, client_state.mapped_pose_buffer[0].translation[0],
+        client_state.mapped_pose_buffer[0].translation[1],
+        client_state.mapped_pose_buffer[0].translation[2],
+        client_state.mapped_pose_buffer[0].orientation[0],
+        client_state.mapped_pose_buffer[0].orientation[1],
+        client_state.mapped_pose_buffer[0].orientation[2],
+        client_state.mapped_pose_buffer[0].orientation[3]);
+    return 0;
+  }
+
+  int NotifyVsync(uint32_t vsync_count, int64_t display_timestamp,
+                  int64_t display_period_ns,
+                  int64_t right_eye_photon_offset_ns) {
+    const struct iovec data[] = {
+        {.iov_base = &vsync_count, .iov_len = sizeof(vsync_count)},
+        {.iov_base = &display_timestamp, .iov_len = sizeof(display_timestamp)},
+        {.iov_base = &display_period_ns, .iov_len = sizeof(display_period_ns)},
+        {.iov_base = &right_eye_photon_offset_ns,
+         .iov_len = sizeof(right_eye_photon_offset_ns)},
+    };
+    Transaction trans{*this};
+    Status<int> status =
+        trans.SendVector<int>(DVR_POSE_NOTIFY_VSYNC, data, nullptr);
+    ALOGE_IF(!status, "Pose NotifyVsync() failed because: %s\n",
+             status.GetErrorMessage().c_str());
+    return ReturnStatusOrError(status);
+  }
+
+  int GetRingBufferFd(LocalHandle* fd) {
+    int ret = GetRingBuffer(nullptr);
+    if (ret < 0)
+      return ret;
+    *fd = pose_buffer_->GetBlobFd();
+    return 0;
+  }
+
+ private:
+  friend BASE;
+
+  // Set up a channel to the pose service.
+  PoseClient()
+      : BASE(pdx::default_transport::ClientChannelFactory::Create(
+            DVR_POSE_SERVICE_CLIENT)) {
+    // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
+    // block while waiting for the pose service to come back up.
+    EnableAutoReconnect(kInfiniteTimeout);
+  }
+
+  PoseClient(const PoseClient&) = delete;
+  PoseClient& operator=(const PoseClient&) = delete;
+
+  void GetPoseRingBufferInfo(DvrPoseRingBufferInfo* out_info) const {
+    out_info->min_future_count = kPoseAsyncBufferMinFutureCount;
+    out_info->total_count = kPoseAsyncBufferTotalCount;
+    out_info->buffer = mapped_pose_buffer_->ring;
+  }
+
+  std::unique_ptr<BufferConsumer> pose_buffer_;
+  const DvrPoseRingBuffer* mapped_pose_buffer_ = nullptr;
+
+  struct ControllerClientState {
+    std::unique_ptr<BufferConsumer> pose_buffer;
+    const DvrPoseAsync* mapped_pose_buffer = nullptr;
+  };
+  ControllerClientState controllers_[2];
+};
+
+}  // namespace dvr
+}  // namespace android
+
+using android::dvr::PoseClient;
+
+struct DvrPose {};
+
+extern "C" {
+
+DvrPose* dvrPoseCreate() {
+  PoseClient* client = PoseClient::Create().release();
+  return reinterpret_cast<DvrPose*>(client);
+}
+
+void dvrPoseDestroy(DvrPose* client) { delete PoseClient::FromC(client); }
+
+int dvrPoseGet(DvrPose* client, uint32_t vsync_count, DvrPoseAsync* out_pose) {
+  return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
+}
+
+uint32_t dvrPoseGetVsyncCount(DvrPose* client) {
+  return PoseClient::FromC(client)->GetVsyncCount();
+}
+
+int dvrPoseGetController(DvrPose* client, int32_t controller_id,
+                         uint32_t vsync_count, DvrPoseAsync* out_pose) {
+  return PoseClient::FromC(client)->GetControllerPose(controller_id,
+                                                      vsync_count, out_pose);
+}
+
+int dvrPoseLogController(DvrPose* client, bool enable) {
+  return PoseClient::FromC(client)->LogController(enable);
+}
+
+int dvrPosePoll(DvrPose* client, DvrPoseState* state) {
+  return PoseClient::FromC(client)->Poll(state);
+}
+
+int dvrPoseFreeze(DvrPose* client, const DvrPoseState* frozen_state) {
+  return PoseClient::FromC(client)->Freeze(*frozen_state);
+}
+
+int dvrPoseSetMode(DvrPose* client, DvrPoseMode mode) {
+  return PoseClient::FromC(client)->SetMode(mode);
+}
+
+int dvrPoseGetMode(DvrPose* client, DvrPoseMode* mode) {
+  return PoseClient::FromC(client)->GetMode(mode);
+}
+
+int dvrPoseGetRingBuffer(DvrPose* client, DvrPoseRingBufferInfo* out_info) {
+  return PoseClient::FromC(client)->GetRingBuffer(out_info);
+}
+
+int privateDvrPoseNotifyVsync(DvrPose* client, uint32_t vsync_count,
+                              int64_t display_timestamp,
+                              int64_t display_period_ns,
+                              int64_t right_eye_photon_offset_ns) {
+  return PoseClient::FromC(client)->NotifyVsync(vsync_count, display_timestamp,
+                                                display_period_ns,
+                                                right_eye_photon_offset_ns);
+}
+
+int privateDvrPoseGetRingBufferFd(DvrPose* client, LocalHandle* fd) {
+  return PoseClient::FromC(client)->GetRingBufferFd(fd);
+}
+
+}  // extern "C"
diff --git a/libs/vr/libsensor/sensor_client.cpp b/libs/vr/libsensor/sensor_client.cpp
new file mode 100644
index 0000000..04e88cc
--- /dev/null
+++ b/libs/vr/libsensor/sensor_client.cpp
@@ -0,0 +1,79 @@
+#define LOG_TAG "SensorClient"
+#include <private/dvr/sensor_client.h>
+
+#include <log/log.h>
+#include <poll.h>
+
+#include <pdx/default_transport/client_channel_factory.h>
+#include <private/dvr/sensor-ipc.h>
+
+using android::pdx::Transaction;
+
+namespace android {
+namespace dvr {
+
+SensorClient::SensorClient(int sensor_type)
+    : BASE(pdx::default_transport::ClientChannelFactory::Create(
+          DVR_SENSOR_SERVICE_CLIENT)),
+      sensor_type_(sensor_type) {}
+
+SensorClient::~SensorClient() {}
+
+int SensorClient::StartSensor() {
+  Transaction trans{*this};
+  auto status = trans.Send<int>(DVR_SENSOR_START, &sensor_type_,
+                                sizeof(sensor_type_), nullptr, 0);
+  ALOGE_IF(!status, "startSensor() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return ReturnStatusOrError(status);
+}
+
+int SensorClient::StopSensor() {
+  Transaction trans{*this};
+  auto status = trans.Send<int>(DVR_SENSOR_STOP);
+  ALOGE_IF(!status, "stopSensor() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return ReturnStatusOrError(status);
+}
+
+int SensorClient::Poll(sensors_event_t* events, int max_events) {
+  int num_events = 0;
+  struct iovec rvec[] = {
+      {.iov_base = &num_events, .iov_len = sizeof(int)},
+      {.iov_base = events, .iov_len = max_events * sizeof(sensors_event_t)},
+  };
+  Transaction trans{*this};
+  auto status = trans.SendVector<int>(DVR_SENSOR_POLL, nullptr, rvec);
+  ALOGE_IF(!status, "Sensor poll() failed because: %s\n",
+           status.GetErrorMessage().c_str());
+  return !status ? -status.error() : num_events;
+}
+
+}  // namespace dvr
+}  // namespace android
+
+// Entrypoints to simplify using the library when programmatically dynamicly
+// loading it.
+// Allows us to call this library without linking it, as, for instance,
+// when compiling GVR in Google3.
+// NOTE(segal): It's kind of a hack.
+
+extern "C" uint64_t dvrStartSensor(int type) {
+  android::dvr::SensorClient* service =
+      android::dvr::SensorClient::Create(type).release();
+  service->StartSensor();
+  return (uint64_t)service;
+}
+
+extern "C" void dvrStopSensor(uint64_t service) {
+  android::dvr::SensorClient* iss =
+      reinterpret_cast<android::dvr::SensorClient*>(service);
+  iss->StopSensor();
+  delete iss;
+}
+
+extern "C" int dvrPollSensor(uint64_t service, int max_count,
+                             sensors_event_t* events) {
+  return reinterpret_cast<android::dvr::SensorClient*>(service)->Poll(
+      events, max_count);
+}
diff --git a/libs/vr/libsensor/tests/sensor_app_tests.cpp b/libs/vr/libsensor/tests/sensor_app_tests.cpp
new file mode 100644
index 0000000..64c9864
--- /dev/null
+++ b/libs/vr/libsensor/tests/sensor_app_tests.cpp
@@ -0,0 +1,132 @@
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include <dvr/graphics.h>
+#include <dvr/pose_client.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <private/dvr/types.h>
+
+using android::dvr::vec4;
+
+namespace {
+
+vec4 ToVec4(float32x4_t rhs) { return vec4(rhs[0], rhs[1], rhs[2], rhs[3]); }
+
+}
+
+DvrGraphicsContext* CreateContext() {
+  DvrGraphicsContext* context = nullptr;
+  int display_width = 0, display_height = 0;
+  int surface_width = 0, surface_height = 0;
+  float inter_lens_meters = 0.0f;
+  float left_fov[4] = {0.0f};
+  float right_fov[4] = {0.0f};
+  int disable_warp = 0;
+  DvrSurfaceParameter surface_params[] = {
+      DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, disable_warp),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_WIDTH, &display_width),
+      DVR_SURFACE_PARAMETER_OUT(DISPLAY_HEIGHT, &display_height),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+      DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+      DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &inter_lens_meters),
+      DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, left_fov),
+      DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, right_fov),
+      DVR_SURFACE_PARAMETER_LIST_END,
+  };
+  dvrGraphicsContextCreate(surface_params, &context);
+  return context;
+}
+
+TEST(SensorAppTests, GetPose) {
+  DvrGraphicsContext* context = CreateContext();
+  ASSERT_NE(nullptr, context);
+  DvrPose* client = dvrPoseCreate();
+  ASSERT_NE(nullptr, client);
+
+  DvrPoseAsync last_pose;
+  uint32_t last_vsync_count = 0;
+  for (int i = 0; i < 10; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+    DvrPoseAsync pose;
+    int ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+    ASSERT_EQ(0, ret);
+
+    // Check for unit-length quaternion to verify valid pose.
+    vec4 quaternion = ToVec4(pose.orientation);
+    float length = quaternion.norm();
+    EXPECT_GT(0.001, fabs(1.0f - length));
+
+    // Check for different data each frame, but skip first few to allow
+    // startup anomalies.
+    if (i > 0) {
+      if (last_vsync_count == schedule.vsync_count)
+        ALOGE("vsync did not increment: %u", schedule.vsync_count);
+      if (pose.timestamp_ns == last_pose.timestamp_ns)
+        ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
+      // TODO(jbates) figure out why the bots are not passing this check.
+      // EXPECT_NE(last_vsync_count, schedule.vsync_count);
+      // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
+    }
+    last_pose = pose;
+    last_vsync_count = schedule.vsync_count;
+    dvrBeginRenderFrame(context);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    dvrPresent(context);
+  }
+
+  dvrPoseDestroy(client);
+  dvrGraphicsContextDestroy(context);
+}
+
+TEST(SensorAppTests, PoseRingBuffer) {
+  DvrGraphicsContext* context = CreateContext();
+  ASSERT_NE(nullptr, context);
+  DvrPose* client = dvrPoseCreate();
+  ASSERT_NE(nullptr, client);
+
+  DvrPoseRingBufferInfo info;
+  int ret = dvrPoseGetRingBuffer(client, &info);
+  ASSERT_EQ(0, ret);
+  ASSERT_NE(nullptr, info.buffer);
+  EXPECT_LE(2u, info.min_future_count);
+  EXPECT_LE(8u, info.total_count);
+
+  DvrPoseAsync last_pose;
+  uint32_t last_vsync_count = 0;
+  for (int i = 0; i < 10; ++i) {
+    DvrFrameSchedule schedule;
+    dvrGraphicsWaitNextFrame(context, 0, &schedule);
+    DvrPoseAsync pose;
+    ret = dvrPoseGet(client, schedule.vsync_count, &pose);
+    ASSERT_EQ(0, ret);
+
+    // Check for unit-length quaternion to verify valid pose.
+    vec4 quaternion = ToVec4(pose.orientation);
+    float length = quaternion.norm();
+    EXPECT_GT(0.001, fabs(1.0f - length));
+
+    // Check for different data each frame, but skip first few to allow
+    // startup anomalies.
+    if (i > 0) {
+      if (last_vsync_count == schedule.vsync_count)
+        ALOGE("vsync did not increment: %u", schedule.vsync_count);
+      if (pose.timestamp_ns == last_pose.timestamp_ns)
+        ALOGE("timestamp did not change: %" PRIu64, pose.timestamp_ns);
+      // TODO(jbates) figure out why the bots are not passing this check.
+      // EXPECT_NE(last_vsync_count, schedule.vsync_count);
+      // EXPECT_NE(pose.timestamp_ns, last_pose.timestamp_ns);
+    }
+    last_pose = pose;
+    last_vsync_count = schedule.vsync_count;
+    dvrBeginRenderFrame(context);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    dvrPresent(context);
+  }
+
+  dvrPoseDestroy(client);
+  dvrGraphicsContextDestroy(context);
+}
diff --git a/libs/vr/libvr_manager/Android.mk b/libs/vr/libvr_manager/Android.mk
new file mode 100644
index 0000000..e9987f7
--- /dev/null
+++ b/libs/vr/libvr_manager/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+exported_include_dirs := \
+  $(LOCAL_PATH)/include
+
+include_dirs := \
+  frameworks/native/include/vr/vr_manager \
+  $(exported_include_dirs)
+
+src_files := \
+  vr_manager.cpp \
+  trusted_uids.cpp
+
+static_libs := \
+  libutils \
+  libbinder \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src_files)
+LOCAL_C_INCLUDES := $(include_dirs)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(exported_include_dirs)
+LOCAL_CFLAGS += -Wall
+LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Wunused
+LOCAL_CFLAGS += -Wunreachable-code
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_MODULE := libvr_manager
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h b/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h
new file mode 100644
index 0000000..4496fbf
--- /dev/null
+++ b/libs/vr/libvr_manager/include/private/dvr/trusted_uids.h
@@ -0,0 +1,33 @@
+#ifndef ANDROID_DVR_TRUSTED_UIDS_H_
+#define ANDROID_DVR_TRUSTED_UIDS_H_
+
+#include <sys/types.h>
+
+namespace android {
+namespace dvr {
+
+/**
+ * Tells if a provided UID can be trusted to access restricted VR APIs.
+ *
+ * UID trust is based on the android.permission.RESTRICTED_VR_ACCESS permission.
+ * AID_SYSTEM and AID_ROOT are automatically trusted by Android.
+ *
+ * UIDs are guaranteed not to be reused until the next reboot even in case
+ * of package reinstall. For performance reasons this method caches results by
+ * default, as otherwise every check would trigger a Java call.
+ *
+ * This function is thread-safe.
+ *
+ * @param uid The uid to check.
+ * @param use_cache If true any cached result for the provided uid will be
+ *     reused. If false this call will reach the Application Manager Service
+ *     in Java to get updated values. Any updates will be stored in the cache.
+ * @return true if the uid is trusted, false if not or if the VR Manager Service
+ *         could not be reached to verify the uid.
+ */
+bool IsTrustedUid(uid_t uid, bool use_cache = true);
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_TRUSTED_UIDS_H_
diff --git a/libs/vr/libvr_manager/trusted_uids.cpp b/libs/vr/libvr_manager/trusted_uids.cpp
new file mode 100644
index 0000000..4228a05
--- /dev/null
+++ b/libs/vr/libvr_manager/trusted_uids.cpp
@@ -0,0 +1,51 @@
+#include "private/dvr/trusted_uids.h"
+
+#include <mutex>
+#include <unordered_map>
+
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String16.h>
+#include <vr/vr_manager/vr_manager.h>
+
+namespace android {
+namespace dvr {
+
+bool IsTrustedUid(uid_t uid, bool use_cache) {
+  static std::unordered_map<uid_t, bool> uid_cache;
+  static std::mutex uid_cache_mutex;
+
+  // Whitelist requests from the system UID.
+  // These are already whitelisted by the permission service, but it might not
+  // be available if the ActivityManagerService is up during boot.
+  // This ensures the correct result for system services while booting up.
+  if (uid == AID_SYSTEM)
+    return true;
+
+  std::lock_guard<std::mutex> lock(uid_cache_mutex);
+
+  if (use_cache) {
+    auto it = uid_cache.find(uid);
+    if (it != uid_cache.end())
+      return it->second;
+  }
+
+  sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+  if (binder == 0) {
+    ALOGW("Could not access permission service");
+    return false;
+  }
+
+  // Note: we ignore the pid because it's only used to automatically reply
+  // true if the caller is the Activity Manager Service.
+  bool trusted = interface_cast<IPermissionController>(binder)->checkPermission(
+      String16("android.permission.RESTRICTED_VR_ACCESS"), -1, uid);
+
+  // Cache the information for this uid to avoid future Java calls.
+  uid_cache[uid] = trusted;
+  return trusted;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvr_manager/vr_manager.cpp b/libs/vr/libvr_manager/vr_manager.cpp
new file mode 100644
index 0000000..d24cbb5
--- /dev/null
+++ b/libs/vr/libvr_manager/vr_manager.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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");
+
+}  // namespace android
diff --git a/libs/vr/libvrflinger/Android.mk b/libs/vr/libvrflinger/Android.mk
new file mode 100644
index 0000000..3450788
--- /dev/null
+++ b/libs/vr/libvrflinger/Android.mk
@@ -0,0 +1,84 @@
+# 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 := \
+	acquired_buffer.cpp \
+	compositor.cpp \
+	debug_hud_data.cpp \
+	debug_hud_view.cpp \
+	display_manager_service.cpp \
+	display_service.cpp \
+	display_surface.cpp \
+	epoll_event_dispatcher.cpp \
+	hardware_composer.cpp \
+	screenshot_service.cpp \
+	surface_channel.cpp \
+	video_compositor.cpp \
+	video_mesh_surface.cpp \
+	vr_flinger.cpp \
+	vsync_service.cpp
+
+includeFiles := $(LOCAL_PATH)/include
+
+staticLibraries := \
+	libsurfaceflingerincludes \
+	libhwcomposer-command-buffer \
+	libbufferhub \
+	libbufferhubqueue \
+	libeds \
+	libdisplay \
+	libdvrcommon \
+	libdvrgraphics \
+	libperformance \
+	libsensor \
+	libpdx_default_transport \
+	libvr_manager \
+
+sharedLibraries := \
+	android.dvr.composer@1.0 \
+	android.hardware.graphics.allocator@2.0 \
+	android.hardware.graphics.composer@2.1 \
+	libbinder \
+	libbase \
+	libcutils \
+	liblog \
+	libhardware \
+	libnativewindow \
+	libutils \
+	libEGL \
+	libGLESv1_CM \
+	libGLESv2 \
+	libvulkan \
+	libui \
+	libgui \
+	libsync \
+	libhidlbase \
+	libhidltransport \
+	libfmq \
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+
+LOCAL_CFLAGS += -DLOG_TAG=\"vr_flinger\"
+LOCAL_CFLAGS += -DTRACE=0
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_GRAPHICS
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_WHOLE_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_MODULE := libvrflinger
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
new file mode 100644
index 0000000..5a3aa7f
--- /dev/null
+++ b/libs/vr/libvrflinger/acquired_buffer.cpp
@@ -0,0 +1,100 @@
+#include "acquired_buffer.h"
+
+#include <log/log.h>
+#include <sync/sync.h>
+
+using android::pdx::LocalHandle;
+
+namespace android {
+namespace dvr {
+
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+                               LocalHandle acquire_fence, uint64_t /*sequence*/)
+    : buffer_(buffer), acquire_fence_(std::move(acquire_fence)) {}
+
+AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+                               int* error) {
+  LocalHandle fence;
+  const int ret = buffer->Acquire(&fence);
+
+  if (error)
+    *error = ret;
+
+  if (ret < 0) {
+    ALOGW("AcquiredBuffer::AcquiredBuffer: Failed to acquire buffer: %s",
+          strerror(-ret));
+    buffer_ = nullptr;
+    // Default construct sets acquire_fence_ to empty.
+  } else {
+    buffer_ = buffer;
+    acquire_fence_ = std::move(fence);
+  }
+}
+
+AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other)
+    : buffer_(std::move(other.buffer_)),
+      acquire_fence_(std::move(other.acquire_fence_)) {}
+
+AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); }
+
+AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) {
+  if (this != &other) {
+    Release(LocalHandle(kEmptyFence));
+
+    buffer_ = std::move(other.buffer_);
+    acquire_fence_ = std::move(other.acquire_fence_);
+  }
+  return *this;
+}
+
+bool AcquiredBuffer::IsAvailable() const {
+  if (IsEmpty())
+    return false;
+
+  // Only check the fence if the acquire fence is not empty.
+  if (acquire_fence_) {
+    const int ret = sync_wait(acquire_fence_.Get(), 0);
+    ALOGD_IF(TRACE || (ret < 0 && errno != ETIME),
+             "AcquiredBuffer::IsAvailable: acquire_fence_=%d sync_wait()=%d "
+             "errno=%d.",
+             acquire_fence_.Get(), ret, ret < 0 ? errno : 0);
+    if (ret == 0) {
+      // The fence is completed, so to avoid further calls to sync_wait we close
+      // it here.
+      acquire_fence_.Close();
+    }
+    return ret == 0;
+  } else {
+    return true;
+  }
+}
+
+LocalHandle AcquiredBuffer::ClaimAcquireFence() {
+  return std::move(acquire_fence_);
+}
+
+std::shared_ptr<BufferConsumer> AcquiredBuffer::ClaimBuffer() {
+  return std::move(buffer_);
+}
+
+int AcquiredBuffer::Release(LocalHandle release_fence) {
+  if (buffer_) {
+    // Close the release fence since we can't transfer it with an async release.
+    release_fence.Close();
+    const int ret = buffer_->ReleaseAsync();
+    if (ret < 0) {
+      ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s",
+            buffer_->id(), strerror(-ret));
+      if (ret != -ESHUTDOWN)
+        return ret;
+    }
+
+    buffer_ = nullptr;
+    acquire_fence_.Close();
+  }
+
+  return 0;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
new file mode 100644
index 0000000..050cd5f
--- /dev/null
+++ b/libs/vr/libvrflinger/acquired_buffer.h
@@ -0,0 +1,82 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
+
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+
+#include <memory>
+
+namespace android {
+namespace dvr {
+
+// Manages the ACQUIRE/RELEASE ownership cycle of a BufferConsumer.
+class AcquiredBuffer {
+ public:
+  static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle;
+
+  AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {}
+
+  // Constructs an AcquiredBuffer from a BufferConsumer pointer and an acquire
+  // fence. The BufferConsumer MUST be in the ACQUIRED state prior to calling
+  // this constructor; the constructor does not attempt to ACQUIRE the buffer
+  // itself.
+  AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer,
+                 pdx::LocalHandle acquire_fence, uint64_t sequence);
+
+  // Constructs an AcquiredBuffer from a BufferConsumer. The BufferConsumer MUST
+  // be in the POSTED state prior to calling this constructor, as this
+  // constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails
+  // this instance is left in the empty state. An optional error code is
+  // returned in |error|, which may be nullptr if not needed.
+  AcquiredBuffer(const std::shared_ptr<BufferConsumer>& buffer, int* error);
+
+  // Move constructor. Behaves similarly to the move assignment operator below.
+  AcquiredBuffer(AcquiredBuffer&& other);
+
+  ~AcquiredBuffer();
+
+  // Move assignment operator. Moves the BufferConsumer and acquire fence from
+  // |other| into this instance after RELEASING the current BufferConsumer and
+  // closing the acquire fence. After the move |other| is left in the empty
+  // state.
+  AcquiredBuffer& operator=(AcquiredBuffer&& other);
+
+  // Accessors for the underlying BufferConsumer, the acquire fence, and the
+  // use-case specific sequence value from the acquisition (see
+  // dreamos/buffer_hub_client.h).
+  std::shared_ptr<BufferConsumer> buffer() const { return buffer_; }
+  int acquire_fence() const { return acquire_fence_.Get(); }
+
+  // When non-empty, returns true if the acquired fence was signaled (or if the
+  // fence is empty). Returns false when empty or if the fence is not signaled.
+  bool IsAvailable() const;
+
+  bool IsEmpty() const { return buffer_ == nullptr; }
+
+  // Returns the acquire fence, passing ownership to the caller.
+  pdx::LocalHandle ClaimAcquireFence();
+
+  // Returns the buffer, passing ownership to the caller. Caller is responsible
+  // for calling Release on the returned buffer.
+  std::shared_ptr<BufferConsumer> ClaimBuffer();
+
+  // Releases the BufferConsumer, passing the release fence in |release_fence|
+  // to the producer. On success, the BufferConsumer and acquire fence are set
+  // to empty state; if release fails, the BufferConsumer and acquire fence are
+  // left in place and a negative error code is returned.
+  int Release(pdx::LocalHandle release_fence);
+
+ private:
+  AcquiredBuffer(const AcquiredBuffer&) = delete;
+  void operator=(const AcquiredBuffer&) = delete;
+
+  std::shared_ptr<BufferConsumer> buffer_;
+  // Mutable so that the fence can be closed when it is determined to be
+  // signaled during IsAvailable().
+  mutable pdx::LocalHandle acquire_fence_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
diff --git a/libs/vr/libvrflinger/compositor.cpp b/libs/vr/libvrflinger/compositor.cpp
new file mode 100644
index 0000000..5a111d4
--- /dev/null
+++ b/libs/vr/libvrflinger/compositor.cpp
@@ -0,0 +1,873 @@
+#include "compositor.h"
+
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+
+#include <memory>
+
+#include <cutils/properties.h>
+
+#include <dvr/graphics.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/debug.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/dummy_native_window.h>
+#include <private/dvr/gl_fenced_flush.h>
+#include <private/dvr/graphics/blur.h>
+#include <private/dvr/graphics/gpu_profiler.h>
+#include <private/dvr/lucid_metrics.h>
+#include <private/dvr/native_buffer.h>
+#include <private/dvr/platform_defines.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "debug_hud_data.h"
+#include "debug_hud_view.h"
+#include "display_surface.h"
+
+#define BINNING_CONTROL_HINT_QCOM 0x8FB0
+
+// Accepted by the <hint> parameter of glHint:
+#define BINNING_QCOM 0x8FB1
+#define VISIBILITY_OPTIMIZED_BINNING_QCOM 0x8FB2
+#define RENDER_DIRECT_TO_FRAMEBUFFER_QCOM 0x8FB3
+
+#ifndef EGL_CONTEXT_MAJOR_VERSION
+#define EGL_CONTEXT_MAJOR_VERSION 0x3098
+#define EGL_CONTEXT_MINOR_VERSION 0x30FB
+#endif
+
+using android::pdx::LocalHandle;
+
+static const int kDistortionMeshResolution = 40;
+
+static std::shared_ptr<int64_t> eds_gpu_duration_ns =
+    std::make_shared<int64_t>(0);
+
+static constexpr char kDisableLensDistortionProp[] =
+    "persist.dreamos.disable_distort";
+
+static constexpr char kEnableEdsPoseSaveProp[] =
+    "persist.dreamos.save_eds_pose";
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// An implementation of ANativeWindowBuffer backed by a temporary IonBuffer.
+// Do not hold on to this kind of object, because the IonBuffer may become
+// invalid in other scopes.
+class TemporaryNativeBuffer
+    : public ANativeObjectBase<ANativeWindowBuffer, TemporaryNativeBuffer,
+                               LightRefBase<TemporaryNativeBuffer>> {
+ public:
+  explicit TemporaryNativeBuffer(const IonBuffer* buffer) : BASE() {
+    ANativeWindowBuffer::width = buffer->width();
+    ANativeWindowBuffer::height = buffer->height();
+    ANativeWindowBuffer::stride = buffer->stride();
+    ANativeWindowBuffer::format = buffer->format();
+    ANativeWindowBuffer::usage = buffer->usage();
+    // TODO(eieio): Update NYC to support layer_count.
+    // ANativeWindowBuffer::layer_count = 1;
+    handle = buffer->handle();
+  }
+
+ private:
+  friend class android::LightRefBase<TemporaryNativeBuffer>;
+
+  TemporaryNativeBuffer(const TemporaryNativeBuffer&) = delete;
+  void operator=(TemporaryNativeBuffer&) = delete;
+};
+
+std::vector<uint8_t> ReadTextureRGBA(GLuint texture_id, int width, int height) {
+  std::vector<uint8_t> data(width * height * 4);
+  GLuint fbo;
+  glGenFramebuffers(1, &fbo);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         texture_id, 0);
+  // Using default GL_PACK_ALIGNMENT of 4 for the 4 byte source data.
+  glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  glDeleteFramebuffers(1, &fbo);
+  CHECK_GL();
+  return data;
+}
+
+}  // namespace
+
+class Compositor::Texture {
+ public:
+  Texture(std::shared_ptr<BufferConsumer> consumer, EGLDisplay display,
+          int index);
+  ~Texture();
+
+  std::shared_ptr<BufferConsumer> consumer() const { return consumer_; }
+  GLuint texture_id() const { return texture_id_; }
+  vec2i size() const {
+    return vec2i(native_buffer_.get()->width, native_buffer_.get()->height);
+  }
+  int index() const { return index_; }
+
+  bool Initialize();
+
+ private:
+  Texture(const Texture&) = delete;
+  void operator=(const Texture&) = delete;
+
+  std::shared_ptr<BufferConsumer> consumer_;
+
+  android::sp<NativeBufferConsumer> native_buffer_;
+
+  EGLDisplay display_;
+  EGLImageKHR image_;
+  GLuint texture_id_;
+  int index_;
+};
+
+Compositor::Texture::Texture(std::shared_ptr<BufferConsumer> consumer,
+                             EGLDisplay display, int index)
+    : consumer_(consumer),
+      display_(display),
+      image_(nullptr),
+      texture_id_(0),
+      index_(index) {}
+
+Compositor::Texture::~Texture() {
+  glDeleteTextures(1, &texture_id_);
+  eglDestroyImageKHR(display_, image_);
+}
+
+bool Compositor::Texture::Initialize() {
+  native_buffer_ = new NativeBufferConsumer(consumer_, index_);
+
+  CHECK_GL();
+  image_ = eglCreateImageKHR(
+      display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer_.get()), nullptr);
+  if (!image_) {
+    ALOGE("Failed to create EGLImage\n");
+    return false;
+  }
+
+  glGenTextures(1, &texture_id_);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, texture_id_);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image_);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  CHECK_GL();
+  return true;
+}
+
+Compositor::RenderTarget::RenderTarget()
+    : buffer_texture_id_(0),
+      buffer_framebuffer_id_(0),
+      buffer_image_(nullptr) {}
+
+Compositor::RenderTarget::~RenderTarget() { Destroy(); }
+
+void Compositor::RenderTarget::Destroy() {
+  glDeleteFramebuffers(1, &buffer_framebuffer_id_);
+  glDeleteTextures(1, &buffer_texture_id_);
+  eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), buffer_image_);
+  buffer_texture_id_ = 0;
+  buffer_framebuffer_id_ = 0;
+  buffer_image_ = nullptr;
+}
+
+void Compositor::RenderTarget::Initialize(int width, int height) {
+  LOG_ALWAYS_FATAL_IF(buffer_texture_id_ || buffer_framebuffer_id_ ||
+                      buffer_image_);
+  constexpr int usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_COMPOSER |
+                        GRALLOC_USAGE_HW_RENDER |
+                        GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION;
+  buffer_ = std::make_shared<IonBuffer>(width, height,
+                                        HAL_PIXEL_FORMAT_RGBA_8888, usage);
+
+  native_buffer_ = new NativeBuffer(buffer_);
+
+  buffer_image_ = eglCreateImageKHR(
+      eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT,
+      EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer_.get()), nullptr);
+
+  glGenTextures(1, &buffer_texture_id_);
+  glBindTexture(GL_TEXTURE_2D, buffer_texture_id_);
+  CHECK_GL();
+
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, buffer_image_);
+  CHECK_GL();
+
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glBindTexture(GL_TEXTURE_2D, 0);
+
+  // Generate a framebuffer.
+  glGenFramebuffers(1, &buffer_framebuffer_id_);
+  glBindFramebuffer(GL_FRAMEBUFFER, buffer_framebuffer_id_);
+  CHECK_GL();
+
+  // Attach the color buffer
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         buffer_texture_id_, 0);
+  CHECK_GL();
+  GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+  CHECK_GL();
+  if (result != GL_FRAMEBUFFER_COMPLETE) {
+    ALOGE("Framebuffer incomplete: %d", result);
+  }
+
+  // Clear the render target to black once. In direct render mode we never draw
+  // the corner pixels.
+  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+  glClear(GL_COLOR_BUFFER_BIT);
+  glFlush();
+
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+  CHECK_GL();
+}
+
+void Compositor::RenderTarget::BindFramebuffer() {
+  glBindFramebuffer(GL_FRAMEBUFFER, buffer_framebuffer_id_);
+}
+
+void Compositor::RenderTarget::DiscardColorAttachment() {
+  GLenum attachment = GL_COLOR_ATTACHMENT0;
+  glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachment);
+  CHECK_GL();
+}
+
+class Compositor::RenderPoseBufferObject {
+ public:
+  RenderPoseBufferObject(LocalHandle&& render_pose_buffer_fd) {
+    // Create new pose tracking buffer for this surface.
+    glGenBuffers(1, &render_pose_buffer_object_);
+    glBindBuffer(GL_UNIFORM_BUFFER, render_pose_buffer_object_);
+    if (render_pose_buffer_fd) {
+      LOG_ALWAYS_FATAL_IF(!glBindSharedBufferQCOM);
+      if (glBindSharedBufferQCOM)
+        glBindSharedBufferQCOM(GL_UNIFORM_BUFFER,
+                               sizeof(DisplaySurfaceMetadata),
+                               render_pose_buffer_fd.Get());
+      else
+        ALOGE("Error: Missing gralloc buffer extension");
+      CHECK_GL();
+    }
+    glBindBuffer(GL_UNIFORM_BUFFER, 0);
+  }
+
+  ~RenderPoseBufferObject() { glDeleteBuffers(1, &render_pose_buffer_object_); }
+
+  GLuint object_id() const { return render_pose_buffer_object_; }
+
+ private:
+  // Render pose buffer object. This contains an array of poses that corresponds
+  // with the surface buffers.
+  GLuint render_pose_buffer_object_;
+
+  RenderPoseBufferObject(const RenderPoseBufferObject&) = delete;
+  void operator=(const RenderPoseBufferObject&) = delete;
+};
+
+HeadMountMetrics CreateDefaultHeadMountMetrics() {
+  const bool enable_distortion =
+      property_get_bool(kDisableLensDistortionProp, 0) == 0;
+  return enable_distortion ? CreateHeadMountMetrics()
+                           : CreateUndistortedHeadMountMetrics();
+}
+
+Compositor::Compositor()
+    : head_mount_metrics_(CreateDefaultHeadMountMetrics()),
+      display_(0),
+      config_(0),
+      surface_(0),
+      context_(0),
+      active_render_target_(0),
+      is_render_direct_(false),
+      compute_fbo_(0),
+      compute_fbo_texture_(0),
+      hmd_metrics_requires_update_(false),
+      eds_pose_capture_enabled_(false) {}
+
+Compositor::~Compositor() {}
+
+bool Compositor::Initialize(const DisplayMetrics& display_metrics) {
+  ATRACE_NAME("Compositor::Initialize");
+  if (!InitializeEGL())
+    return false;
+
+  display_metrics_ = display_metrics;
+  const int width = display_metrics_.GetSizePixels().x();
+  const int height = display_metrics_.GetSizePixels().y();
+
+  render_target_[0].Initialize(width, height);
+  render_target_[1].Initialize(width, height);
+
+  // EDS:
+  GpuProfiler::Get()->SetEnableGpuTracing(true);
+
+  eds_pose_capture_enabled_ = property_get_bool(kEnableEdsPoseSaveProp, 0) == 1;
+
+  CheckAndUpdateHeadMountMetrics(true);
+
+  debug_hud_.reset(new DebugHudView(*composite_hmd_.get()));
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  return true;
+}
+
+void Compositor::UpdateHeadMountMetrics(
+    const HeadMountMetrics& head_mount_metrics) {
+  // Recalculating the mesh must be done in the draw loop, defer until then.
+  std::lock_guard<std::mutex> _lock(mutex_);
+  head_mount_metrics_ = head_mount_metrics;
+  hmd_metrics_requires_update_ = true;
+}
+
+void Compositor::CheckAndUpdateHeadMountMetrics(bool force_update) {
+  std::lock_guard<std::mutex> _lock(mutex_);
+  if (hmd_metrics_requires_update_ || force_update) {
+    hmd_metrics_requires_update_ = false;
+    composite_hmd_.reset(
+        new CompositeHmd(head_mount_metrics_, display_metrics_));
+    CHECK_GL();
+    eds_renderer_.reset(new DistortionRenderer(
+        *composite_hmd_.get(), display_metrics_.GetSizePixels(),
+        kDistortionMeshResolution, true, false, false, true, true));
+  }
+}
+
+bool Compositor::InitializeEGL() {
+  ATRACE_NAME("Compositor::InitializeEGL");
+  display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  if (!display_) {
+    ALOGE("Failed to get egl display\n");
+    return false;
+  }
+
+  eglInitialize(display_, nullptr, nullptr);
+
+  EGLint attribs[] = {
+      EGL_BUFFER_SIZE,
+      32,
+      EGL_ALPHA_SIZE,
+      0,
+      EGL_BLUE_SIZE,
+      8,
+      EGL_RED_SIZE,
+      8,
+      EGL_GREEN_SIZE,
+      8,
+      EGL_DEPTH_SIZE,
+      0,
+      EGL_SURFACE_TYPE,
+      EGL_WINDOW_BIT,
+      EGL_RENDERABLE_TYPE,
+      EGL_OPENGL_ES2_BIT,
+      EGL_NONE,
+  };
+
+  EGLint num_configs;
+  if (!eglChooseConfig(display_, attribs, &config_, 1, &num_configs)) {
+    ALOGE("Couldn't find config");
+    return false;
+  }
+
+  std::unique_ptr<DummyNativeWindow> window(new DummyNativeWindow());
+
+  surface_ = eglCreateWindowSurface(display_, config_, window.get(), nullptr);
+  if (surface_ == EGL_NO_SURFACE) {
+    ALOGE("Failed to create egl surface");
+    return false;
+  }
+  window.release();
+
+  EGLint context_attribs[] = {EGL_CONTEXT_MAJOR_VERSION,
+                              3,
+                              EGL_CONTEXT_MINOR_VERSION,
+                              1,
+                              EGL_CONTEXT_PRIORITY_LEVEL_IMG,
+                              EGL_CONTEXT_PRIORITY_HIGH_IMG,
+                              EGL_NONE};
+  context_ = eglCreateContext(display_, config_, nullptr, context_attribs);
+  if (!eglMakeCurrent(display_, surface_, surface_, context_)) {
+    ALOGE("Unable to create GLESv2 context");
+    return false;
+  }
+
+  load_gl_extensions();
+
+  glEnable(BINNING_CONTROL_HINT_QCOM);
+  glHint(BINNING_CONTROL_HINT_QCOM, RENDER_DIRECT_TO_FRAMEBUFFER_QCOM);
+  is_render_direct_ = true;
+  CHECK_GL();
+
+  // Initialize the placeholder 1x1 framebuffer that we bind during compute
+  // shader instances to avoid accesses to other framebuffers.
+  glGenFramebuffers(1, &compute_fbo_);
+  glGenTextures(1, &compute_fbo_texture_);
+  glBindFramebuffer(GL_FRAMEBUFFER, compute_fbo_);
+  glBindTexture(GL_TEXTURE_2D, compute_fbo_texture_);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+               nullptr);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         compute_fbo_texture_, 0);
+  CHECK_GL();
+  CHECK_GL_FBO();
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+  return true;
+}
+
+void Compositor::Shutdown() {
+  render_target_[0].Destroy();
+  render_target_[1].Destroy();
+  layers_.clear();
+  glDeleteFramebuffers(1, &compute_fbo_);
+  glDeleteTextures(1, &compute_fbo_texture_);
+
+  debug_hud_.reset();
+  eds_renderer_.reset();
+
+  if (context_) {
+    eglDestroyContext(display_, context_);
+    context_ = 0;
+  }
+
+  if (surface_ != EGL_NO_SURFACE) {
+    eglDestroySurface(display_, surface_);
+    surface_ = EGL_NO_SURFACE;
+  }
+}
+
+void Compositor::RemoveAllBuffers() { layers_.clear(); }
+
+void Compositor::UpdateSurfaces(
+    const std::vector<std::shared_ptr<DisplaySurface>>& surfaces) {
+  // Delete the removed surfaces.
+  layers_.erase(
+      std::remove_if(layers_.begin(), layers_.end(),
+                     [&surfaces](const AppFrame& layer) {
+                       for (const auto& surface : surfaces)
+                         if (surface->surface_id() == layer.surface_id())
+                           return false;
+                       return true;
+                     }),
+      layers_.end());
+  // New surfaces are added on-demand as buffers are posted.
+}
+
+Compositor::AppFrame::AppFrame()
+    : surface_id_(-1),
+      blur_(0.0f),
+      z_order_(0),
+      vertical_flip_(false),
+      enable_cac_(true),
+      render_buffer_index_(0) {}
+
+Compositor::AppFrame::~AppFrame() {}
+
+const Compositor::Texture* Compositor::AppFrame::GetGlTextureId(
+    EGLDisplay display, int index) {
+  auto buffer_consumer = buffer_.buffer();
+  if (!buffer_consumer) {
+    return nullptr;
+  }
+  auto texture_it = std::find_if(
+      textures_.begin(), textures_.end(),
+      [buffer_consumer, index](const std::shared_ptr<Texture>& t) {
+        return t->consumer() == buffer_consumer && t->index() == index;
+      });
+
+  if (texture_it != textures_.end()) {
+    return (*texture_it).get();
+  }
+
+  textures_.push_back(
+      std::make_shared<Texture>(buffer_consumer, display, index));
+  if (!textures_.back()->Initialize()) {
+    textures_.pop_back();
+    return nullptr;
+  }
+  return textures_.back().get();
+}
+
+bool Compositor::AppFrame::UpdateSurface(
+    const std::shared_ptr<DisplaySurface>& surface) {
+  int surface_id = surface->surface_id();
+  float blur = surface->manager_blur();
+  bool need_sort = false;
+  if (z_order_ != surface->layer_order()) {
+    need_sort = true;
+    z_order_ = surface->layer_order();
+  }
+
+  surface_id_ = surface_id;
+  if (!render_pose_buffer_object_) {
+    render_pose_buffer_object_.reset(
+        new RenderPoseBufferObject(surface->GetMetadataBufferFd()));
+  }
+
+  blur_ = blur;
+  vertical_flip_ =
+      !!(surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_VERTICAL_FLIP);
+  enable_cac_ =
+      !(surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_CAC);
+
+  AcquiredBuffer skipped_buffer;
+  AcquiredBuffer buffer =
+      surface->AcquireNewestAvailableBuffer(&skipped_buffer);
+  if (!skipped_buffer.IsEmpty()) {
+    DebugHudData::data.SkipLayerFrame(z_order_);
+    ATRACE_NAME("DropToCatchUp");
+    ATRACE_ASYNC_END("BufferPost", skipped_buffer.buffer()->id());
+  }
+  if (!buffer.IsEmpty()) {
+    DebugHudData::data.AddLayerFrame(z_order_);
+    // Buffer was already ready, so we don't need to wait on the fence.
+    buffer.ClaimAcquireFence().Close();
+    ATRACE_ASYNC_END("BufferPost", buffer.buffer()->id());
+
+    render_buffer_index_ = surface->GetRenderBufferIndex(buffer.buffer()->id());
+
+#ifdef TRACE
+    const volatile DisplaySurfaceMetadata* data =
+        surface->GetMetadataBufferPtr();
+#endif
+    ALOGE_IF(TRACE, "read pose index %d %f %f", render_buffer_index_,
+             data->orientation[render_buffer_index_][0],
+             data->orientation[render_buffer_index_][1]);
+
+    // Move the new buffer over the old. AcquiredBuffer releases the old one.
+    buffer_ = std::move(buffer);
+  }
+  return need_sort;
+}
+
+void Compositor::AppFrame::UpdateVideoMeshSurface(
+    const std::shared_ptr<DisplaySurface>& surface) {
+  // Update |video_compositors_| with |video_surface|. Note that
+  // |UpdateVideoMeshSurface| should only be called on the PostThread before
+  // |DrawFrame| is called. Thus, no synchronization is required for
+  // |video_compositors_|.
+  if (!surface->video_mesh_surfaces_updated())
+    return;
+
+  // TODO(jwcai) The following loop handles adding new surfaces; video mesh
+  // removal logic shall be handled by listening to |OnChannelClose| event from
+  // DisplayService.
+  for (const auto& video_surface : surface->GetVideoMeshSurfaces()) {
+    // Here we assume number of |video_surface|s is relatively small, thus, the
+    // merge should be efficient enough.
+    auto video_compositor_it = std::find_if(
+        video_compositors_.begin(), video_compositors_.end(),
+        [video_surface](const std::shared_ptr<VideoCompositor>& c) {
+          return c->surface_id() == video_surface->surface_id();
+        });
+
+    if (video_compositor_it == video_compositors_.end()) {
+      // This video surface is new, create a new VideoCompositor.
+      video_compositors_.push_back(std::make_shared<VideoCompositor>(
+          video_surface, surface->GetMetadataBufferPtr()));
+    } else {
+      // There is a compositor in |video_compositors_| is already set up for
+      // this |video_surface|.
+      ALOGW("Duplicated video_mesh_surface: surface_id=%d",
+            video_surface->surface_id());
+    }
+  }
+}
+
+void Compositor::AppFrame::ResetBlurrers() { blurrers_.clear(); }
+
+void Compositor::AppFrame::AddBlurrer(Blur* blurrer) {
+  blurrers_.emplace_back(blurrer);
+}
+
+void Compositor::PostBuffer(const std::shared_ptr<DisplaySurface>& surface) {
+  int surface_id = surface->surface_id();
+
+  ALOGD_IF(TRACE, "Post surface %d", surface_id);
+
+  auto layer_it = std::find_if(layers_.begin(), layers_.end(),
+                               [surface_id](const AppFrame& frame) {
+                                 return frame.surface_id() == surface_id;
+                               });
+
+  bool need_sort = false;
+  if (layer_it == layers_.end()) {
+    layers_.push_back(AppFrame());
+    layer_it = layers_.end() - 1;
+    need_sort = true;
+  }
+
+  need_sort |= layer_it->UpdateSurface(surface);
+  layer_it->UpdateVideoMeshSurface(surface);
+
+  if (need_sort) {
+    std::stable_sort(layers_.begin(), layers_.end());
+  }
+}
+
+std::vector<uint8_t> Compositor::ReadLayerPixels(size_t index, int* width,
+                                                 int* height) {
+  if (index >= layers_.size()) {
+    return {};
+  }
+
+  const Texture* texture = layers_[index].GetGlTextureId(display_, 0);
+  if (!texture) {
+    return {};
+  }
+
+  *width = texture->size()[0];
+  *height = texture->size()[1];
+  return ReadTextureRGBA(texture->texture_id(), *width, *height);
+}
+
+std::vector<uint8_t> Compositor::ReadBufferPixels(const IonBuffer* buffer) {
+  android::sp<TemporaryNativeBuffer> native_buffer =
+      new TemporaryNativeBuffer(buffer);
+
+  // Finish to make sure the GL driver has completed drawing of prior FBOs.
+  // Since we are creating an EGL image here, the driver will not know that
+  // there is a dependency on earlier GL draws.
+  glFinish();
+
+  EGLImageKHR image = eglCreateImageKHR(
+      display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+      static_cast<ANativeWindowBuffer*>(native_buffer.get()), nullptr);
+  if (!image) {
+    ALOGE("Failed to create EGLImage\n");
+    return {};
+  }
+
+  GLuint texture_id;
+  glGenTextures(1, &texture_id);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, texture_id);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+
+  int width = buffer->width();
+  int height = buffer->height();
+  std::vector<uint8_t> data = ReadTextureRGBA(texture_id, width, height);
+
+  glBindTexture(GL_TEXTURE_2D, 0);
+  glDeleteTextures(1, &texture_id);
+  eglDestroyImageKHR(display_, image);
+  return data;
+}
+
+bool Compositor::DrawFrame(uint32_t target_vsync_count,
+                           LocalHandle* buffer_fence_fd) {
+  CheckAndUpdateHeadMountMetrics(false);
+
+  ATRACE_NAME("Compositor::DrawFrame");
+  GpuProfiler::Get()->PollGlTimerQueries();
+
+  if (buffer_fence_fd)
+    buffer_fence_fd->Close();
+
+  int num_layers = 0;
+  const int kMaxLayers = 4;
+  GLuint texture_id[2][kMaxLayers] = {{0}};
+  GLuint render_pose_buffer_id[kMaxLayers] = {0};
+  uint32_t render_buffer_index[kMaxLayers] = {0};
+  bool vertical_flip[kMaxLayers] = {false};
+  bool separate_eye_textures[kMaxLayers] = {false};
+  bool enable_cac[kMaxLayers] = {};
+  CHECK_GL();
+  for (auto& layer : layers_) {
+    if (!layer.buffer().buffer()) {
+      ATRACE_NAME("no_buffer");
+      continue;
+    }
+
+    // Extract surface parameters.
+    render_buffer_index[num_layers] = layer.render_buffer_index();
+    render_pose_buffer_id[num_layers] =
+        layer.render_pose_buffer_object()->object_id();
+    vertical_flip[num_layers] = layer.vertical_flip();
+    enable_cac[num_layers] =
+        head_mount_metrics_.supports_chromatic_aberration_correction() &&
+        layer.enable_cac();
+
+    // Extract per-eye textures. These may be separate or joined (atlased).
+    vec2i size(0, 0);
+    int view_count = layer.buffer().buffer()->slice_count();
+    ALOGE_IF(view_count > 2, "Error: more than 2 views not supported");
+    view_count = std::min(2, view_count);
+    separate_eye_textures[num_layers] = (view_count > 1);
+    bool is_missing_texture = false;
+    for (int eye = 0; eye < 2; ++eye) {
+      // If view_count is 1, each eye texture is the 0th.
+      int view_index = (view_count == 2) ? eye : 0;
+      const Texture* texture = layer.GetGlTextureId(display_, view_index);
+      // Texture will be null if the EGL image creation fails (hopefully never).
+      if (!texture) {
+        is_missing_texture = true;
+        break;
+      }
+      // All views are currently expected to have the same size.
+      size = texture->size();
+      texture_id[eye][num_layers] = texture->texture_id();
+    }
+    if (is_missing_texture) {
+      continue;
+    }
+
+    // Perform blur if requested.
+    if (fabs(layer.blur()) > 0.001f) {
+      // No need for CAC on blurred layers.
+      enable_cac[num_layers] = false;
+      if (layer.blurrer_count() < 1 || layer.blurrer(0)->width() != size[0] ||
+          layer.blurrer(0)->height() != size[1]) {
+        // Blur is created with the left eye texture, but the same instance
+        // can be used for the right eye as well.
+        layer.ResetBlurrers();
+        layer.AddBlurrer(new Blur(size[0], size[1], texture_id[0][num_layers],
+                                  GL_TEXTURE_2D, GL_TEXTURE_2D, true, display_,
+                                  view_count));
+      }
+      // Reset blur instances to prepare for drawing.
+      layer.blurrer(0)->StartFrame();
+      layer.blurrer(0)->set_scale(layer.blur());
+      // Perform blur and replace source texture with blurred output texture.
+      if (view_count == 1) {
+        // Single wide buffer for both eyes, blur both eyes in one operation.
+        texture_id[0][num_layers] = texture_id[1][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[0][num_layers]);
+      } else {
+        // Split eye buffers in a single frame, blur each framebuffer.
+        texture_id[0][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[0][num_layers]);
+        texture_id[1][num_layers] =
+            layer.blurrer(0)->DrawBlur(texture_id[1][num_layers]);
+      }
+    }
+
+    ++num_layers;
+    if (num_layers >= kMaxLayers)
+      break;
+  }
+
+  CHECK_GL();
+  // Set appropriate binning mode for the number of layers.
+  if (num_layers > 1 && is_render_direct_) {
+    is_render_direct_ = false;
+    glDisable(BINNING_CONTROL_HINT_QCOM);
+  } else if (num_layers <= 1 && !is_render_direct_) {
+    is_render_direct_ = true;
+    glEnable(BINNING_CONTROL_HINT_QCOM);
+    glHint(BINNING_CONTROL_HINT_QCOM, RENDER_DIRECT_TO_FRAMEBUFFER_QCOM);
+  }
+
+  // Workaround for GL driver bug that causes the currently bound FBO to be
+  // accessed during a compute shader pass (DoLateLatch below). Based on an
+  // analysis with systrace, the best pattern here was to run the compute shader
+  // with a *different* FBO than what will be drawn to afterward. So we bind
+  // a dummy 1x1 FBO here and discard it. If instead, the current render target
+  // is bound during the compute shader, the following draw calls will be forced
+  // into direct mode rendering.
+  glBindFramebuffer(GL_FRAMEBUFFER, compute_fbo_);
+  GLenum attachment = GL_COLOR_ATTACHMENT0;
+  glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachment);
+
+  // Double buffer the render target.  Get the render target we're drawing into,
+  // and update the active buffer to the next buffer.
+  RenderTarget& render_target = GetRenderTarget();
+  SetNextRenderTarget();
+
+  if (num_layers > 0) {
+    // This trace prints the EDS+Warp GPU overhead and prints every 5 seconds:
+    TRACE_GPU_PRINT("GPU EDS+Warp", 5 * 60);
+    CHECK_GL();
+    eds_renderer_->DoLateLatch(target_vsync_count, render_buffer_index,
+                               render_pose_buffer_id, vertical_flip,
+                               separate_eye_textures, num_layers);
+
+    render_target.BindFramebuffer();
+
+    // Discard to avoid unresolving the framebuffer during tiled rendering.
+    render_target.DiscardColorAttachment();
+
+    // For tiled mode rendering, we clear every frame to avoid garbage showing
+    // up in the parts of tiles that are not rendered.
+    if (!is_render_direct_) {
+      glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+      glClear(GL_COLOR_BUFFER_BIT);
+    }
+
+    for (int eye = kLeftEye; eye <= kRightEye; ++eye) {
+      eds_renderer_->PrepGlState(static_cast<EyeType>(eye));
+      for (int layer_i = 0; layer_i < num_layers; ++layer_i) {
+        bool blend_with_previous = layer_i > 0;
+        uint32_t current_buffer_index = render_buffer_index[layer_i];
+
+        // Render video mesh in the background of each graphics layer.
+        layers_[layer_i].ForEachVideoCompositor([this, eye, layer_i,
+                                                 current_buffer_index,
+                                                 &blend_with_previous](
+            const std::shared_ptr<VideoCompositor>& video_compositor) mutable {
+          eds_renderer_->DrawVideoQuad(
+              static_cast<EyeType>(eye), layer_i,
+              video_compositor->GetActiveTextureId(display_),
+              video_compositor->GetTransform(eye, current_buffer_index));
+          blend_with_previous = true;
+        });
+
+        // Apply distortion to frame submitted from the app's GL context.
+        eds_renderer_->SetChromaticAberrationCorrectionEnabled(
+            enable_cac[layer_i]);
+        eds_renderer_->ApplyDistortionCorrectionToTexture(
+            static_cast<EyeType>(eye), &texture_id[eye][layer_i],
+            &vertical_flip[layer_i], &separate_eye_textures[layer_i], &layer_i,
+            1, blend_with_previous, false);
+      }
+    }
+    eds_renderer_->ResetGlState(1);
+    CHECK_GL();
+  } else {
+    ALOGI("No buffers for compositing, clearing to black.");
+    render_target.BindFramebuffer();
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+  }
+
+  debug_hud_->Update();
+  debug_hud_->Draw();
+
+  LocalHandle fence_fd = CreateGLSyncAndFlush(display_);
+
+  if (buffer_fence_fd)
+    *buffer_fence_fd = std::move(fence_fd);
+
+  if (eds_pose_capture_enabled_) {
+    std::lock_guard<std::mutex> _lock(mutex_);
+    eds_renderer_->GetLastEdsPose(&eds_pose_capture_);
+  }
+
+  return true;
+}
+
+bool Compositor::GetLastEdsPose(LateLatchOutput* out_data) {
+  if (eds_pose_capture_enabled_) {
+    std::lock_guard<std::mutex> _lock(mutex_);
+    *out_data = eds_pose_capture_;
+    return true;
+  } else {
+    ALOGE("Eds pose capture is not enabled.");
+    return false;
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/compositor.h b/libs/vr/libvrflinger/compositor.h
new file mode 100644
index 0000000..be26d31
--- /dev/null
+++ b/libs/vr/libvrflinger/compositor.h
@@ -0,0 +1,233 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_COMPOSITOR_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_COMPOSITOR_H_
+
+#include <EGL/egl.h>
+#include <log/log.h>
+#include <utils/StrongPointer.h>
+
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <vector>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/display_metrics.h>
+#include <private/dvr/distortion_renderer.h>
+#include <private/dvr/frame_time_history.h>
+#include <private/dvr/ion_buffer.h>
+#include <private/dvr/native_buffer.h>
+
+#include "acquired_buffer.h"
+#include "video_compositor.h"
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class Blur;
+class BufferConsumer;
+class CompositeHmd;
+class DebugHudView;
+class DisplaySurface;
+
+// This is a GPU compositor for software EDS and lens warp on buffers provided
+// by HardwareComposer.
+class Compositor {
+ public:
+  Compositor();
+  ~Compositor();
+
+  bool Initialize(const DisplayMetrics& display_metrics);
+  void UpdateHeadMountMetrics(const HeadMountMetrics& head_mount_metrics);
+  void Shutdown();
+
+  // Renders a frame with the latest buffers with EDS and warp applied.
+  // buffer_fence_fd can be used to get a fence for the rendered frame. It can
+  // be set to null if the fence isn't needed.
+  bool DrawFrame(uint32_t target_vsync_count,
+                 pdx::LocalHandle* buffer_fence_fd);
+
+  // Remove all buffers.
+  void RemoveAllBuffers();
+
+  // Synchronize compositor layers with in given surfaces.
+  void UpdateSurfaces(
+      const std::vector<std::shared_ptr<DisplaySurface>>& surfaces);
+
+  // This must be called for each surface before DrawFrame is called.
+  void PostBuffer(const std::shared_ptr<DisplaySurface>& surface);
+
+  std::shared_ptr<IonBuffer> GetBuffer() const {
+    return render_target_[active_render_target_].buffer();
+  }
+
+  // Returns the number of layers being rendered by the compositor.
+  size_t GetLayerCount() const { return layers_.size(); }
+
+  // Returns the source buffer at the given layer index or nullptr if none is
+  // available.
+  std::shared_ptr<BufferConsumer> PeekAtLayer(size_t index) const {
+    if (index >= GetLayerCount())
+      return nullptr;
+    return layers_[index].buffer().buffer();
+  }
+
+  // Expensive operation to transfer the pixels of the given layer index into
+  // unformatted memory and return as a RGBA buffer.
+  // On success, returns non-zero sized vector and sets width and height.
+  // On failure, returns empty vector.
+  std::vector<uint8_t> ReadLayerPixels(size_t index, int* width, int* height);
+
+  // Expensive operation to transfer the pixels of the given buffer into
+  // unformatted memory and return as a RGBA buffer.
+  // On success, returns non-zero sized vector.
+  // On failure, returns empty vector.
+  std::vector<uint8_t> ReadBufferPixels(const IonBuffer* buffer);
+
+  bool GetLastEdsPose(LateLatchOutput* out_data);
+
+  const HeadMountMetrics& head_mount_metrics() const {
+    return head_mount_metrics_;
+  }
+
+ private:
+  class Texture;
+  class RenderPoseBufferObject;
+
+  // A rendered frame from an application.
+  class AppFrame {
+   public:
+    AppFrame();
+    ~AppFrame();
+
+    AppFrame(AppFrame&& other) = default;
+    AppFrame& operator=(AppFrame&&) = default;
+
+    // Gets a GL texture object for the current buffer. The resulting texture
+    // object will be cached for future calls. Returns a pointer for temporary
+    // access - not meant to hold on to.
+    const Texture* GetGlTextureId(EGLDisplay display, int index);
+
+    bool operator<(const AppFrame& rhs) const {
+      return z_order_ < rhs.z_order_;
+    }
+    int z_order() const { return z_order_; }
+    // Return true if this surface z order has been changed.
+    bool UpdateSurface(const std::shared_ptr<DisplaySurface>& surface);
+    void UpdateVideoMeshSurface(const std::shared_ptr<DisplaySurface>& surface);
+    void ResetBlurrers();
+    void AddBlurrer(Blur* blurrer);
+
+    const AcquiredBuffer& buffer() const { return buffer_; }
+    int surface_id() const { return surface_id_; }
+    float blur() const { return blur_; }
+    bool vertical_flip() const { return vertical_flip_; }
+    bool enable_cac() const { return enable_cac_; }
+    size_t blurrer_count() const { return blurrers_.size(); }
+    Blur* blurrer(size_t i) {
+      return blurrers_.size() < i ? nullptr : blurrers_[i].get();
+    }
+    uint32_t render_buffer_index() const { return render_buffer_index_; }
+    const RenderPoseBufferObject* render_pose_buffer_object() const {
+      return render_pose_buffer_object_.get();
+    }
+
+    template <class A>
+    void ForEachVideoCompositor(A action) const {
+      for (auto& c : video_compositors_) {
+        action(c);
+      }
+    }
+
+   private:
+    int surface_id_;
+    float blur_;
+    int z_order_;
+    bool vertical_flip_;
+    bool enable_cac_;
+    std::vector<std::unique_ptr<Blur>> blurrers_;
+    AcquiredBuffer buffer_;
+    std::vector<std::shared_ptr<Texture>> textures_;
+    uint32_t render_buffer_index_;
+    std::unique_ptr<RenderPoseBufferObject> render_pose_buffer_object_;
+
+    // Active video mesh compositors
+    std::vector<std::shared_ptr<VideoCompositor>> video_compositors_;
+
+    AppFrame(const AppFrame& other) = delete;
+    AppFrame& operator=(const AppFrame&) = delete;
+  };
+
+  class RenderTarget {
+   public:
+    RenderTarget();
+    ~RenderTarget();
+
+    void Initialize(int width, int height);
+    void Destroy();
+    void BindFramebuffer();
+    void DiscardColorAttachment();
+
+    std::shared_ptr<IonBuffer> buffer() const { return buffer_; }
+
+   private:
+    std::shared_ptr<IonBuffer> buffer_;
+    android::sp<NativeBuffer> native_buffer_;
+
+    GLuint buffer_texture_id_;
+    GLuint buffer_framebuffer_id_;
+    EGLImageKHR buffer_image_;
+  };
+
+  Compositor(const Compositor&) = delete;
+  void operator=(const Compositor&) = delete;
+
+  bool InitializeEGL();
+
+  void UpdateHudToggle();
+  void PrintStatsHud();
+  void CheckAndUpdateHeadMountMetrics(bool force_update);
+
+  RenderTarget& GetRenderTarget() {
+    return render_target_[active_render_target_];
+  }
+
+  void SetNextRenderTarget() {
+    active_render_target_ = (active_render_target_ + 1) & 1;
+  }
+
+  std::vector<AppFrame> layers_;
+
+  DisplayMetrics display_metrics_;
+  HeadMountMetrics head_mount_metrics_;
+
+  EGLDisplay display_;
+  EGLConfig config_;
+  EGLSurface surface_;
+  EGLContext context_;
+  int active_render_target_;
+  RenderTarget render_target_[2];
+  bool is_render_direct_;
+
+  // FBO for compute shader.
+  GLuint compute_fbo_;
+  GLuint compute_fbo_texture_;
+
+  std::unique_ptr<DebugHudView> debug_hud_;
+
+  // EDS:
+  std::unique_ptr<CompositeHmd> composite_hmd_;
+  bool hmd_metrics_requires_update_;
+  std::unique_ptr<DistortionRenderer> eds_renderer_;
+
+  bool eds_pose_capture_enabled_;
+  std::mutex mutex_;
+  LateLatchOutput eds_pose_capture_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_COMPOSITOR_H_
diff --git a/libs/vr/libvrflinger/debug_hud_data.cpp b/libs/vr/libvrflinger/debug_hud_data.cpp
new file mode 100644
index 0000000..d387bba
--- /dev/null
+++ b/libs/vr/libvrflinger/debug_hud_data.cpp
@@ -0,0 +1,9 @@
+#include "debug_hud_data.h"
+
+namespace android {
+namespace dvr {
+
+DebugHudData DebugHudData::data;
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/debug_hud_data.h b/libs/vr/libvrflinger/debug_hud_data.h
new file mode 100644
index 0000000..778169d
--- /dev/null
+++ b/libs/vr/libvrflinger/debug_hud_data.h
@@ -0,0 +1,110 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_DATA_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_DATA_H_
+
+#include <stdint.h>
+
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/frame_time_history.h>
+
+namespace android {
+namespace dvr {
+
+// Tracks debug stats for the displayd debug HUD. Unless otherwise noted,
+// there is no synchronization of data accesses to avoid performance impact.
+// All accesses to this data are on the displayd HWC post thread. Accesses from
+// other threads will need to be duly protected from races.
+// This is a lightweight struct to make it easy to add and remove
+// tracking data.
+struct DebugHudData {
+  // Maximum supported layers for the debug HUD.
+  enum { kMaxLayers = 4 };
+
+  // The global singleton HUD data instance.
+  static DebugHudData data;
+
+  // Tracks framerate and skipped frames.
+  struct FrameStats {
+    void AddFrame() {
+      int64_t now = GetSystemClockNs();
+      frame_time.AddSample(now - last_frame_ts);
+      last_frame_ts = now;
+    }
+
+    void SkipFrame() {
+      AddFrame();
+      ++drops;
+    }
+
+    int drops = 0;
+    int64_t last_frame_ts = 0;
+    FrameTimeHistory frame_time;
+  };
+
+  // Debug data for compositor layers (applications, system UI, etc).
+  struct LayerData {
+    void Reset() {
+      ResetStats();
+      width = 0;
+      height = 0;
+      is_separate = false;
+    }
+
+    void ResetStats() { frame_stats.drops = 0; }
+
+    FrameStats frame_stats;
+    int width = 0;
+    int height = 0;
+    bool is_separate = false;
+  };
+
+  // Resets the stats.
+  void ResetStats() {
+    hwc_frame_stats.drops = 0;
+    hwc_latency = 0;
+    for (auto& l : layer_data)
+      l.ResetStats();
+  }
+
+  // Resets the layer configuration.
+  void ResetLayers() {
+    num_layers = 0;
+    for (auto& l : layer_data)
+      l.Reset();
+  }
+
+  // Tracks a frame arrival for the given layer.
+  void AddLayerFrame(size_t layer) {
+    if (layer < kMaxLayers) {
+      num_layers = std::max(layer + 1, num_layers);
+      layer_data[layer].frame_stats.AddFrame();
+    }
+  }
+
+  // Tracks a frame skip/drop for the given layer.
+  void SkipLayerFrame(size_t layer) {
+    if (layer < kMaxLayers) {
+      num_layers = std::max(layer + 1, num_layers);
+      layer_data[layer].frame_stats.SkipFrame();
+    }
+  }
+
+  // Sets the resolution and other details of the layer.
+  void SetLayerInfo(size_t layer, int width, int height, bool is_separate) {
+    if (layer < kMaxLayers) {
+      num_layers = std::max(layer + 1, num_layers);
+      layer_data[layer].width = width;
+      layer_data[layer].height = height;
+      layer_data[layer].is_separate = is_separate;
+    }
+  }
+
+  FrameStats hwc_frame_stats;
+  int64_t hwc_latency = 0;
+  size_t num_layers = 0;
+  LayerData layer_data[kMaxLayers];
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_DATA_H_
diff --git a/libs/vr/libvrflinger/debug_hud_view.cpp b/libs/vr/libvrflinger/debug_hud_view.cpp
new file mode 100644
index 0000000..4936ac6
--- /dev/null
+++ b/libs/vr/libvrflinger/debug_hud_view.cpp
@@ -0,0 +1,91 @@
+#include "debug_hud_view.h"
+
+#include <dvr/pose_client.h>
+
+#include "debug_hud_data.h"
+
+namespace android {
+namespace dvr {
+
+DebugHudView::DebugHudView(const CompositeHmd& hmd) {
+  pose_client_ = dvrPoseCreate();
+
+  display_size_ = hmd.GetDisplayMetrics().GetSizePixels();
+  vec2 display_size_meters = hmd.GetDisplayMetrics().GetSizeMeters();
+  inter_lens_dist_screen_space_ =
+      2.0f * hmd.GetHeadMountMetrics().GetInterLensDistance() /
+      std::max(display_size_meters[0], display_size_meters[1]);
+}
+
+DebugHudView::~DebugHudView() {
+  if (pose_client_)
+    dvrPoseDestroy(pose_client_);
+  pose_client_ = nullptr;
+}
+
+void DebugHudView::Update() {
+  // Check for gesture that enables the debug stats HUD.
+  if (!pose_client_)
+    return;
+  DvrPoseAsync pose;
+  dvrPoseGet(pose_client_, 0, &pose);
+  float32x4_t q = pose.orientation;
+  quat orientation(q[3], q[0], q[1], q[2]);
+  vec3 up = orientation * vec3(0, 1, 0);
+  if (up[1] < -0.8f) {
+    ++switch_timer_;
+  } else {
+    switch_timer_ = 0;
+  }
+  // A few seconds upside down => toggle stats HUD.
+  if (switch_timer_ > 200) {
+    switch_timer_ = 0;
+    enabled_ = !enabled_;
+    DebugHudData::data.ResetStats();
+    ALOGE("Toggle debug stats HUD: %s", enabled_ ? "ON" : "OFF");
+  }
+}
+
+void DebugHudView::Draw() {
+  if (!enabled_)
+    return;
+  if (!debug_text_)
+    debug_text_.reset(new DebugText(400, display_size_[0], display_size_[1]));
+
+  const DebugHudData& data = DebugHudData::data;
+  const size_t layer_char_count = 50;
+  char layer_data[DebugHudData::kMaxLayers][layer_char_count];
+  for (size_t i = 0; i < data.num_layers; ++i) {
+    float fps = data.layer_data[i].frame_stats.frame_time.GetAverageFps();
+    snprintf(layer_data[i], layer_char_count,
+             "Layer %d %dx%d%s FPS: %.2f Drops: %d\n", static_cast<int>(i),
+             data.layer_data[i].width, data.layer_data[i].height,
+             data.layer_data[i].is_separate ? "x2" : "", fps,
+             data.layer_data[i].frame_stats.drops);
+  }
+
+  float hwc_fps = data.hwc_frame_stats.frame_time.GetAverageFps();
+
+  char text[400];
+  float hwc_latency_ms = static_cast<float>(data.hwc_latency) / 1000000.0f;
+  snprintf(text, sizeof(text), "HWC FPS: %.2f Latency: %.3f ms Skips: %d\n",
+           hwc_fps, hwc_latency_ms, data.hwc_frame_stats.drops);
+
+  for (size_t i = 0; i < data.num_layers; ++i) {
+    strncat(text, layer_data[i], sizeof(text) - strlen(text) - 1);
+  }
+
+  // Ensure text termination.
+  text[sizeof(text) - 1] = '\0';
+
+  glViewport(0, 0, display_size_[0], display_size_[1]);
+  glEnable(GL_BLEND);
+  // No stereo, because you can see the HUD OK in one eye. Stereo actually
+  // makes it more difficult to focus sometimes. To enable stereo:
+  // replace the second to last parameter with inter_lens_dist_screen_space_.
+  debug_text_->Draw(0.0f, -0.7f * inter_lens_dist_screen_space_, text, 0.0f, 1);
+  glDisable(GL_BLEND);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/debug_hud_view.h b/libs/vr/libvrflinger/debug_hud_view.h
new file mode 100644
index 0000000..50f38a8
--- /dev/null
+++ b/libs/vr/libvrflinger/debug_hud_view.h
@@ -0,0 +1,48 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_VIEW_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_VIEW_H_
+
+#include <stdint.h>
+
+#include <utils/Log.h>
+
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/graphics/debug_text.h>
+
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class CompositeHmd;
+
+// The view and the controller for the displayd debug HUD.
+// The HUD is enabled and disabled by internally tracking the head pose.
+// When the head pose is upside down for ~3 seconds, the enabled state toggles.
+// See DebugHudData for the data that is reported.
+class DebugHudView {
+ public:
+  DebugHudView(const CompositeHmd& hmd);
+  ~DebugHudView();
+
+  // Updates HUD state.
+  void Update();
+
+  // Draws HUD into the current framebuffer if it is currently enabled.
+  void Draw();
+
+ private:
+  DebugHudView(const DebugHudView&) = delete;
+  DebugHudView& operator=(const DebugHudView&) = delete;
+
+  DvrPose* pose_client_ = nullptr;
+  vec2i display_size_;
+  bool enabled_ = false;
+  int switch_timer_ = 0;
+  float inter_lens_dist_screen_space_ = 0.0f;
+  std::unique_ptr<DebugText> debug_text_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DEBUG_HUD_VIEW_H_
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
new file mode 100644
index 0000000..6730ba8
--- /dev/null
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -0,0 +1,225 @@
+#include "display_manager_service.h"
+
+#include <pdx/channel_handle.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/display_rpc.h>
+#include <sys/poll.h>
+
+#include <array>
+
+using android::pdx::Channel;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Message;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::IfAnyOf;
+
+namespace {
+
+// As a first line of defense, the display manager endpoint is only accessible
+// to the user and group.
+
+// TODO(dnicoara): Remove read/write permission for others. This is in here just
+// to allow us to experiment with cast functionality from a plain old app.
+constexpr mode_t kDisplayManagerEndpointFileMode =
+    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
+constexpr size_t kMaxSurfacesPerRequest = 32;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+void DisplayManager::SetNotificationsPending(bool pending) {
+  int ret = service_->ModifyChannelEvents(channel_id_, pending ? 0 : POLLIN,
+                                          pending ? POLLIN : 0);
+  ALOGE_IF(ret < 0,
+           "DisplayManager::SetNotificationPending: Failed to modify channel "
+           "events: %s",
+           strerror(-ret));
+}
+
+DisplayManagerService::DisplayManagerService(
+    const std::shared_ptr<DisplayService>& display_service)
+    : BASE("DisplayManagerService",
+           Endpoint::Create(DisplayManagerRPC::kClientPath,
+                            kDisplayManagerEndpointFileMode)),
+      display_service_(display_service) {
+  display_service_->SetDisplayConfigurationUpdateNotifier(
+      std::bind(&DisplayManagerService::OnDisplaySurfaceChange, this));
+}
+
+std::shared_ptr<pdx::Channel> DisplayManagerService::OnChannelOpen(
+    pdx::Message& message) {
+  // Prevent more than one display manager from registering at a time.
+  if (display_manager_)
+    REPLY_ERROR_RETURN(message, EPERM, nullptr);
+
+  display_manager_ =
+      std::make_shared<DisplayManager>(this, message.GetChannelId());
+  return display_manager_;
+}
+
+void DisplayManagerService::OnChannelClose(
+    pdx::Message& /*message*/, const std::shared_ptr<pdx::Channel>& channel) {
+  // Unregister the display manager when the channel closes.
+  if (display_manager_ == channel)
+    display_manager_ = nullptr;
+}
+
+int DisplayManagerService::HandleMessage(pdx::Message& message) {
+  auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel());
+
+  switch (message.GetOp()) {
+    case DisplayManagerRPC::GetSurfaceList::Opcode:
+      DispatchRemoteMethod<DisplayManagerRPC::GetSurfaceList>(
+          *this, &DisplayManagerService::OnGetSurfaceList, message);
+      return 0;
+
+    case DisplayManagerRPC::GetSurfaceBuffers::Opcode:
+      DispatchRemoteMethod<DisplayManagerRPC::GetSurfaceBuffers>(
+          *this, &DisplayManagerService::OnGetSurfaceBuffers, message);
+      return 0;
+
+    case DisplayManagerRPC::UpdateSurfaces::Opcode:
+      DispatchRemoteMethod<DisplayManagerRPC::UpdateSurfaces>(
+          *this, &DisplayManagerService::OnUpdateSurfaces, message);
+      return 0;
+
+    default:
+      return Service::DefaultHandleMessage(message);
+  }
+}
+
+std::vector<DisplaySurfaceInfo> DisplayManagerService::OnGetSurfaceList(
+    pdx::Message& /*message*/) {
+  std::vector<DisplaySurfaceInfo> items;
+
+  display_service_->ForEachDisplaySurface([&items](
+      const std::shared_ptr<DisplaySurface>& surface) mutable {
+    DisplaySurfaceInfo item;
+
+    item.surface_id = surface->surface_id();
+    item.process_id = surface->process_id();
+    item.type = surface->type();
+    item.flags = 0;  // TODO(eieio)
+    item.client_attributes = DisplaySurfaceAttributes{
+        {DisplaySurfaceAttributeEnum::Visible,
+         DisplaySurfaceAttributeValue{surface->client_visible()}},
+        {DisplaySurfaceAttributeEnum::ZOrder,
+         DisplaySurfaceAttributeValue{surface->client_z_order()}},
+        {DisplaySurfaceAttributeEnum::Blur, DisplaySurfaceAttributeValue{0.f}}};
+    item.manager_attributes = DisplaySurfaceAttributes{
+        {DisplaySurfaceAttributeEnum::Visible,
+         DisplaySurfaceAttributeValue{surface->manager_visible()}},
+        {DisplaySurfaceAttributeEnum::ZOrder,
+         DisplaySurfaceAttributeValue{surface->manager_z_order()}},
+        {DisplaySurfaceAttributeEnum::Blur,
+         DisplaySurfaceAttributeValue{surface->manager_blur()}}};
+
+    items.push_back(item);
+  });
+
+  // The fact that we're in the message handler implies that display_manager_ is
+  // not nullptr. No check required, unless this service becomes multi-threaded.
+  display_manager_->SetNotificationsPending(false);
+
+  return items;
+}
+
+std::vector<LocalChannelHandle> DisplayManagerService::OnGetSurfaceBuffers(
+    pdx::Message& message, int surface_id) {
+  std::shared_ptr<DisplaySurface> surface =
+      display_service_->GetDisplaySurface(surface_id);
+  if (!surface)
+    REPLY_ERROR_RETURN(message, ENOENT, {});
+
+  std::vector<LocalChannelHandle> consumers;
+  int ret = surface->GetConsumers(&consumers);
+  if (ret < 0) {
+    ALOGE(
+        "DisplayManagerService::OnGetDisplaySurfaceBuffers: Failed to get "
+        "consumers for surface %d: %s",
+        surface_id, strerror(-ret));
+    REPLY_ERROR_RETURN(message, -ret, {});
+  }
+
+  return consumers;
+}
+
+int DisplayManagerService::OnUpdateSurfaces(
+    pdx::Message& /*message*/,
+    const std::map<int, DisplaySurfaceAttributes>& updates) {
+  for (const auto& surface_update : updates) {
+    const int surface_id = surface_update.first;
+    const DisplaySurfaceAttributes& attributes = surface_update.second;
+
+    std::shared_ptr<DisplaySurface> surface =
+        display_service_->GetDisplaySurface(surface_id);
+
+    if (!surface)
+      return -ENOENT;
+
+    for (const auto& attribute : attributes) {
+      const auto& key = attribute.first;
+      const auto* variant = &attribute.second;
+      bool invalid_value = false;
+      switch (key) {
+        case DisplaySurfaceAttributeEnum::ZOrder:
+          invalid_value =
+              !IfAnyOf<int32_t>::Call(variant, [&surface](const auto& value) {
+                surface->ManagerSetZOrder(value);
+              });
+          break;
+        case DisplaySurfaceAttributeEnum::Visible:
+          invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+              variant, [&surface](const auto& value) {
+                surface->ManagerSetVisible(value);
+              });
+          break;
+        case DisplaySurfaceAttributeEnum::Blur:
+          invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
+              variant, [&surface](const auto& value) {
+                surface->ManagerSetBlur(value);
+              });
+          break;
+        default:
+          ALOGW(
+              "DisplayManagerService::OnUpdateSurfaces: Attempt to set invalid "
+              "attribute %u on surface %d",
+              key, surface_id);
+          break;
+      }
+
+      if (invalid_value) {
+        ALOGW(
+            "DisplayManagerService::OnUpdateSurfaces: Failed to set display "
+            "surface attribute '%s' because of incompatible type: %d",
+            DisplaySurfaceAttributeEnum::ToString(key).c_str(),
+            variant->index());
+      }
+    }
+  }
+
+  // Reconfigure the display layers for any active surface changes.
+  display_service_->UpdateActiveDisplaySurfaces();
+  return 0;
+}
+
+void DisplayManagerService::OnDisplaySurfaceChange() {
+  if (display_manager_) {
+    display_manager_->SetNotificationsPending(true);
+  } else {
+    // If there isn't a display manager registered, default all display surfaces
+    // to visible.
+    display_service_->ForEachDisplaySurface(
+        [](const std::shared_ptr<DisplaySurface>& surface) {
+          surface->ManagerSetVisible(true);
+        });
+    display_service_->UpdateActiveDisplaySurfaces();
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
new file mode 100644
index 0000000..46401fa
--- /dev/null
+++ b/libs/vr/libvrflinger/display_manager_service.h
@@ -0,0 +1,73 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_MANAGER_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_MANAGER_SERVICE_H_
+
+#include <pdx/service.h>
+#include <private/dvr/display_rpc.h>
+
+#include "display_service.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayManagerService;
+
+// The display manager is a client of the display manager service. This class
+// represents the connected client that the display manager service sends
+// notifications to.
+class DisplayManager : public pdx::Channel {
+ public:
+  DisplayManager(DisplayManagerService* service, int channel_id)
+      : service_(service), channel_id_(channel_id) {}
+
+  int channel_id() const { return channel_id_; }
+
+  // Sets or clears the channel event mask to indicate pending events that the
+  // display manager on the other end of the channel should read and handle.
+  // When |pending| is true the POLLIN bit is set in the event mask; when
+  // |pending| is false the POLLIN bit is cleared in the event mask.
+  void SetNotificationsPending(bool pending);
+
+ private:
+  DisplayManager(const DisplayManager&) = delete;
+  void operator=(const DisplayManager&) = delete;
+
+  DisplayManagerService* service_;
+  int channel_id_;
+};
+
+// The display manager service marshalls state and events from the display
+// service to the display manager.
+class DisplayManagerService : public pdx::ServiceBase<DisplayManagerService> {
+ public:
+  std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
+  void OnChannelClose(pdx::Message& message,
+                      const std::shared_ptr<pdx::Channel>& channel) override;
+  int HandleMessage(pdx::Message& message) override;
+
+ private:
+  friend BASE;
+
+  explicit DisplayManagerService(
+      const std::shared_ptr<DisplayService>& display_service);
+
+  std::vector<DisplaySurfaceInfo> OnGetSurfaceList(pdx::Message& message);
+  std::vector<pdx::LocalChannelHandle> OnGetSurfaceBuffers(
+      pdx::Message& message, int surface_id);
+  int OnUpdateSurfaces(pdx::Message& message,
+                       const std::map<int, DisplaySurfaceAttributes>& updates);
+
+  // Called by the display service to indicate changes to display surfaces that
+  // the display manager should evaluate.
+  void OnDisplaySurfaceChange();
+
+  DisplayManagerService(const DisplayManagerService&) = delete;
+  void operator=(const DisplayManagerService&) = delete;
+
+  std::shared_ptr<DisplayService> display_service_;
+  std::shared_ptr<DisplayManager> display_manager_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_MANAGER_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
new file mode 100644
index 0000000..bb70c5c
--- /dev/null
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -0,0 +1,342 @@
+#include "display_service.h"
+
+#include <vector>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <pdx/rpc/remote_method.h>
+#include <private/dvr/composite_hmd.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/lucid_metrics.h>
+#include <private/dvr/numeric.h>
+#include <private/dvr/polynomial_radial_distortion.h>
+#include <private/dvr/types.h>
+
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::WrapBuffer;
+
+namespace android {
+namespace dvr {
+
+DisplayService::DisplayService() : DisplayService(nullptr) {}
+
+DisplayService::DisplayService(Hwc2::Composer* hidl)
+    : BASE("DisplayService", Endpoint::Create(DisplayRPC::kClientPath)),
+      hardware_composer_(hidl) {
+  hardware_composer_.Initialize();
+}
+
+bool DisplayService::IsInitialized() const {
+  return BASE::IsInitialized() && hardware_composer_.IsInitialized();
+}
+
+std::string DisplayService::DumpState(size_t max_length) {
+  std::vector<char> buffer(max_length);
+  uint32_t max_len_p = static_cast<uint32_t>(max_length);
+  hardware_composer_.Dump(buffer.data(), &max_len_p);
+  return std::string(buffer.data());
+}
+
+void DisplayService::OnChannelClose(pdx::Message& /*message*/,
+                                    const std::shared_ptr<Channel>& channel) {
+  auto surface = std::static_pointer_cast<SurfaceChannel>(channel);
+  if (surface && surface->type() == SurfaceTypeEnum::Normal) {
+    auto display_surface = std::static_pointer_cast<DisplaySurface>(surface);
+    display_surface->ManagerSetVisible(false);
+    display_surface->ClientSetVisible(false);
+    NotifyDisplayConfigurationUpdate();
+  }
+  // TODO(jwcai) Handle ChannelClose of VideoMeshSurface.
+}
+
+// First-level dispatch for display service messages. Directly handles messages
+// that are independent of the display surface (metrics, creation) and routes
+// surface-specific messages to the per-instance handlers.
+int DisplayService::HandleMessage(pdx::Message& message) {
+  auto channel = message.GetChannel<SurfaceChannel>();
+
+  switch (message.GetOp()) {
+    case DisplayRPC::GetMetrics::Opcode:
+      DispatchRemoteMethod<DisplayRPC::GetMetrics>(
+          *this, &DisplayService::OnGetMetrics, message);
+      return 0;
+
+    case DisplayRPC::GetEdsCapture::Opcode:
+      DispatchRemoteMethod<DisplayRPC::GetEdsCapture>(
+          *this, &DisplayService::OnGetEdsCapture, message);
+      return 0;
+
+    case DisplayRPC::CreateSurface::Opcode:
+      DispatchRemoteMethod<DisplayRPC::CreateSurface>(
+          *this, &DisplayService::OnCreateSurface, message);
+      return 0;
+
+    case DisplayRPC::EnterVrMode::Opcode:
+      DispatchRemoteMethod<DisplayRPC::EnterVrMode>(
+          *this, &DisplayService::OnEnterVrMode, message);
+      return 0;
+
+    case DisplayRPC::ExitVrMode::Opcode:
+      DispatchRemoteMethod<DisplayRPC::ExitVrMode>(
+          *this, &DisplayService::OnExitVrMode, message);
+      return 0;
+
+    case DisplayRPC::SetViewerParams::Opcode:
+      DispatchRemoteMethod<DisplayRPC::SetViewerParams>(
+          *this, &DisplayService::OnSetViewerParams, message);
+      return 0;
+
+    // Direct the surface specific messages to the surface instance.
+    case DisplayRPC::AllocateBuffer::Opcode:
+    case DisplayRPC::SetAttributes::Opcode:
+    case DisplayRPC::GetMetadataBuffer::Opcode:
+    case DisplayRPC::CreateVideoMeshSurface::Opcode:
+    case DisplayRPC::VideoMeshSurfaceCreateProducerQueue::Opcode:
+      return HandleSurfaceMessage(message);
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+SystemDisplayMetrics DisplayService::OnGetMetrics(pdx::Message& message) {
+  const Compositor* compositor = hardware_composer_.GetCompositor();
+  if (compositor == nullptr)
+    REPLY_ERROR_RETURN(message, EINVAL, {});
+
+  HeadMountMetrics head_mount = compositor->head_mount_metrics();
+  CompositeHmd hmd(head_mount, hardware_composer_.GetHmdDisplayMetrics());
+  vec2i distorted_render_size = hmd.GetRecommendedRenderTargetSize();
+  FieldOfView left_fov = hmd.GetEyeFov(kLeftEye);
+  FieldOfView right_fov = hmd.GetEyeFov(kRightEye);
+
+  SystemDisplayMetrics metrics;
+
+  metrics.display_native_width = GetDisplayMetrics().width;
+  metrics.display_native_height = GetDisplayMetrics().height;
+  metrics.display_x_dpi = GetDisplayMetrics().dpi.x;
+  metrics.display_y_dpi = GetDisplayMetrics().dpi.y;
+  metrics.distorted_width = distorted_render_size[0];
+  metrics.distorted_height = distorted_render_size[1];
+  metrics.vsync_period_ns =
+      hardware_composer_.native_display_metrics().vsync_period_ns;
+  metrics.hmd_ipd_mm = 0;
+  metrics.inter_lens_distance_m = head_mount.GetInterLensDistance();
+  metrics.left_fov_lrbt[0] = left_fov.GetLeft();
+  metrics.left_fov_lrbt[1] = left_fov.GetRight();
+  metrics.left_fov_lrbt[2] = left_fov.GetBottom();
+  metrics.left_fov_lrbt[3] = left_fov.GetTop();
+  metrics.right_fov_lrbt[0] = right_fov.GetLeft();
+  metrics.right_fov_lrbt[1] = right_fov.GetRight();
+  metrics.right_fov_lrbt[2] = right_fov.GetBottom();
+  metrics.right_fov_lrbt[3] = right_fov.GetTop();
+
+  return metrics;
+}
+
+// Creates a new DisplaySurface and associates it with this channel. This may
+// only be done once per channel.
+int DisplayService::OnCreateSurface(pdx::Message& message, int width,
+                                    int height, int format, int usage,
+                                    DisplaySurfaceFlags flags) {
+  // A surface may only be created once per channel.
+  if (message.GetChannel())
+    return -EINVAL;
+
+  ALOGI_IF(TRACE, "DisplayService::OnCreateSurface: cid=%d",
+           message.GetChannelId());
+
+  // Use the channel id as the unique surface id.
+  const int surface_id = message.GetChannelId();
+  const int process_id = message.GetProcessId();
+
+  ALOGI_IF(TRACE,
+           "DisplayService::OnCreateSurface: surface_id=%d process_id=%d "
+           "width=%d height=%d format=%x usage=%x flags=%x",
+           surface_id, process_id, width, height, format, usage, flags);
+
+  // TODO(eieio,jbates): Validate request parameters.
+  auto channel = std::make_shared<DisplaySurface>(
+      this, surface_id, process_id, width, height, format, usage, flags);
+
+  message.SetChannel(channel);
+  NotifyDisplayConfigurationUpdate();
+  return 0;
+}
+
+DisplayRPC::ByteBuffer DisplayService::OnGetEdsCapture(pdx::Message& message) {
+  Compositor* compositor = hardware_composer_.GetCompositor();
+  if (compositor == nullptr)
+    REPLY_ERROR_RETURN(message, EINVAL, {});
+
+  std::vector<std::uint8_t> buffer(sizeof(LateLatchOutput));
+
+  if (!compositor->GetLastEdsPose(
+          reinterpret_cast<LateLatchOutput*>(buffer.data()))) {
+    REPLY_ERROR_RETURN(message, EPERM, {});
+  }
+
+  return WrapBuffer(std::move(buffer));
+}
+
+int DisplayService::OnEnterVrMode(pdx::Message& /*message*/) {
+  hardware_composer_.Resume();
+  return 0;
+}
+
+int DisplayService::OnExitVrMode(pdx::Message& /*message*/) {
+  hardware_composer_.Suspend();
+  return 0;
+}
+
+void DisplayService::OnSetViewerParams(pdx::Message& message,
+                                       const ViewerParams& view_params) {
+  Compositor* compositor = hardware_composer_.GetCompositor();
+  if (compositor == nullptr)
+    REPLY_ERROR_RETURN(message, EINVAL);
+
+  FieldOfView left(55.0f, 55.0f, 55.0f, 55.0f);
+  FieldOfView right(55.0f, 55.0f, 55.0f, 55.0f);
+  if (view_params.left_eye_field_of_view_angles.size() >= 4) {
+    left = FieldOfView(ToRad(view_params.left_eye_field_of_view_angles[0]),
+                       ToRad(view_params.left_eye_field_of_view_angles[1]),
+                       ToRad(view_params.left_eye_field_of_view_angles[2]),
+                       ToRad(view_params.left_eye_field_of_view_angles[3]));
+    right = FieldOfView(ToRad(view_params.left_eye_field_of_view_angles[1]),
+                        ToRad(view_params.left_eye_field_of_view_angles[0]),
+                        ToRad(view_params.left_eye_field_of_view_angles[2]),
+                        ToRad(view_params.left_eye_field_of_view_angles[3]));
+  }
+
+  std::shared_ptr<ColorChannelDistortion> red_distortion;
+  std::shared_ptr<ColorChannelDistortion> green_distortion;
+  std::shared_ptr<ColorChannelDistortion> blue_distortion;
+
+  // We should always have a red distortion.
+  LOG_FATAL_IF(view_params.distortion_coefficients_r.empty());
+  red_distortion = std::make_shared<PolynomialRadialDistortion>(
+      view_params.distortion_coefficients_r);
+
+  if (!view_params.distortion_coefficients_g.empty()) {
+    green_distortion = std::make_shared<PolynomialRadialDistortion>(
+        view_params.distortion_coefficients_g);
+  }
+
+  if (!view_params.distortion_coefficients_b.empty()) {
+    blue_distortion = std::make_shared<PolynomialRadialDistortion>(
+        view_params.distortion_coefficients_b);
+  }
+
+  HeadMountMetrics::EyeOrientation left_orientation =
+      HeadMountMetrics::EyeOrientation::kCCW0Degrees;
+  HeadMountMetrics::EyeOrientation right_orientation =
+      HeadMountMetrics::EyeOrientation::kCCW0Degrees;
+
+  if (view_params.eye_orientations.size() > 1) {
+    left_orientation = static_cast<HeadMountMetrics::EyeOrientation>(
+        view_params.eye_orientations[0]);
+    right_orientation = static_cast<HeadMountMetrics::EyeOrientation>(
+        view_params.eye_orientations[1]);
+  }
+
+  HeadMountMetrics head_mount_metrics(
+      view_params.inter_lens_distance, view_params.tray_to_lens_distance,
+      view_params.screen_to_lens_distance,
+      static_cast<HeadMountMetrics::VerticalAlignment>(
+          view_params.vertical_alignment),
+      left, right, red_distortion, green_distortion, blue_distortion,
+      left_orientation, right_orientation,
+      view_params.screen_center_to_lens_distance);
+
+  compositor->UpdateHeadMountMetrics(head_mount_metrics);
+}
+
+// Calls the message handler for the DisplaySurface associated with this
+// channel.
+int DisplayService::HandleSurfaceMessage(pdx::Message& message) {
+  auto surface = std::static_pointer_cast<SurfaceChannel>(message.GetChannel());
+  ALOGW_IF(!surface,
+           "DisplayService::HandleSurfaceMessage: surface is nullptr!");
+
+  if (surface)
+    return surface->HandleMessage(message);
+  else
+    REPLY_ERROR_RETURN(message, EINVAL, 0);
+}
+
+std::shared_ptr<DisplaySurface> DisplayService::GetDisplaySurface(
+    int surface_id) const {
+  return std::static_pointer_cast<DisplaySurface>(GetChannel(surface_id));
+}
+
+std::vector<std::shared_ptr<DisplaySurface>>
+DisplayService::GetDisplaySurfaces() const {
+  return GetChannels<DisplaySurface>();
+}
+
+std::vector<std::shared_ptr<DisplaySurface>>
+DisplayService::GetVisibleDisplaySurfaces() const {
+  std::vector<std::shared_ptr<DisplaySurface>> visible_surfaces;
+
+  ForEachDisplaySurface(
+      [&](const std::shared_ptr<DisplaySurface>& surface) mutable {
+        if (surface->IsVisible())
+          visible_surfaces.push_back(surface);
+      });
+
+  return visible_surfaces;
+}
+
+int DisplayService::UpdateActiveDisplaySurfaces() {
+  auto visible_surfaces = GetVisibleDisplaySurfaces();
+
+  // Sort the surfaces based on manager z order first, then client z order.
+  std::sort(visible_surfaces.begin(), visible_surfaces.end(),
+            [](const std::shared_ptr<DisplaySurface>& a,
+               const std::shared_ptr<DisplaySurface>& b) {
+              return a->manager_z_order() != b->manager_z_order()
+                         ? a->manager_z_order() < b->manager_z_order()
+                         : a->client_z_order() < b->client_z_order();
+            });
+
+  ALOGD_IF(TRACE,
+           "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
+           visible_surfaces.size());
+
+  // TODO(jbates) Have the shell manage blurred layers.
+  bool blur_requested = false;
+  auto end = visible_surfaces.crend();
+  for (auto it = visible_surfaces.crbegin(); it != end; ++it) {
+    auto surface = *it;
+    // Surfaces with exclude_from_blur==true are not blurred
+    // and are excluded from blur computation of other layers.
+    if (surface->client_exclude_from_blur()) {
+      surface->ManagerSetBlur(0.0f);
+      continue;
+    }
+    surface->ManagerSetBlur(blur_requested ? 1.0f : 0.0f);
+    if (surface->client_blur_behind())
+      blur_requested = true;
+  }
+  return hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
+}
+
+void DisplayService::OnHardwareComposerRefresh() {
+  hardware_composer_.OnHardwareComposerRefresh();
+}
+
+void DisplayService::SetDisplayConfigurationUpdateNotifier(
+    DisplayConfigurationUpdateNotifier update_notifier) {
+  update_notifier_ = update_notifier;
+}
+
+void DisplayService::NotifyDisplayConfigurationUpdate() {
+  if (update_notifier_)
+    update_notifier_();
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
new file mode 100644
index 0000000..b207e4d
--- /dev/null
+++ b/libs/vr/libvrflinger/display_service.h
@@ -0,0 +1,110 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
+
+#include <pdx/service.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/late_latch.h>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "acquired_buffer.h"
+#include "display_surface.h"
+#include "epoll_event_dispatcher.h"
+#include "hardware_composer.h"
+
+namespace android {
+namespace dvr {
+
+// DisplayService implements the displayd display service over ServiceFS.
+class DisplayService : public pdx::ServiceBase<DisplayService> {
+ public:
+  bool IsInitialized() const override;
+  std::string DumpState(size_t max_length) override;
+
+  void OnChannelClose(pdx::Message& message,
+                      const std::shared_ptr<pdx::Channel>& channel) override;
+  int HandleMessage(pdx::Message& message) override;
+
+  std::shared_ptr<DisplaySurface> GetDisplaySurface(int surface_id) const;
+  std::vector<std::shared_ptr<DisplaySurface>> GetDisplaySurfaces() const;
+  std::vector<std::shared_ptr<DisplaySurface>> GetVisibleDisplaySurfaces()
+      const;
+
+  // Updates the list of actively displayed surfaces. This must be called after
+  // any change to client/manager attributes that affect visibility or z order.
+  int UpdateActiveDisplaySurfaces();
+
+  template <class A>
+  void ForEachDisplaySurface(A action) const {
+    ForEachChannel([action](const ChannelIterator::value_type& pair) mutable {
+      auto surface = std::static_pointer_cast<SurfaceChannel>(pair.second);
+      if (surface->type() == SurfaceTypeEnum::Normal)
+        action(std::static_pointer_cast<DisplaySurface>(surface));
+    });
+  }
+
+  using DisplayConfigurationUpdateNotifier = std::function<void(void)>;
+  void SetDisplayConfigurationUpdateNotifier(
+      DisplayConfigurationUpdateNotifier notifier);
+
+  using VSyncCallback = HardwareComposer::VSyncCallback;
+  void SetVSyncCallback(VSyncCallback callback) {
+    hardware_composer_.SetVSyncCallback(callback);
+  }
+
+  HWCDisplayMetrics GetDisplayMetrics() {
+    return hardware_composer_.display_metrics();
+  }
+
+  void SetActive(bool activated) {
+    if (activated) {
+      hardware_composer_.Resume();
+    } else {
+      hardware_composer_.Suspend();
+    }
+  }
+
+  void OnHardwareComposerRefresh();
+
+ private:
+  friend BASE;
+  friend DisplaySurface;
+
+  friend class VrDisplayStateService;
+
+  DisplayService();
+  DisplayService(android::Hwc2::Composer* hidl);
+
+  SystemDisplayMetrics OnGetMetrics(pdx::Message& message);
+  int OnCreateSurface(pdx::Message& message, int width, int height,
+                      int format, int usage, DisplaySurfaceFlags flags);
+
+  DisplayRPC::ByteBuffer OnGetEdsCapture(pdx::Message& message);
+
+  int OnEnterVrMode(pdx::Message& message);
+  int OnExitVrMode(pdx::Message& message);
+  void OnSetViewerParams(pdx::Message& message, const ViewerParams& view_params);
+
+  // Called by DisplaySurface to signal that a surface property has changed and
+  // the display manager should be notified.
+  void NotifyDisplayConfigurationUpdate();
+
+  int HandleSurfaceMessage(pdx::Message& message);
+
+  DisplayService(const DisplayService&) = delete;
+  void operator=(const DisplayService&) = delete;
+
+  EpollEventDispatcher dispatcher_;
+  HardwareComposer hardware_composer_;
+  DisplayConfigurationUpdateNotifier update_notifier_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
new file mode 100644
index 0000000..7c821bf
--- /dev/null
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -0,0 +1,454 @@
+#include "display_surface.h"
+
+#include <utils/Trace.h>
+
+#include <private/dvr/platform_defines.h>
+
+#include "display_service.h"
+#include "hardware_composer.h"
+
+#define LOCAL_TRACE 1
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::LocalChannelHandle;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::IfAnyOf;
+
+namespace android {
+namespace dvr {
+
+DisplaySurface::DisplaySurface(DisplayService* service, int surface_id,
+                               int process_id, int width, int height,
+                               int format, int usage, int flags)
+    : SurfaceChannel(service, surface_id, SurfaceTypeEnum::Normal,
+                     sizeof(DisplaySurfaceMetadata)),
+      process_id_(process_id),
+      posted_buffers_(kMaxPostedBuffers),
+      video_mesh_surfaces_updated_(false),
+      width_(width),
+      height_(height),
+      format_(format),
+      usage_(usage),
+      flags_(flags),
+      client_visible_(false),
+      client_z_order_(0),
+      client_exclude_from_blur_(false),
+      client_blur_behind_(false),
+      manager_visible_(false),
+      manager_z_order_(0),
+      manager_blur_(0.0f),
+      allocated_buffer_index_(0),
+      layer_order_(0) {}
+
+DisplaySurface::~DisplaySurface() {
+  ALOGD_IF(LOCAL_TRACE,
+           "DisplaySurface::~DisplaySurface: surface_id=%d process_id=%d",
+           surface_id(), process_id_);
+}
+
+void DisplaySurface::ManagerSetVisible(bool visible) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  manager_visible_ = visible;
+}
+
+void DisplaySurface::ManagerSetZOrder(int z_order) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  manager_z_order_ = z_order;
+}
+
+void DisplaySurface::ManagerSetBlur(float blur) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  manager_blur_ = blur;
+}
+
+void DisplaySurface::ClientSetVisible(bool visible) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  client_visible_ = visible;
+}
+
+void DisplaySurface::ClientSetZOrder(int z_order) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  client_z_order_ = z_order;
+}
+
+void DisplaySurface::ClientSetExcludeFromBlur(bool exclude_from_blur) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  client_exclude_from_blur_ = exclude_from_blur;
+}
+
+void DisplaySurface::ClientSetBlurBehind(bool blur_behind) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  client_blur_behind_ = blur_behind;
+}
+
+size_t DisplaySurface::GetBufferCount() const {
+  std::lock_guard<std::mutex> autolock(lock_);
+  return buffers_.size();
+}
+
+std::vector<std::shared_ptr<BufferConsumer>> DisplaySurface::GetBuffers() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  std::vector<std::shared_ptr<BufferConsumer>> return_vector(buffers_.size());
+
+  for (const auto pair : buffers_) {
+    return_vector.push_back(pair.second);
+  }
+
+  return return_vector;
+}
+
+AcquiredBuffer DisplaySurface::AcquireNewestAvailableBuffer(
+    AcquiredBuffer* skipped_buffer) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  AcquiredBuffer buffer;
+  int frames = 0;
+  // Basic latency stopgap for when the application misses a frame:
+  // If the application recovers on the 2nd or 3rd (etc) frame after
+  // missing, this code will skip frames to catch up by checking if
+  // the next frame is also available.
+  while (!posted_buffers_.IsEmpty() && posted_buffers_.Front().IsAvailable()) {
+    // Capture the skipped buffer into the result parameter.
+    // Note that this API only supports skipping one buffer per vsync.
+    if (frames > 0 && skipped_buffer)
+      *skipped_buffer = std::move(buffer);
+    ++frames;
+    buffer = std::move(posted_buffers_.Front());
+    posted_buffers_.PopFront();
+    if (frames == 2)
+      break;
+  }
+  return buffer;
+}
+
+bool DisplaySurface::IsBufferAvailable() const {
+  std::lock_guard<std::mutex> autolock(lock_);
+  return !posted_buffers_.IsEmpty() && posted_buffers_.Front().IsAvailable();
+}
+
+bool DisplaySurface::IsBufferPosted() const {
+  std::lock_guard<std::mutex> autolock(lock_);
+  return !posted_buffers_.IsEmpty();
+}
+
+AcquiredBuffer DisplaySurface::AcquireCurrentBuffer() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  if (posted_buffers_.IsEmpty()) {
+    ALOGE("Error: attempt to acquire buffer when none are posted.");
+    return AcquiredBuffer();
+  }
+  AcquiredBuffer buffer = std::move(posted_buffers_.Front());
+  posted_buffers_.PopFront();
+  return buffer;
+}
+
+int DisplaySurface::GetConsumers(std::vector<LocalChannelHandle>* consumers) {
+  std::lock_guard<std::mutex> autolock(lock_);
+  std::vector<LocalChannelHandle> items;
+
+  for (auto pair : buffers_) {
+    const auto& buffer = pair.second;
+
+    Status<LocalChannelHandle> consumer_channel = buffer->CreateConsumer();
+    if (!consumer_channel) {
+      ALOGE(
+          "DisplaySurface::GetConsumers: Failed to get a new consumer for "
+          "buffer %d: %s",
+          buffer->id(), consumer_channel.GetErrorMessage().c_str());
+      return -consumer_channel.error();
+    }
+
+    items.push_back(consumer_channel.take());
+  }
+
+  *consumers = std::move(items);
+  return 0;
+}
+
+int DisplaySurface::HandleMessage(pdx::Message& message) {
+  switch (message.GetOp()) {
+    case DisplayRPC::SetAttributes::Opcode:
+      DispatchRemoteMethod<DisplayRPC::SetAttributes>(
+          *this, &DisplaySurface::OnClientSetAttributes, message);
+      break;
+
+    case DisplayRPC::AllocateBuffer::Opcode:
+      DispatchRemoteMethod<DisplayRPC::AllocateBuffer>(
+          *this, &DisplaySurface::OnAllocateBuffer, message);
+      break;
+
+    case DisplayRPC::CreateVideoMeshSurface::Opcode:
+      DispatchRemoteMethod<DisplayRPC::CreateVideoMeshSurface>(
+          *this, &DisplaySurface::OnCreateVideoMeshSurface, message);
+      break;
+
+    default:
+      return SurfaceChannel::HandleMessage(message);
+  }
+
+  return 0;
+}
+
+int DisplaySurface::OnClientSetAttributes(
+    pdx::Message& /*message*/, const DisplaySurfaceAttributes& attributes) {
+  for (const auto& attribute : attributes) {
+    const auto& key = attribute.first;
+    const auto* variant = &attribute.second;
+    bool invalid_value = false;
+    switch (key) {
+      case DisplaySurfaceAttributeEnum::ZOrder:
+        invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
+            variant, [this](const auto& value) {
+              DisplaySurface::ClientSetZOrder(value);
+            });
+        break;
+      case DisplaySurfaceAttributeEnum::Visible:
+        invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+            variant, [this](const auto& value) {
+              DisplaySurface::ClientSetVisible(value);
+            });
+        break;
+      case DisplaySurfaceAttributeEnum::ExcludeFromBlur:
+        invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+            variant, [this](const auto& value) {
+              DisplaySurface::ClientSetExcludeFromBlur(value);
+            });
+        break;
+      case DisplaySurfaceAttributeEnum::BlurBehind:
+        invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
+            variant, [this](const auto& value) {
+              DisplaySurface::ClientSetBlurBehind(value);
+            });
+        break;
+      default:
+        ALOGW(
+            "DisplaySurface::OnClientSetAttributes: Unrecognized attribute %d "
+            "surface_id=%d",
+            key, surface_id());
+        break;
+    }
+
+    if (invalid_value) {
+      ALOGW(
+          "DisplaySurface::OnClientSetAttributes: Failed to set display "
+          "surface attribute '%s' because of incompatible type: %d",
+          DisplaySurfaceAttributeEnum::ToString(key).c_str(), variant->index());
+    }
+  }
+
+  service()->NotifyDisplayConfigurationUpdate();
+  return 0;
+}
+
+// Allocates a new buffer for the DisplaySurface associated with this channel.
+std::pair<uint32_t, LocalChannelHandle> DisplaySurface::OnAllocateBuffer(
+    pdx::Message& message) {
+  // Inject flag to enable framebuffer compression for the application buffers.
+  // TODO(eieio,jbates): Make this configurable per hardware platform.
+  const int usage = usage_ | GRALLOC_USAGE_QCOM_FRAMEBUFFER_COMPRESSION;
+  const int slice_count =
+      (flags_ & static_cast<int>(DisplaySurfaceFlagsEnum::SeparateGeometry))
+          ? 2
+          : 1;
+
+  ALOGI_IF(
+      TRACE,
+      "DisplaySurface::OnAllocateBuffer: width=%d height=%d format=%x usage=%x "
+      "slice_count=%d",
+      width_, height_, format_, usage, slice_count);
+
+  // Create a producer buffer to hand back to the sender.
+  auto producer = BufferProducer::Create(width_, height_, format_, usage,
+                                         sizeof(uint64_t), slice_count);
+  if (!producer)
+    REPLY_ERROR_RETURN(message, EINVAL, {});
+
+  // Create and import a consumer attached to the producer.
+  Status<LocalChannelHandle> consumer_channel = producer->CreateConsumer();
+  if (!consumer_channel)
+    REPLY_ERROR_RETURN(message, consumer_channel.error(), {});
+
+  std::shared_ptr<BufferConsumer> consumer =
+      BufferConsumer::Import(consumer_channel.take());
+  if (!consumer)
+    REPLY_ERROR_RETURN(message, ENOMEM, {});
+
+  // Add the consumer to this surface.
+  int err = AddConsumer(consumer);
+  if (err < 0) {
+    ALOGE("DisplaySurface::OnAllocateBuffer: failed to add consumer: buffer=%d",
+          consumer->id());
+    REPLY_ERROR_RETURN(message, -err, {});
+  }
+
+  // Move the channel handle so that it doesn't get closed when the producer
+  // goes out of scope.
+  std::pair<uint32_t, LocalChannelHandle> return_value(
+      allocated_buffer_index_, std::move(producer->GetChannelHandle()));
+
+  // Save buffer index, associated with the buffer id so that it can be looked
+  // up later.
+  buffer_id_to_index_[consumer->id()] = allocated_buffer_index_;
+  ++allocated_buffer_index_;
+
+  return return_value;
+}
+
+RemoteChannelHandle DisplaySurface::OnCreateVideoMeshSurface(
+    pdx::Message& message) {
+  if (flags_ & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION) {
+    ALOGE(
+        "DisplaySurface::OnCreateVideoMeshSurface: system distorion is "
+        "disabled on this display surface, cannot create VideoMeshSurface on "
+        "top of it.");
+    REPLY_ERROR_RETURN(message, EINVAL, {});
+  }
+
+  int channel_id;
+  auto status = message.PushChannel(0, nullptr, &channel_id);
+
+  if (!status) {
+    ALOGE(
+        "DisplaySurface::OnCreateVideoMeshSurface: failed to push channel: %s",
+        status.GetErrorMessage().c_str());
+    REPLY_ERROR_RETURN(message, ENOMEM, {});
+  }
+
+  auto surface = std::make_shared<VideoMeshSurface>(service(), channel_id);
+  const int ret = service()->SetChannel(channel_id, surface);
+  if (ret < 0) {
+    ALOGE(
+        "DisplaySurface::OnCreateVideoMeshSurface: failed to set new video "
+        "mesh surface channel: %s",
+        strerror(-ret));
+    REPLY_ERROR_RETURN(message, ENOMEM, {});
+  }
+
+  {
+    std::lock_guard<std::mutex> autolock(lock_);
+    pending_video_mesh_surfaces_.push_back(surface);
+    video_mesh_surfaces_updated_ = true;
+  }
+
+  return status.take();
+}
+
+int DisplaySurface::AddConsumer(
+    const std::shared_ptr<BufferConsumer>& consumer) {
+  ALOGD_IF(TRACE, "DisplaySurface::AddConsumer: buffer_id=%d", consumer->id());
+  // Add the consumer to the epoll dispatcher, edge-triggered.
+  int err = service()->dispatcher_.AddEventHandler(
+      consumer->event_fd(), EPOLLET | EPOLLIN | EPOLLHUP,
+      std::bind(&DisplaySurface::HandleConsumerEvents,
+                std::static_pointer_cast<DisplaySurface>(shared_from_this()),
+                consumer, std::placeholders::_1));
+  if (err) {
+    ALOGE(
+        "DisplaySurface::AddConsumer: failed to add epoll event handler for "
+        "consumer: %s",
+        strerror(-err));
+    return err;
+  }
+
+  // Add the consumer to the list of buffers for this surface.
+  std::lock_guard<std::mutex> autolock(lock_);
+  buffers_.insert(std::make_pair(consumer->id(), consumer));
+  return 0;
+}
+
+void DisplaySurface::RemoveConsumer(
+    const std::shared_ptr<BufferConsumer>& consumer) {
+  ALOGD_IF(TRACE, "DisplaySurface::RemoveConsumer: buffer_id=%d",
+           consumer->id());
+  service()->dispatcher_.RemoveEventHandler(consumer->event_fd());
+
+  std::lock_guard<std::mutex> autolock(lock_);
+  buffers_.erase(consumer->id());
+}
+
+void DisplaySurface::RemoveConsumerUnlocked(
+    const std::shared_ptr<BufferConsumer>& consumer) {
+  ALOGD_IF(TRACE, "DisplaySurface::RemoveConsumerUnlocked: buffer_id=%d",
+           consumer->id());
+  service()->dispatcher_.RemoveEventHandler(consumer->event_fd());
+  buffers_.erase(consumer->id());
+}
+
+void DisplaySurface::OnPostConsumer(
+    const std::shared_ptr<BufferConsumer>& consumer) {
+  ATRACE_NAME("DisplaySurface::OnPostConsumer");
+  std::lock_guard<std::mutex> autolock(lock_);
+
+  if (!IsVisible()) {
+    ALOGD_IF(TRACE,
+             "DisplaySurface::OnPostConsumer: Discarding buffer_id=%d on "
+             "invisible surface.",
+             consumer->id());
+    consumer->Discard();
+    return;
+  }
+
+  if (posted_buffers_.IsFull()) {
+    ALOGE("Error: posted buffers full, overwriting");
+    posted_buffers_.PopBack();
+  }
+
+  int error;
+  posted_buffers_.Append(AcquiredBuffer(consumer, &error));
+
+  // Remove the consumer if the other end was closed.
+  if (posted_buffers_.Back().IsEmpty() && error == -EPIPE)
+    RemoveConsumerUnlocked(consumer);
+}
+
+void DisplaySurface::HandleConsumerEvents(
+    const std::shared_ptr<BufferConsumer>& consumer, int events) {
+  auto status = consumer->GetEventMask(events);
+  if (!status) {
+    ALOGW(
+        "DisplaySurface::HandleConsumerEvents: Failed to get event mask for "
+        "consumer: %s",
+        status.GetErrorMessage().c_str());
+    return;
+  }
+
+  events = status.get();
+  if (events & EPOLLHUP) {
+    ALOGD_IF(TRACE,
+             "DisplaySurface::HandleConsumerEvents: removing event handler for "
+             "buffer=%d",
+             consumer->id());
+    RemoveConsumer(consumer);
+  } else if (events & EPOLLIN) {
+    // BufferHub uses EPOLLIN to signal consumer ownership.
+    ALOGD_IF(TRACE,
+             "DisplaySurface::HandleConsumerEvents: posting buffer=%d for "
+             "process=%d",
+             consumer->id(), process_id_);
+
+    OnPostConsumer(consumer);
+  }
+}
+
+std::vector<std::shared_ptr<VideoMeshSurface>>
+DisplaySurface::GetVideoMeshSurfaces() {
+  std::lock_guard<std::mutex> autolock(lock_);
+  std::vector<std::shared_ptr<VideoMeshSurface>> surfaces;
+
+  for (auto& surface : pending_video_mesh_surfaces_) {
+    if (auto video_surface = surface.lock()) {
+      surfaces.push_back(video_surface);
+    } else {
+      ALOGE("Unable to lock video mesh surface.");
+    }
+  }
+
+  pending_video_mesh_surfaces_.clear();
+  video_mesh_surfaces_updated_ = false;
+  return surfaces;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
new file mode 100644
index 0000000..b7bcd97
--- /dev/null
+++ b/libs/vr/libvrflinger/display_surface.h
@@ -0,0 +1,211 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
+
+#include <pdx/file_handle.h>
+#include <pdx/service.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/ring_buffer.h>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "acquired_buffer.h"
+#include "epoll_event_dispatcher.h"
+#include "surface_channel.h"
+#include "video_mesh_surface.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayService;
+
+// DisplaySurface is the service-side notion of a client display context. It is
+// responsible for managing display buffer format, geometry, and state, and
+// maintains the buffer consumers connected to the client.
+class DisplaySurface : public SurfaceChannel {
+ public:
+  DisplaySurface(DisplayService* service, int surface_id, int process_id,
+                 int width, int height, int format, int usage, int flags);
+  ~DisplaySurface() override;
+
+  int process_id() const { return process_id_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+  int format() const { return format_; }
+  int usage() const { return usage_; }
+  int flags() const { return flags_; }
+
+  bool client_visible() const { return client_visible_; }
+  int client_z_order() const { return client_z_order_; }
+  bool client_exclude_from_blur() const { return client_exclude_from_blur_; }
+  bool client_blur_behind() const { return client_blur_behind_; }
+
+  bool manager_visible() const { return manager_visible_; }
+  int manager_z_order() const { return manager_z_order_; }
+  float manager_blur() const { return manager_blur_; }
+
+  bool video_mesh_surfaces_updated() const {
+    return video_mesh_surfaces_updated_;
+  }
+
+  volatile const DisplaySurfaceMetadata* GetMetadataBufferPtr() {
+    if (EnsureMetadataBuffer()) {
+      void* addr = nullptr;
+      metadata_buffer_->GetBlobReadWritePointer(metadata_size(), &addr);
+      return static_cast<const volatile DisplaySurfaceMetadata*>(addr);
+    } else {
+      return nullptr;
+    }
+  }
+
+  uint32_t GetRenderBufferIndex(int buffer_id) {
+    return buffer_id_to_index_[buffer_id];
+  }
+
+  size_t GetBufferCount() const;
+  std::vector<std::shared_ptr<BufferConsumer>> GetBuffers();
+
+  // Gets a new set of consumers for all of the surface's buffers. These
+  // consumers are independent from the consumers maintained internally to the
+  // surface and may be passed to other processes over IPC.
+  int GetConsumers(std::vector<pdx::LocalChannelHandle>* consumers);
+
+  template <class A>
+  void ForEachBuffer(A action) {
+    std::lock_guard<std::mutex> autolock(lock_);
+    std::for_each(buffers_.begin(), buffers_.end(), action);
+  }
+
+  bool IsBufferAvailable() const;
+  bool IsBufferPosted() const;
+  AcquiredBuffer AcquireCurrentBuffer();
+
+  // Get the newest buffer. Up to one buffer will be skipped. If a buffer is
+  // skipped, it will be stored in skipped_buffer if non null.
+  AcquiredBuffer AcquireNewestAvailableBuffer(AcquiredBuffer* skipped_buffer);
+
+  // Display manager interface to control visibility and z order.
+  void ManagerSetVisible(bool visible);
+  void ManagerSetZOrder(int z_order);
+  void ManagerSetBlur(float blur);
+
+  // A surface must be set visible by both the client and the display manager to
+  // be visible on screen.
+  bool IsVisible() const { return client_visible_ && manager_visible_; }
+
+  // A surface is blurred if the display manager requests it.
+  bool IsBlurred() const { return manager_blur_ > 0.0f; }
+
+  // Set by HardwareComposer to the current logical layer order of this surface.
+  void SetLayerOrder(int layer_order) { layer_order_ = layer_order; }
+  // Gets the unique z-order index of this surface among other visible surfaces.
+  // This is not the same as the hardware layer index, as not all display
+  // surfaces map directly to hardware layers. Lower layer orders should be
+  // composited underneath higher layer orders.
+  int layer_order() const { return layer_order_; }
+
+  // Lock all video mesh surfaces so that VideoMeshCompositor can access them.
+  std::vector<std::shared_ptr<VideoMeshSurface>> GetVideoMeshSurfaces();
+
+ private:
+  friend class DisplayService;
+
+  // The capacity of the pending buffer queue. Should be enough to hold all the
+  // buffers of this DisplaySurface, although in practice only 1 or 2 frames
+  // will be pending at a time.
+  static constexpr int kMaxPostedBuffers =
+      kSurfaceBufferMaxCount * kSurfaceViewMaxCount;
+
+  // Returns whether a frame is available without locking the mutex.
+  bool IsFrameAvailableNoLock() const;
+
+  // Handles epoll events for BufferHub consumers. Events are mainly generated
+  // by producers posting buffers ready for display. This handler runs on the
+  // epoll event thread.
+  void HandleConsumerEvents(const std::shared_ptr<BufferConsumer>& consumer,
+                            int events);
+
+  // Dispatches display surface messages to the appropriate handlers. This
+  // handler runs on the displayd message dispatch thread.
+  int HandleMessage(pdx::Message& message) override;
+
+  // Sets display surface's client-controlled attributes.
+  int OnClientSetAttributes(pdx::Message& message,
+                            const DisplaySurfaceAttributes& attributes);
+
+  // Allocates a buffer with the display surface geometry and settings and
+  // returns it to the client.
+  std::pair<uint32_t, pdx::LocalChannelHandle> OnAllocateBuffer(
+      pdx::Message& message);
+
+  // Creates a video mesh surface associated with this surface and returns it
+  // to the client.
+  pdx::RemoteChannelHandle OnCreateVideoMeshSurface(pdx::Message& message);
+
+  // Sets the current buffer for the display surface, discarding the previous
+  // buffer if it is not already claimed. Runs on the epoll event thread.
+  void OnPostConsumer(const std::shared_ptr<BufferConsumer>& consumer);
+
+  // Client interface (called through IPC) to set visibility and z order.
+  void ClientSetVisible(bool visible);
+  void ClientSetZOrder(int z_order);
+  void ClientSetExcludeFromBlur(bool exclude_from_blur);
+  void ClientSetBlurBehind(bool blur_behind);
+
+  // Runs on the displayd message dispatch thread.
+  int AddConsumer(const std::shared_ptr<BufferConsumer>& consumer);
+
+  // Runs on the epoll event thread.
+  void RemoveConsumer(const std::shared_ptr<BufferConsumer>& consumer);
+
+  // Runs on the epoll and display post thread.
+  void RemoveConsumerUnlocked(const std::shared_ptr<BufferConsumer>& consumer);
+
+  DisplaySurface(const DisplaySurface&) = delete;
+  void operator=(const DisplaySurface&) = delete;
+
+  int process_id_;
+
+  // Synchronizes access to mutable state below between message dispatch thread,
+  // epoll event thread, and frame post thread.
+  mutable std::mutex lock_;
+  std::unordered_map<int, std::shared_ptr<BufferConsumer>> buffers_;
+
+  // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be
+  // posted and pending.
+  RingBuffer<AcquiredBuffer> posted_buffers_;
+
+  // Provides access to VideoMeshSurface. Here we don't want to increase
+  // the reference count immediately on allocation, will leave it into
+  // compositor's hand.
+  std::vector<std::weak_ptr<VideoMeshSurface>> pending_video_mesh_surfaces_;
+  volatile bool video_mesh_surfaces_updated_;
+
+  // Surface parameters.
+  int width_;
+  int height_;
+  int format_;
+  int usage_;
+  int flags_;
+  bool client_visible_;
+  int client_z_order_;
+  bool client_exclude_from_blur_;
+  bool client_blur_behind_;
+  bool manager_visible_;
+  int manager_z_order_;
+  float manager_blur_;
+  // The monotonically increasing index for allocated buffers in this surface.
+  uint32_t allocated_buffer_index_;
+  int layer_order_;
+
+  // Maps from the buffer id to the corresponding allocated buffer index.
+  std::unordered_map<int, uint32_t> buffer_id_to_index_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
new file mode 100644
index 0000000..b37e76e
--- /dev/null
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
@@ -0,0 +1,142 @@
+#include "epoll_event_dispatcher.h"
+
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/prctl.h>
+
+#include <dvr/performance_client_api.h>
+
+namespace android {
+namespace dvr {
+
+EpollEventDispatcher::EpollEventDispatcher()
+    : exit_thread_(false), epoll_fd_(-1), event_fd_(-1) {
+  epoll_fd_ = epoll_create(64);
+  if (epoll_fd_ < 0) {
+    ALOGE("Failed to create epoll fd: %s", strerror(errno));
+    return;
+  }
+
+  event_fd_ = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+  if (event_fd_ < 0) {
+    ALOGE("Failed to create event for epolling: %s", strerror(errno));
+    return;
+  }
+
+  // Add watch for eventfd. This should only watch for EPOLLIN, which gets set
+  // when eventfd_write occurs. Use "this" as a unique sentinal value to
+  // identify events from the event fd.
+  epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}};
+  if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, event_fd_, &event) < 0) {
+    ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno));
+    return;
+  }
+
+  thread_ = std::thread(&EpollEventDispatcher::EventThread, this);
+}
+
+EpollEventDispatcher::~EpollEventDispatcher() {
+  Stop();
+
+  close(epoll_fd_);
+  close(event_fd_);
+}
+
+void EpollEventDispatcher::Stop() {
+  exit_thread_.store(true);
+  eventfd_write(event_fd_, 1);
+}
+
+int EpollEventDispatcher::AddEventHandler(int fd, int event_mask,
+                                          Handler handler) {
+  std::lock_guard<std::mutex> lock(lock_);
+
+  epoll_event event;
+  event.events = event_mask;
+  event.data.ptr = &(handlers_[fd] = handler);
+
+  ALOGD_IF(
+      TRACE,
+      "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p",
+      fd, event_mask, event.data.ptr);
+
+  int err = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event);
+  return err < 0 ? -errno : 0;
+}
+
+int EpollEventDispatcher::RemoveEventHandler(int fd) {
+  ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
+  std::lock_guard<std::mutex> lock(lock_);
+
+  epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
+  if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &dummy) < 0) {
+    ALOGE("Failed to remove fd from epoll set because: %s", strerror(errno));
+    return -errno;
+  }
+
+  // If the fd was valid above, add it to the list of ids to remove.
+  removed_handlers_.push_back(fd);
+
+  // Wake up the event thread to clean up.
+  eventfd_write(event_fd_, 1);
+
+  return 0;
+}
+
+void EpollEventDispatcher::EventThread() {
+  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("EpollEvent"), 0, 0, 0);
+
+  const int error = dvrSetSchedulerClass(0, "graphics");
+  LOG_ALWAYS_FATAL_IF(
+      error < 0,
+      "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s",
+      strerror(-error));
+
+  const size_t kMaxNumEvents = 128;
+  epoll_event events[kMaxNumEvents];
+
+  while (!exit_thread_.load()) {
+    int num_events = epoll_wait(epoll_fd_, events, kMaxNumEvents, -1);
+    if (num_events < 0 && errno != EINTR)
+      break;
+
+    ALOGD_IF(TRACE, "EpollEventDispatcher::EventThread: num_events=%d",
+             num_events);
+
+    for (int i = 0; i < num_events; i++) {
+      ALOGD_IF(
+          TRACE,
+          "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x",
+          i, events[i].data.ptr, events[i].events);
+
+      if (events[i].data.ptr == this) {
+        // Clear pending event on event_fd_. Serialize the read with respect to
+        // writes from other threads.
+        std::lock_guard<std::mutex> lock(lock_);
+        eventfd_t value;
+        eventfd_read(event_fd_, &value);
+      } else {
+        auto handler = reinterpret_cast<Handler*>(events[i].data.ptr);
+        if (handler)
+          (*handler)(events[i].events);
+      }
+    }
+
+    // Remove any handlers that have been posted for removal. This is done here
+    // instead of in RemoveEventHandler() to prevent races between the dispatch
+    // thread and the code requesting the removal. Handlers are guaranteed to
+    // stay alive between exiting epoll_wait() and the dispatch loop above.
+    std::lock_guard<std::mutex> lock(lock_);
+    for (auto handler_fd : removed_handlers_) {
+      ALOGD_IF(TRACE,
+               "EpollEventDispatcher::EventThread: removing handler: fd=%d",
+               handler_fd);
+      handlers_.erase(handler_fd);
+    }
+    removed_handlers_.clear();
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.h b/libs/vr/libvrflinger/epoll_event_dispatcher.h
new file mode 100644
index 0000000..43bca2e
--- /dev/null
+++ b/libs/vr/libvrflinger/epoll_event_dispatcher.h
@@ -0,0 +1,61 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
+
+#include <sys/epoll.h>
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+class EpollEventDispatcher {
+ public:
+  // Function type for event handlers. The handler receives a bitmask of the
+  // epoll events that occurred on the file descriptor associated with the
+  // handler.
+  using Handler = std::function<void(int)>;
+
+  EpollEventDispatcher();
+  ~EpollEventDispatcher();
+
+  // |handler| is called on the internal dispatch thread when |fd| is signaled
+  // by events in |event_mask|.
+  // Return 0 on success or a negative error code on failure.
+  int AddEventHandler(int fd, int event_mask, Handler handler);
+  int RemoveEventHandler(int fd);
+
+  void Stop();
+
+ private:
+  void EventThread();
+
+  std::thread thread_;
+  std::atomic<bool> exit_thread_;
+
+  // Protects handlers_ and removed_handlers_ and serializes operations on
+  // epoll_fd_ and event_fd_.
+  std::mutex lock_;
+
+  // Maintains a map of fds to event handlers. This is primarily to keep any
+  // references alive that may be bound in the std::function instances. It is
+  // not used at dispatch time to avoid performance problems with different
+  // versions of std::unordered_map.
+  std::unordered_map<int, Handler> handlers_;
+
+  // List of fds to be removed from the map. The actual removal is performed
+  // by the event dispatch thread to avoid races.
+  std::vector<int> removed_handlers_;
+
+  int epoll_fd_;
+  int event_fd_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
new file mode 100644
index 0000000..f801d9b
--- /dev/null
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -0,0 +1,1546 @@
+#include "hardware_composer.h"
+
+#include <log/log.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sync/sync.h>
+#include <sys/eventfd.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/system_properties.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <functional>
+#include <map>
+
+#include <dvr/performance_client_api.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/pose_client_internal.h>
+#include <private/dvr/sync_util.h>
+
+#include "debug_hud_data.h"
+#include "screenshot_service.h"
+
+using android::pdx::LocalHandle;
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// If the number of pending fences goes over this count at the point when we
+// are about to submit a new frame to HWC, we will drop the frame. This should
+// be a signal that the display driver has begun queuing frames. Note that with
+// smart displays (with RAM), the fence is signaled earlier than the next vsync,
+// at the point when the DMA to the display completes. Currently we use a smart
+// display and the EDS timing coincides with zero pending fences, so this is 0.
+constexpr int kAllowedPendingFenceCount = 0;
+
+// If we think we're going to miss vsync by more than this amount, skip the
+// frame.
+constexpr int64_t kFrameSkipThresholdNs = 4000000;  // 4ms
+
+// Counter PostLayers() deficiency by requiring apps to produce a frame at least
+// 2.5ms before vsync. See b/28881672.
+constexpr int64_t kFrameTimeEstimateMin = 2500000;  // 2.5ms
+
+constexpr size_t kDefaultDisplayConfigCount = 32;
+
+constexpr float kMetersPerInch = 0.0254f;
+
+const char kBacklightBrightnessSysFile[] =
+    "/sys/class/leds/lcd-backlight/brightness";
+
+const char kPrimaryDisplayVSyncEventFile[] =
+    "/sys/class/graphics/fb0/vsync_event";
+
+const char kPrimaryDisplayWaitPPEventFile[] = "/sys/class/graphics/fb0/wait_pp";
+
+const char kDvrPerformanceProperty[] = "sys.dvr.performance";
+
+const char kRightEyeOffsetProperty[] = "dreamos.right_eye_offset_ns";
+
+// Returns our best guess for the time the compositor will spend rendering the
+// next frame.
+int64_t GuessFrameTime(int compositor_visible_layer_count) {
+  // The cost of asynchronous EDS and lens warp is currently measured at 2.5ms
+  // for one layer and 7ms for two layers, but guess a higher frame time to
+  // account for CPU overhead. This guess is only used before we've measured the
+  // actual time to render a frame for the current compositor configuration.
+  switch (compositor_visible_layer_count) {
+    case 0:
+      return 500000;  // .5ms
+    case 1:
+      return 5000000;  // 5ms
+    default:
+      return 10500000;  // 10.5ms
+  }
+}
+
+// Get time offset from a vsync to when the pose for that vsync should be
+// predicted out to. For example, if scanout gets halfway through the frame
+// at the halfway point between vsyncs, then this could be half the period.
+// With global shutter displays, this should be changed to the offset to when
+// illumination begins. Low persistence adds a frame of latency, so we predict
+// to the center of the next frame.
+inline int64_t GetPosePredictionTimeOffset(int64_t vsync_period_ns) {
+  return (vsync_period_ns * 150) / 100;
+}
+
+}  // anonymous namespace
+
+HardwareComposer::HardwareComposer()
+  : HardwareComposer(nullptr) {
+}
+
+HardwareComposer::HardwareComposer(Hwc2::Composer* hwc2_hidl)
+    : initialized_(false),
+      hwc2_hidl_(hwc2_hidl),
+      display_transform_(HWC_TRANSFORM_NONE),
+      display_surfaces_updated_(false),
+      hardware_layers_need_update_(false),
+      active_layer_count_(0),
+      gpu_layer_(nullptr),
+      post_thread_state_(PostThreadState::kPaused),
+      terminate_post_thread_event_fd_(-1),
+      backlight_brightness_fd_(-1),
+      primary_display_vsync_event_fd_(-1),
+      primary_display_wait_pp_fd_(-1),
+      vsync_sleep_timer_fd_(-1),
+      last_vsync_timestamp_(0),
+      vsync_count_(0),
+      frame_skip_count_(0),
+      pose_client_(nullptr) {
+  std::transform(layer_storage_.begin(), layer_storage_.end(), layers_.begin(),
+                 [](auto& layer) { return &layer; });
+
+  callbacks_ = new ComposerCallback;
+}
+
+HardwareComposer::~HardwareComposer(void) {
+  Suspend();
+}
+
+bool HardwareComposer::Initialize() {
+  if (initialized_) {
+    ALOGE("HardwareComposer::Initialize: already initialized.");
+    return false;
+  }
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  Hwc2::Config config;
+  ret = (int32_t)hwc2_hidl_->getActiveConfig(HWC_DISPLAY_PRIMARY, &config);
+
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get current display config : %d",
+          config);
+    return false;
+  }
+
+  ret =
+      GetDisplayMetrics(HWC_DISPLAY_PRIMARY, config, &native_display_metrics_);
+
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE(
+        "HardwareComposer: Failed to get display attributes for current "
+        "configuration : %d",
+        ret);
+    return false;
+  }
+
+  ALOGI(
+      "HardwareComposer: primary display attributes: width=%d height=%d "
+      "vsync_period_ns=%d DPI=%dx%d",
+      native_display_metrics_.width, native_display_metrics_.height,
+      native_display_metrics_.vsync_period_ns, native_display_metrics_.dpi.x,
+      native_display_metrics_.dpi.y);
+
+  // Set the display metrics but never use rotation to avoid the long latency of
+  // rotation processing in hwc.
+  display_transform_ = HWC_TRANSFORM_NONE;
+  display_metrics_ = native_display_metrics_;
+
+  initialized_ = true;
+
+  return initialized_;
+}
+
+bool HardwareComposer::Resume() {
+  std::lock_guard<std::mutex> post_thread_lock(post_thread_state_mutex_);
+  if (post_thread_state_ == PostThreadState::kRunning) {
+    return false;
+  }
+
+  std::lock_guard<std::mutex> layer_lock(layer_mutex_);
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  // Always turn off vsync when we start.
+  EnableVsync(false);
+
+  constexpr int format = HAL_PIXEL_FORMAT_RGBA_8888;
+  constexpr int usage =
+      GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER;
+
+  framebuffer_target_ = std::make_shared<IonBuffer>(
+      native_display_metrics_.width, native_display_metrics_.height, format,
+      usage);
+
+  // Associate each Layer instance with a hardware composer layer.
+  for (auto layer : layers_) {
+    layer->Initialize(hwc2_hidl_.get(), &native_display_metrics_);
+  }
+
+#if ENABLE_BACKLIGHT_BRIGHTNESS
+  // TODO(hendrikw): This isn't required at the moment. It's possible that there
+  //                 is another method to access this when needed.
+  // Open the backlight brightness control sysfs node.
+  backlight_brightness_fd_ = LocalHandle(kBacklightBrightnessSysFile, O_RDWR);
+  ALOGW_IF(!backlight_brightness_fd_,
+           "HardwareComposer: Failed to open backlight brightness control: %s",
+           strerror(errno));
+#endif // ENABLE_BACKLIGHT_BRIGHTNESS
+
+  // Open the vsync event node for the primary display.
+  // TODO(eieio): Move this into a platform-specific class.
+  primary_display_vsync_event_fd_ =
+      LocalHandle(kPrimaryDisplayVSyncEventFile, O_RDONLY);
+  ALOGE_IF(!primary_display_vsync_event_fd_,
+           "HardwareComposer: Failed to open vsync event node for primary "
+           "display: %s",
+           strerror(errno));
+
+  // Open the wait pingpong status node for the primary display.
+  // TODO(eieio): Move this into a platform-specific class.
+  primary_display_wait_pp_fd_ =
+      LocalHandle(kPrimaryDisplayWaitPPEventFile, O_RDONLY);
+  ALOGE_IF(
+      !primary_display_wait_pp_fd_,
+      "HardwareComposer: Failed to open wait_pp node for primary display: %s",
+      strerror(errno));
+
+  // Create a timerfd based on CLOCK_MONOTINIC.
+  vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
+  LOG_ALWAYS_FATAL_IF(
+      !vsync_sleep_timer_fd_,
+      "HardwareComposer: Failed to create vsync sleep timerfd: %s",
+      strerror(errno));
+
+  // Connect to pose service.
+  pose_client_ = dvrPoseCreate();
+  ALOGE_IF(!pose_client_, "HardwareComposer: Failed to create pose client");
+
+  terminate_post_thread_event_fd_.Reset(
+      eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+  LOG_ALWAYS_FATAL_IF(
+      !terminate_post_thread_event_fd_,
+      "HardwareComposer: Failed to create terminate PostThread event fd : %s",
+      strerror(errno));
+
+  post_thread_state_ = PostThreadState::kRunning;
+  post_thread_state_cond_var_.notify_all();
+
+  // If get_id() is the default thread::id object, it has not been created yet
+  if (post_thread_.get_id() == std::thread::id()) {
+    post_thread_ = std::thread(&HardwareComposer::PostThread, this);
+  } else {
+    UpdateDisplayState();
+  }
+
+  return true;
+}
+
+bool HardwareComposer::Suspend() {
+  std::unique_lock<std::mutex> post_thread_lock(post_thread_state_mutex_);
+  if (post_thread_state_ == PostThreadState::kPaused) {
+    return false;
+  }
+
+  post_thread_state_ = PostThreadState::kPauseRequested;
+
+  int error = eventfd_write(terminate_post_thread_event_fd_.Get(), 1);
+  ALOGE_IF(error,
+           "HardwareComposer::Suspend: could not write post "
+           "thread termination event fd : %d",
+           error);
+
+  post_thread_state_cond_var_.wait(
+      post_thread_lock,
+      [this] { return post_thread_state_ == PostThreadState::kPaused; });
+  terminate_post_thread_event_fd_.Close();
+
+  // Wait for any pending layer operations to finish
+  std::lock_guard<std::mutex> layer_lock(layer_mutex_);
+
+  EnableVsync(false);
+
+  backlight_brightness_fd_.Close();
+  primary_display_vsync_event_fd_.Close();
+  primary_display_wait_pp_fd_.Close();
+  vsync_sleep_timer_fd_.Close();
+  retire_fence_fds_.clear();
+  gpu_layer_ = nullptr;
+
+  // We have to destroy the layers before we close the hwc device
+  for (size_t i = 0; i < kMaxHardwareLayers; ++i) {
+    layers_[i]->Reset();
+  }
+
+  active_layer_count_ = 0;
+
+  framebuffer_target_.reset();
+
+  //hwc2_hidl_.reset();
+
+  if (pose_client_)
+    dvrPoseDestroy(pose_client_);
+
+  return true;
+}
+
+DisplayMetrics HardwareComposer::GetHmdDisplayMetrics() const {
+  vec2i screen_size(display_metrics_.width, display_metrics_.height);
+  DisplayOrientation orientation =
+      (display_metrics_.width > display_metrics_.height
+           ? DisplayOrientation::kLandscape
+           : DisplayOrientation::kPortrait);
+  float dpi_x = static_cast<float>(display_metrics_.dpi.x) / 1000.0f;
+  float dpi_y = static_cast<float>(display_metrics_.dpi.y) / 1000.0f;
+  float meters_per_pixel_x = kMetersPerInch / dpi_x;
+  float meters_per_pixel_y = kMetersPerInch / dpi_y;
+  vec2 meters_per_pixel(meters_per_pixel_x, meters_per_pixel_y);
+  double frame_duration_s =
+      static_cast<double>(display_metrics_.vsync_period_ns) / 1000000000.0;
+  // TODO(hendrikw): Hard coding to 3mm.  The Pixel is actually 4mm, but it
+  //                 seems that their tray to lens distance is wrong too, which
+  //                 offsets this, at least for the pixel.
+  float border_size = 0.003f;
+  return DisplayMetrics(screen_size, meters_per_pixel, border_size,
+                        static_cast<float>(frame_duration_s), orientation);
+}
+
+int32_t HardwareComposer::Validate(hwc2_display_t display) {
+  uint32_t num_types;
+  uint32_t num_requests;
+  int32_t error =
+      (int32_t)hwc2_hidl_->validateDisplay(display, &num_types, &num_requests);
+
+  if (error == HWC2_ERROR_HAS_CHANGES) {
+    // TODO(skiazyk): We might need to inspect the requested changes first, but
+    // so far it seems like we shouldn't ever hit a bad state.
+    // error = hwc2_funcs_.accept_display_changes_fn_(hardware_composer_device_,
+    //                                               display);
+    error = (int32_t)hwc2_hidl_->acceptDisplayChanges(display);
+  }
+
+  return error;
+}
+
+int32_t HardwareComposer::EnableVsync(bool enabled) {
+  return (int32_t)hwc2_hidl_->setVsyncEnabled(
+      HWC_DISPLAY_PRIMARY,
+      (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
+                                             : HWC2_VSYNC_DISABLE));
+}
+
+int32_t HardwareComposer::Present(hwc2_display_t display) {
+  int32_t present_fence;
+  int32_t error = (int32_t)hwc2_hidl_->presentDisplay(display, &present_fence);
+
+  // According to the documentation, this fence is signaled at the time of
+  // vsync/DMA for physical displays.
+  if (error == HWC2_ERROR_NONE) {
+    ATRACE_INT("HardwareComposer: VsyncFence", present_fence);
+    retire_fence_fds_.emplace_back(present_fence);
+  } else {
+    ATRACE_INT("HardwareComposer: PresentResult", error);
+  }
+
+  return error;
+}
+
+int32_t HardwareComposer::GetDisplayAttribute(hwc2_display_t display,
+                                              hwc2_config_t config,
+                                              hwc2_attribute_t attribute,
+                                              int32_t* out_value) const {
+  return (int32_t)hwc2_hidl_->getDisplayAttribute(
+      display, config, (Hwc2::IComposerClient::Attribute)attribute, out_value);
+}
+
+int32_t HardwareComposer::GetDisplayMetrics(
+    hwc2_display_t display, hwc2_config_t config,
+    HWCDisplayMetrics* out_metrics) const {
+  int32_t ret = HWC2_ERROR_NONE;
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_WIDTH,
+                            &out_metrics->width);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display width");
+    return ret;
+  }
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_HEIGHT,
+                            &out_metrics->height);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display height");
+    return ret;
+  }
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_VSYNC_PERIOD,
+                            &out_metrics->vsync_period_ns);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display height");
+    return ret;
+  }
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_X,
+                            &out_metrics->dpi.x);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display DPI X");
+    return ret;
+  }
+
+  ret = GetDisplayAttribute(display, config, HWC2_ATTRIBUTE_DPI_Y,
+                            &out_metrics->dpi.y);
+  if (ret != HWC2_ERROR_NONE) {
+    ALOGE("HardwareComposer: Failed to get display DPI Y");
+    return ret;
+  }
+
+  return HWC2_ERROR_NONE;
+}
+
+void HardwareComposer::Dump(char* buffer, uint32_t* out_size) {
+  std::string debug_str = hwc2_hidl_->dumpDebugInfo();
+  ALOGI("%s", debug_str.c_str());
+
+  if (buffer == nullptr) {
+    *out_size = debug_str.size();
+  } else {
+    std::copy(debug_str.begin(), debug_str.begin() + *out_size, buffer);
+  }
+}
+
+// TODO(skiazyk): Figure out what to do with `is_geometry_changed`. There does
+// not seem to be any equivalent in the HWC2 API, but that doesn't mean its not
+// there.
+void HardwareComposer::PostLayers(bool /*is_geometry_changed*/) {
+  ATRACE_NAME("HardwareComposer::PostLayers");
+
+  // Setup the hardware composer layers with current buffers.
+  for (size_t i = 0; i < active_layer_count_; i++) {
+    layers_[i]->Prepare();
+  }
+
+  // Now that we have taken in a frame from the application, we have a chance
+  // to drop the frame before passing the frame along to HWC.
+  // If the display driver has become backed up, we detect it here and then
+  // react by skipping this frame to catch up latency.
+  while (!retire_fence_fds_.empty() &&
+         (!retire_fence_fds_.front() ||
+          sync_wait(retire_fence_fds_.front().Get(), 0) == 0)) {
+    // There are only 2 fences in here, no performance problem to shift the
+    // array of ints.
+    retire_fence_fds_.erase(retire_fence_fds_.begin());
+  }
+
+  const bool is_frame_pending = IsFramePendingInDriver();
+  const bool is_fence_pending =
+      retire_fence_fds_.size() > kAllowedPendingFenceCount;
+
+  if (is_fence_pending || is_frame_pending) {
+    ATRACE_INT("frame_skip_count", ++frame_skip_count_);
+
+    ALOGW_IF(is_frame_pending, "Warning: frame already queued, dropping frame");
+    ALOGW_IF(is_fence_pending,
+             "Warning: dropping a frame to catch up with HWC (pending = %zd)",
+             retire_fence_fds_.size());
+
+    for (size_t i = 0; i < active_layer_count_; i++) {
+      layers_[i]->Drop();
+    }
+    return;
+  } else {
+    // Make the transition more obvious in systrace when the frame skip happens
+    // above.
+    ATRACE_INT("frame_skip_count", 0);
+  }
+
+#if TRACE
+  for (size_t i = 0; i < active_layer_count_; i++)
+    ALOGI("HardwareComposer::PostLayers: dl[%zu] ctype=0x%08x", i,
+          layers_[i]->GetCompositionType());
+#endif
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  std::vector<Hwc2::IComposerClient::Rect> full_region(1);
+  full_region[0].left = 0;
+  full_region[0].top = 0;
+  full_region[0].right = framebuffer_target_->width();
+  full_region[0].bottom = framebuffer_target_->height();
+
+  ALOGE_IF(ret, "Error setting client target : %d", ret);
+
+  ret = Validate(HWC_DISPLAY_PRIMARY);
+  if (ret) {
+    ALOGE("HardwareComposer::Validate failed; ret=%d", ret);
+    return;
+  }
+
+  ret = Present(HWC_DISPLAY_PRIMARY);
+  if (ret) {
+    ALOGE("HardwareComposer::Present failed; ret=%d", ret);
+    return;
+  }
+
+  std::vector<Hwc2::Layer> out_layers;
+  std::vector<int> out_fences;
+  ret = (int32_t)hwc2_hidl_->getReleaseFences(HWC_DISPLAY_PRIMARY, &out_layers,
+                                              &out_fences);
+  uint32_t num_elements = out_layers.size();
+
+  ALOGE_IF(ret, "HardwareComposer: GetReleaseFences failed; ret=%d", ret);
+
+  // Perform post-frame bookkeeping. Unused layers are a no-op.
+  for (size_t i = 0; i < num_elements; ++i) {
+    for (size_t j = 0; j < active_layer_count_; ++j) {
+      if (layers_[j]->GetLayerHandle() == out_layers[i]) {
+        layers_[j]->Finish(out_fences[i]);
+      }
+    }
+  }
+}
+
+// TODO(skiazyk): This is a work-around for the fact that we currently do not
+// handle the case when new surfaces are introduced when displayd is not
+// in an active state. A proper-solution will require re-structuring
+// displayd a little, but hopefully this is sufficient for now.
+// For example, could this be handled in |UpdateLayerSettings| instead?
+void HardwareComposer::UpdateDisplayState() {
+  const bool has_display_surfaces = display_surfaces_.size() > 0;
+
+  if (has_display_surfaces) {
+    EnableVsync(true);
+  }
+
+  // TODO(skiazyk): We need to do something about accessing this directly,
+  // supposedly there is a backlight service on the way.
+  SetBacklightBrightness(255);
+
+  // Trigger target-specific performance mode change.
+  property_set(kDvrPerformanceProperty, has_display_surfaces ? "performance" : "idle");
+}
+
+int HardwareComposer::SetDisplaySurfaces(
+    std::vector<std::shared_ptr<DisplaySurface>> surfaces) {
+  // The double lock is necessary because we access both the display surfaces
+  // and post_thread_state_.
+  std::lock_guard<std::mutex> post_thread_state_lock(post_thread_state_mutex_);
+  std::lock_guard<std::mutex> layer_lock(layer_mutex_);
+
+  ALOGI("HardwareComposer::SetDisplaySurfaces: surface count=%zd",
+        surfaces.size());
+
+  // Figure out whether we need to update hardware layers. If this surface
+  // change does not add or remove hardware layers we can avoid display hiccups
+  // by gracefully updating only the GPU compositor layers.
+  // hardware_layers_need_update_ is reset to false by the Post thread.
+  int old_gpu_layer_count = 0;
+  int new_gpu_layer_count = 0;
+  // Look for new hardware layers and count new GPU layers.
+  for (const auto& surface : surfaces) {
+    if (!(surface->flags() &
+          DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION))
+      ++new_gpu_layer_count;
+    else if (std::find(display_surfaces_.begin(), display_surfaces_.end(),
+                       surface) == display_surfaces_.end())
+      // This is a new hardware layer, we need to update.
+      hardware_layers_need_update_ = true;
+  }
+  // Look for deleted hardware layers or compositor layers.
+  for (const auto& surface : display_surfaces_) {
+    if (!(surface->flags() &
+          DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION))
+      ++old_gpu_layer_count;
+    else if (std::find(surfaces.begin(), surfaces.end(), surface) ==
+             surfaces.end())
+      // This is a deleted hardware layer, we need to update.
+      hardware_layers_need_update_ = true;
+  }
+  // Check for compositor hardware layer transition.
+  if ((!old_gpu_layer_count && new_gpu_layer_count) ||
+      (old_gpu_layer_count && !new_gpu_layer_count))
+    hardware_layers_need_update_ = true;
+
+  display_surfaces_ = std::move(surfaces);
+  display_surfaces_updated_ = true;
+
+  // Set the chosen layer order for all surfaces.
+  for (size_t i = 0; i < display_surfaces_.size(); ++i) {
+    display_surfaces_[i]->SetLayerOrder(static_cast<int>(i));
+  }
+
+  // TODO(skiazyk): fix this so that it is handled seamlessly with dormant/non-
+  // dormant state.
+  if (post_thread_state_ == PostThreadState::kRunning) {
+    UpdateDisplayState();
+  }
+
+  return 0;
+}
+
+// Reads the value of the display driver wait_pingpong state. Returns 0 or 1
+// (the value of the state) on success or a negative error otherwise.
+// TODO(eieio): This is pretty driver specific, this should be moved to a
+// separate class eventually.
+int HardwareComposer::ReadWaitPPState() {
+  // Gracefully handle when the kernel does not support this feature.
+  if (!primary_display_wait_pp_fd_)
+    return 0;
+
+  const int wait_pp_fd = primary_display_wait_pp_fd_.Get();
+  int ret, error;
+
+  ret = lseek(wait_pp_fd, 0, SEEK_SET);
+  if (ret < 0) {
+    error = errno;
+    ALOGE("HardwareComposer::ReadWaitPPState: Failed to seek wait_pp fd: %s",
+          strerror(error));
+    return -error;
+  }
+
+  char data = -1;
+  ret = read(wait_pp_fd, &data, sizeof(data));
+  if (ret < 0) {
+    error = errno;
+    ALOGE("HardwareComposer::ReadWaitPPState: Failed to read wait_pp state: %s",
+          strerror(error));
+    return -error;
+  }
+
+  switch (data) {
+    case '0':
+      return 0;
+    case '1':
+      return 1;
+    default:
+      ALOGE(
+          "HardwareComposer::ReadWaitPPState: Unexpected value for wait_pp: %d",
+          data);
+      return -EINVAL;
+  }
+}
+
+// Reads the timestamp of the last vsync from the display driver.
+// TODO(eieio): This is pretty driver specific, this should be moved to a
+// separate class eventually.
+int HardwareComposer::ReadVSyncTimestamp(int64_t* timestamp) {
+  const int event_fd = primary_display_vsync_event_fd_.Get();
+  int ret, error;
+
+  // The driver returns data in the form "VSYNC=<timestamp ns>".
+  std::array<char, 32> data;
+  data.fill('\0');
+
+  // Seek back to the beginning of the event file.
+  ret = lseek(event_fd, 0, SEEK_SET);
+  if (ret < 0) {
+    error = errno;
+    ALOGE(
+        "HardwareComposer::ReadVSyncTimestamp: Failed to seek vsync event fd: "
+        "%s",
+        strerror(error));
+    return -error;
+  }
+
+  // Read the vsync event timestamp.
+  ret = read(event_fd, data.data(), data.size());
+  if (ret < 0) {
+    error = errno;
+    ALOGE_IF(
+        error != EAGAIN,
+        "HardwareComposer::ReadVSyncTimestamp: Error while reading timestamp: "
+        "%s",
+        strerror(error));
+    return -error;
+  }
+
+  ret = sscanf(data.data(), "VSYNC=%" PRIu64,
+               reinterpret_cast<uint64_t*>(timestamp));
+  if (ret < 0) {
+    error = errno;
+    ALOGE(
+        "HardwareComposer::ReadVSyncTimestamp: Error while parsing timestamp: "
+        "%s",
+        strerror(error));
+    return -error;
+  }
+
+  return 0;
+}
+
+// Blocks until the next vsync event is signaled by the display driver.
+// TODO(eieio): This is pretty driver specific, this should be moved to a
+// separate class eventually.
+int HardwareComposer::BlockUntilVSync(/*out*/ bool* suspend_requested) {
+  *suspend_requested = false;
+  const int event_fd = primary_display_vsync_event_fd_.Get();
+  pollfd pfd[2] = {
+      {
+          .fd = event_fd, .events = POLLPRI, .revents = 0,
+      },
+      // This extra event fd is to ensure that we can break out of this loop to
+      // pause the thread even when vsync is disabled, and thus no events on the
+      // vsync fd are being generated.
+      {
+          .fd = terminate_post_thread_event_fd_.Get(),
+          .events = POLLPRI | POLLIN,
+          .revents = 0,
+      },
+  };
+  int ret, error;
+  do {
+    ret = poll(pfd, 2, -1);
+    error = errno;
+    ALOGW_IF(ret < 0,
+             "HardwareComposer::BlockUntilVSync: Error while waiting for vsync "
+             "event: %s (%d)",
+             strerror(error), error);
+  } while (ret < 0 && error == EINTR);
+
+  if (ret >= 0 && pfd[1].revents != 0)
+    *suspend_requested = true;
+  return ret < 0 ? -error : 0;
+}
+
+// Waits for the next vsync and returns the timestamp of the vsync event. If
+// vsync already passed since the last call, returns the latest vsync timestamp
+// instead of blocking. This method updates the last_vsync_timeout_ in the
+// process.
+//
+// TODO(eieio): This is pretty driver specific, this should be moved to a
+// separate class eventually.
+int HardwareComposer::WaitForVSync(int64_t* timestamp) {
+  int error;
+
+  // Get the current timestamp and decide what to do.
+  while (true) {
+    int64_t current_vsync_timestamp;
+    error = ReadVSyncTimestamp(&current_vsync_timestamp);
+    if (error < 0 && error != -EAGAIN)
+      return error;
+
+    if (error == -EAGAIN) {
+      // Vsync was turned off, wait for the next vsync event.
+      bool suspend_requested = false;
+      error = BlockUntilVSync(&suspend_requested);
+      if (error < 0 || suspend_requested)
+        return error;
+
+      // Try again to get the timestamp for this new vsync interval.
+      continue;
+    }
+
+    // Check that we advanced to a later vsync interval.
+    if (TimestampGT(current_vsync_timestamp, last_vsync_timestamp_)) {
+      *timestamp = last_vsync_timestamp_ = current_vsync_timestamp;
+      return 0;
+    }
+
+    // See how close we are to the next expected vsync. If we're within 1ms,
+    // sleep for 1ms and try again.
+    const int64_t ns_per_frame = display_metrics_.vsync_period_ns;
+    const int64_t threshold_ns = 1000000;
+
+    const int64_t next_vsync_est = last_vsync_timestamp_ + ns_per_frame;
+    const int64_t distance_to_vsync_est = next_vsync_est - GetSystemClockNs();
+
+    if (distance_to_vsync_est > threshold_ns) {
+      // Wait for vsync event notification.
+      bool suspend_requested = false;
+      error = BlockUntilVSync(&suspend_requested);
+      if (error < 0 || suspend_requested)
+        return error;
+    } else {
+      // Sleep for a short time before retrying.
+      std::this_thread::sleep_for(std::chrono::milliseconds(1));
+    }
+  }
+}
+
+int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) {
+  const int timer_fd = vsync_sleep_timer_fd_.Get();
+  const itimerspec wakeup_itimerspec = {
+      .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+      .it_value = NsToTimespec(wakeup_timestamp),
+  };
+  int ret =
+      timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &wakeup_itimerspec, nullptr);
+  int error = errno;
+  if (ret < 0) {
+    ALOGE("HardwareComposer::SleepUntil: Failed to set timerfd: %s",
+          strerror(error));
+    return -error;
+  }
+
+  // Wait for the timer by reading the expiration count.
+  uint64_t expiration_count;
+  ret = read(timer_fd, &expiration_count, sizeof(expiration_count));
+  if (ret < 0) {
+    ALOGE("HardwareComposer::SleepUntil: Failed to wait for timerfd: %s",
+          strerror(error));
+    return -error;
+  }
+
+  return 0;
+}
+
+void HardwareComposer::PostThread() {
+  // NOLINTNEXTLINE(runtime/int)
+  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("PostThread"), 0, 0, 0);
+
+  // Set the scheduler to SCHED_FIFO with high priority.
+  int error = dvrSetSchedulerClass(0, "graphics:high");
+  LOG_ALWAYS_FATAL_IF(
+      error < 0,
+      "HardwareComposer::PostThread: Failed to set scheduler class: %s",
+      strerror(-error));
+  error = dvrSetCpuPartition(0, "/system/performance");
+  LOG_ALWAYS_FATAL_IF(
+      error < 0,
+      "HardwareComposer::PostThread: Failed to set cpu partition: %s",
+      strerror(-error));
+
+  // Force the layers to be setup at least once.
+  display_surfaces_updated_ = true;
+
+  // Initialize the GPU compositor.
+  LOG_ALWAYS_FATAL_IF(!compositor_.Initialize(GetHmdDisplayMetrics()),
+                      "Failed to initialize the compositor");
+
+  const int64_t ns_per_frame = display_metrics_.vsync_period_ns;
+  const int64_t photon_offset_ns = GetPosePredictionTimeOffset(ns_per_frame);
+
+  // TODO(jbates) Query vblank time from device, when such an API is available.
+  // This value (6.3%) was measured on A00 in low persistence mode.
+  int64_t vblank_ns = ns_per_frame * 63 / 1000;
+  int64_t right_eye_photon_offset_ns = (ns_per_frame - vblank_ns) / 2;
+
+  // Check property for overriding right eye offset value.
+  right_eye_photon_offset_ns =
+      property_get_int64(kRightEyeOffsetProperty, right_eye_photon_offset_ns);
+
+  // The list of surfaces the compositor should attempt to render. This is set
+  // at the start of each frame.
+  std::vector<std::shared_ptr<DisplaySurface>> compositor_surfaces;
+  compositor_surfaces.reserve(2);
+
+  // Our history of frame times. This is used to get a better estimate of how
+  // long the next frame will take, to set a schedule for EDS.
+  FrameTimeHistory frame_time_history;
+
+  // The backlog is used to allow us to start rendering the next frame before
+  // the previous frame has finished, and still get an accurate measurement of
+  // frame duration.
+  std::vector<FrameTimeMeasurementRecord> frame_time_backlog;
+  constexpr int kFrameTimeBacklogMax = 2;
+  frame_time_backlog.reserve(kFrameTimeBacklogMax);
+
+  // Storage for retrieving fence info.
+  FenceInfoBuffer fence_info_buffer;
+
+  while (1) {
+    ATRACE_NAME("HardwareComposer::PostThread");
+
+    {
+      std::unique_lock<std::mutex> post_thread_lock(post_thread_state_mutex_);
+      if (post_thread_state_ == PostThreadState::kPauseRequested) {
+        ALOGI("HardwareComposer::PostThread: Post thread pause requested.");
+        post_thread_state_ = PostThreadState::kPaused;
+        post_thread_state_cond_var_.notify_all();
+        post_thread_state_cond_var_.wait(
+            post_thread_lock,
+            [this] { return post_thread_state_ == PostThreadState::kRunning; });
+        // The layers will need to be updated since they were deleted previously
+        display_surfaces_updated_ = true;
+        hardware_layers_need_update_ = true;
+      }
+    }
+
+    int64_t vsync_timestamp = 0;
+    {
+      std::array<char, 128> buf;
+      snprintf(buf.data(), buf.size(), "wait_vsync|vsync=%d|",
+               vsync_count_ + 1);
+      ATRACE_NAME(buf.data());
+
+      error = WaitForVSync(&vsync_timestamp);
+      ALOGE_IF(
+          error < 0,
+          "HardwareComposer::PostThread: Failed to wait for vsync event: %s",
+          strerror(-error));
+
+      // Don't bother processing this frame if a pause was requested
+      std::lock_guard<std::mutex> post_thread_lock(post_thread_state_mutex_);
+      if (post_thread_state_ == PostThreadState::kPauseRequested) {
+        continue;
+      }
+    }
+
+    ++vsync_count_;
+
+    static double last_print_time = -1;
+    double current_time = GetSystemClockSec();
+    if (last_print_time < 0 || current_time - last_print_time > 3) {
+      last_print_time = current_time;
+    }
+
+    if (pose_client_) {
+      // Signal the pose service with vsync info.
+      // Display timestamp is in the middle of scanout.
+      privateDvrPoseNotifyVsync(pose_client_, vsync_count_,
+                                vsync_timestamp + photon_offset_ns,
+                                ns_per_frame, right_eye_photon_offset_ns);
+    }
+
+    bool layer_config_changed = UpdateLayerConfig(&compositor_surfaces);
+
+    if (layer_config_changed) {
+      frame_time_history.ResetWithSeed(
+          GuessFrameTime(compositor_surfaces.size()));
+      frame_time_backlog.clear();
+    } else {
+      UpdateFrameTimeHistory(&frame_time_backlog, kFrameTimeBacklogMax,
+                             &fence_info_buffer, &frame_time_history);
+    }
+
+    // Get our current best estimate at how long the next frame will take to
+    // render, based on how long previous frames took to render. Use this
+    // estimate to decide when to wake up for EDS.
+    int64_t frame_time_estimate =
+        frame_time_history.GetSampleCount() == 0
+            ? GuessFrameTime(compositor_surfaces.size())
+            : frame_time_history.GetAverage();
+    frame_time_estimate = std::max(frame_time_estimate, kFrameTimeEstimateMin);
+    DebugHudData::data.hwc_latency = frame_time_estimate;
+
+    // Signal all of the vsync clients. Because absolute time is used for the
+    // wakeup time below, this can take a little time if necessary.
+    if (vsync_callback_)
+      vsync_callback_(HWC_DISPLAY_PRIMARY, vsync_timestamp, frame_time_estimate,
+                      vsync_count_);
+
+    {
+      // Sleep until async EDS wakeup time.
+      ATRACE_NAME("sleep");
+
+      int64_t display_time_est = vsync_timestamp + ns_per_frame;
+      int64_t now = GetSystemClockNs();
+      int64_t frame_finish_time_est = now + frame_time_estimate;
+      int64_t sleep_time_ns = display_time_est - now - frame_time_estimate;
+
+      ATRACE_INT64("sleep_time_ns", sleep_time_ns);
+      if (frame_finish_time_est - display_time_est >= kFrameSkipThresholdNs) {
+        ATRACE_INT("frame_skip_count", ++frame_skip_count_);
+        ALOGE(
+            "HardwareComposer::PostThread: Missed frame schedule, drop "
+            "frame. Expected frame miss: %.1fms",
+            static_cast<double>(frame_finish_time_est - display_time_est) /
+                1000000);
+
+        // There are several reasons we might skip a frame, but one possibility
+        // is we mispredicted the frame time. Clear out the frame time history.
+        frame_time_history.ResetWithSeed(
+            GuessFrameTime(compositor_surfaces.size()));
+        frame_time_backlog.clear();
+        DebugHudData::data.hwc_frame_stats.SkipFrame();
+
+        continue;
+      } else {
+        // Make the transition more obvious in systrace when the frame skip
+        // happens above.
+        ATRACE_INT("frame_skip_count", 0);
+      }
+
+      if (sleep_time_ns > 0) {
+        error = SleepUntil(display_time_est - frame_time_estimate);
+        ALOGE_IF(error < 0, "HardwareComposer::PostThread: Failed to sleep: %s",
+                 strerror(-error));
+      }
+    }
+
+    DebugHudData::data.hwc_frame_stats.AddFrame();
+
+    int64_t frame_start_time = GetSystemClockNs();
+
+    // Setup the output buffer for the compositor. This needs to happen before
+    // you draw with the compositor.
+    if (gpu_layer_ != nullptr) {
+      gpu_layer_->UpdateDirectBuffer(compositor_.GetBuffer());
+    }
+
+    // Call PostLayers now before performing the GL code for the compositor to
+    // avoid missing the deadline that can cause the lower-level hwc to get
+    // permanently backed up.
+    PostLayers(layer_config_changed);
+
+    PostCompositorBuffers(compositor_surfaces);
+
+    if (gpu_layer_ != nullptr) {
+      // Note, with scanline racing, this draw is timed along with the post
+      // layers to finish just in time.
+      LocalHandle frame_fence_fd;
+      compositor_.DrawFrame(vsync_count_ + 1, &frame_fence_fd);
+      if (frame_fence_fd) {
+        LOG_ALWAYS_FATAL_IF(frame_time_backlog.size() >= kFrameTimeBacklogMax,
+                            "Frame time backlog exceeds capacity");
+        frame_time_backlog.push_back(
+            {frame_start_time, std::move(frame_fence_fd)});
+      }
+    } else if (!layer_config_changed) {
+      frame_time_history.AddSample(GetSystemClockNs() - frame_start_time);
+    }
+
+    HandlePendingScreenshots();
+  }
+
+  // TODO(skiazyk): Currently the compositor is not fully releasing its EGL
+  // context, which seems to prevent the thread from exiting properly.
+  // This shouldn't be too hard to address, I just don't have time right now.
+  compositor_.Shutdown();
+}
+
+bool HardwareComposer::UpdateLayerConfig(
+    std::vector<std::shared_ptr<DisplaySurface>>* compositor_surfaces) {
+  std::lock_guard<std::mutex> layer_lock(layer_mutex_);
+
+  if (!display_surfaces_updated_)
+    return false;
+
+  display_surfaces_updated_ = false;
+  DebugHudData::data.ResetLayers();
+
+  // Update compositor layers.
+  {
+    ATRACE_NAME("UpdateLayerConfig_GpuLayers");
+    compositor_.UpdateSurfaces(display_surfaces_);
+    compositor_surfaces->clear();
+    for (size_t i = 0; i < display_surfaces_.size(); ++i) {
+      const auto& surface = display_surfaces_[i];
+      if (!(surface->flags() &
+            DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION)) {
+        compositor_surfaces->push_back(surface);
+      }
+    }
+  }
+
+  if (!hardware_layers_need_update_)
+    return true;
+
+  // Update hardware layers.
+
+  ATRACE_NAME("UpdateLayerConfig_HwLayers");
+  hardware_layers_need_update_ = false;
+
+  // Update the display layers in a non-destructive fashion.
+
+  // Create a map from surface id to hardware layer
+  std::map<int, Layer*> display_surface_layers;
+
+  for (size_t i = 0; i < active_layer_count_; ++i) {
+    auto layer = layers_[i];
+    int surface_id = layer->GetSurfaceId();
+
+    auto found =
+        std::find_if(display_surfaces_.begin(), display_surfaces_.end(),
+                     [surface_id](const auto& surface) {
+                       return surface->surface_id() == surface_id;
+                     });
+
+    if (found != display_surfaces_.end()) {
+      display_surface_layers[surface_id] = layer;
+    }
+  }
+
+  bool has_gpu_layer = std::any_of(
+      display_surfaces_.begin(), display_surfaces_.end(),
+      [](const auto& surface) {
+        return !(surface->flags() &
+                 DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION);
+      });
+
+  if (!has_gpu_layer) {
+    gpu_layer_ = nullptr;
+  }
+
+  auto is_layer_active = [&display_surface_layers, has_gpu_layer](auto layer) {
+    int surface_id = layer->GetSurfaceId();
+    if (surface_id >= 0) {
+      return display_surface_layers.count(surface_id) > 0;
+    } else {
+      return has_gpu_layer;
+    }
+  };
+
+  // Compress the in-use layers to the top of the list
+  auto part = std::partition(
+      layers_.begin(), layers_.begin() + active_layer_count_, is_layer_active);
+
+  size_t new_active_layer_count = part - layers_.begin();
+
+  // Clear any unused layers
+  for (size_t i = new_active_layer_count; i < active_layer_count_; ++i) {
+    layers_[i]->Reset();
+  }
+
+  active_layer_count_ = new_active_layer_count;
+
+  bool gpu_layer_applied = false;
+
+  // Create/update all of the hardware layers
+  for (size_t i = 0; i < display_surfaces_.size(); ++i) {
+    const auto& surface = display_surfaces_[i];
+    bool is_hw_surface =
+        surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_DISABLE_SYSTEM_DISTORTION;
+    hwc2_blend_mode_t blending =
+        i == 0 ? HWC2_BLEND_MODE_NONE : HWC2_BLEND_MODE_COVERAGE;
+
+    DebugHudData::data.SetLayerInfo(
+        i, surface->width(), surface->height(),
+        !!(surface->flags() & DVR_DISPLAY_SURFACE_FLAGS_GEOMETRY_SEPARATE_2));
+
+    if (!is_hw_surface && gpu_layer_applied) {
+      continue;
+    }
+
+    Layer* target_layer;
+    bool existing_layer = false;
+
+    if (is_hw_surface) {
+      auto it = display_surface_layers.find(surface->surface_id());
+
+      if (it != display_surface_layers.end()) {
+        target_layer = it->second;
+        existing_layer = true;
+      }
+    } else if (gpu_layer_ != nullptr) {
+      target_layer = gpu_layer_;
+      existing_layer = true;
+    }
+
+    if (!existing_layer) {
+      if (active_layer_count_ >= kMaxHardwareLayers) {
+        ALOGI("HardwareComposer: More than %d hardware layers requested.",
+              kMaxHardwareLayers);
+        break;
+      } else {
+        target_layer = layers_[active_layer_count_];
+        ++active_layer_count_;
+      }
+
+      ALOGD_IF(TRACE,
+               "HardwareComposer::UpdateLayerConfig: (new) surface_id=%d -> "
+               "layer=%zd",
+               surface->surface_id(), i);
+
+      if (is_hw_surface) {
+        target_layer->Setup(surface, blending, display_transform_,
+                            HWC2_COMPOSITION_DEVICE, i);
+      } else {
+        gpu_layer_ = target_layer;
+        target_layer->Setup(compositor_.GetBuffer(), blending,
+                            display_transform_, HWC2_COMPOSITION_DEVICE, i);
+      }
+    } else {
+      ALOGD_IF(TRACE,
+               "HardwareComposer::UpdateLayerConfig: (retained) surface_id=%d "
+               "-> layer=%zd",
+               surface->surface_id(), i);
+
+      target_layer->SetBlending(blending);
+      target_layer->SetZOrderIndex(i);
+      target_layer->UpdateLayerSettings();
+    }
+
+    gpu_layer_applied = !is_hw_surface;
+  }
+
+  ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers",
+           active_layer_count_);
+
+  return true;
+}
+
+void HardwareComposer::PostCompositorBuffers(
+    const std::vector<std::shared_ptr<DisplaySurface>>& compositor_surfaces) {
+  ATRACE_NAME("PostCompositorBuffers");
+  for (const auto& surface : compositor_surfaces) {
+    compositor_.PostBuffer(surface);
+  }
+}
+
+void HardwareComposer::UpdateFrameTimeHistory(
+    std::vector<FrameTimeMeasurementRecord>* backlog, int backlog_max,
+    FenceInfoBuffer* fence_info_buffer, FrameTimeHistory* history) {
+  while (!backlog->empty()) {
+    const auto& frame_time_record = backlog->front();
+    int64_t end_time = 0;
+    bool frame_finished = CheckFrameFinished(frame_time_record.fence.Get(),
+                                             fence_info_buffer, &end_time);
+    if (frame_finished) {
+      int64_t frame_duration = end_time - frame_time_record.start_time;
+      history->AddSample(frame_duration);
+      // Our backlog is tiny (2 elements), so erasing from the front is ok
+      backlog->erase(backlog->begin());
+    } else {
+      break;
+    }
+  }
+
+  if (backlog->size() == static_cast<size_t>(backlog_max)) {
+    // Yikes, something must've gone wrong if our oldest frame hasn't finished
+    // yet. Give up on waiting for it.
+    const auto& stale_frame_time_record = backlog->front();
+    int64_t frame_duration =
+        GetSystemClockNs() - stale_frame_time_record.start_time;
+    backlog->erase(backlog->begin());
+    history->AddSample(frame_duration);
+    ALOGW("Frame didn't finish after %.1fms",
+          static_cast<double>(frame_duration) / 1000000);
+  }
+}
+
+bool HardwareComposer::CheckFrameFinished(int frame_fence_fd,
+                                          FenceInfoBuffer* fence_info_buffer,
+                                          int64_t* timestamp) {
+  int result = -1;
+  int sync_result = sync_wait(frame_fence_fd, 0);
+  if (sync_result == 0) {
+    result =
+        GetFenceSignaledTimestamp(frame_fence_fd, fence_info_buffer, timestamp);
+    if (result < 0) {
+      ALOGE("Failed getting signaled timestamp from fence");
+    }
+  } else if (errno != ETIME) {
+    ALOGE("sync_wait on frame fence failed");
+  }
+  return result >= 0;
+}
+
+void HardwareComposer::HandlePendingScreenshots() {
+  // Take a screenshot of the requested layer, if available.
+  // TODO(eieio): Look into using virtual displays to composite the layer stack
+  // into a single output buffer that can be returned to the screenshot clients.
+  if (active_layer_count_ > 0) {
+    if (auto screenshot_service = ScreenshotService::GetInstance()) {
+      if (screenshot_service->IsScreenshotRequestPending()) {
+        ATRACE_NAME("screenshot");
+        screenshot_service->TakeIfNeeded(layers_, compositor_);
+      }
+    } else {
+      ALOGW(
+          "HardwareComposer::HandlePendingScreenshots: Failed to get "
+          "screenshot service!");
+    }
+  }
+}
+
+void HardwareComposer::SetVSyncCallback(VSyncCallback callback) {
+  vsync_callback_ = callback;
+}
+
+void HardwareComposer::HwcRefresh(hwc2_callback_data_t /*data*/,
+                                  hwc2_display_t /*display*/) {
+  // TODO(eieio): implement invalidate callbacks.
+}
+
+void HardwareComposer::HwcVSync(hwc2_callback_data_t /*data*/,
+                                hwc2_display_t /*display*/,
+                                int64_t /*timestamp*/) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+  // Intentionally empty. HWC may require a callback to be set to enable vsync
+  // signals. We bypass this callback thread by monitoring the vsync event
+  // directly, but signals still need to be enabled.
+}
+
+void HardwareComposer::HwcHotplug(hwc2_callback_data_t /*callbackData*/,
+                                  hwc2_display_t /*display*/,
+                                  hwc2_connection_t /*connected*/) {
+  // TODO(eieio): implement display hotplug callbacks.
+}
+
+void HardwareComposer::OnHardwareComposerRefresh() {
+  // TODO(steventhomas): Handle refresh.
+}
+
+void HardwareComposer::SetBacklightBrightness(int brightness) {
+  if (backlight_brightness_fd_) {
+    std::array<char, 32> text;
+    const int length = snprintf(text.data(), text.size(), "%d", brightness);
+    write(backlight_brightness_fd_.Get(), text.data(), length);
+  }
+}
+
+Layer::Layer()
+    : hwc2_hidl_(nullptr),
+      surface_index_(-1),
+      hardware_composer_layer_(0),
+      display_metrics_(nullptr),
+      blending_(HWC2_BLEND_MODE_NONE),
+      transform_(HWC_TRANSFORM_NONE),
+      composition_type_(HWC2_COMPOSITION_DEVICE),
+      surface_rect_functions_applied_(false) {}
+
+void Layer::Initialize(Hwc2::Composer* hwc2_hidl, HWCDisplayMetrics* metrics) {
+  hwc2_hidl_ = hwc2_hidl;
+  display_metrics_ = metrics;
+}
+
+void Layer::Reset() {
+  const int ret = acquired_buffer_.Release(std::move(release_fence_));
+  ALOGE_IF(ret < 0, "Layer::Reset: failed to release buffer: %s",
+           strerror(-ret));
+
+  if (hwc2_hidl_ != nullptr && hardware_composer_layer_) {
+    hwc2_hidl_->destroyLayer(HWC_DISPLAY_PRIMARY, hardware_composer_layer_);
+    hardware_composer_layer_ = 0;
+  }
+
+  surface_index_ = static_cast<size_t>(-1);
+  blending_ = HWC2_BLEND_MODE_NONE;
+  transform_ = HWC_TRANSFORM_NONE;
+  composition_type_ = HWC2_COMPOSITION_DEVICE;
+  direct_buffer_ = nullptr;
+  surface_ = nullptr;
+  acquire_fence_fd_.Close();
+  surface_rect_functions_applied_ = false;
+}
+
+void Layer::Setup(const std::shared_ptr<DisplaySurface>& surface,
+                  hwc2_blend_mode_t blending, hwc_transform_t transform,
+                  hwc2_composition_t composition_type, size_t index) {
+  Reset();
+  surface_index_ = index;
+  surface_ = surface;
+  blending_ = blending;
+  transform_ = transform;
+  composition_type_ = composition_type;
+  CommonLayerSetup();
+}
+
+void Layer::Setup(const std::shared_ptr<IonBuffer>& buffer,
+                  hwc2_blend_mode_t blending, hwc_transform_t transform,
+                  hwc2_composition_t composition_type, size_t z_order) {
+  Reset();
+  surface_index_ = z_order;
+  direct_buffer_ = buffer;
+  blending_ = blending;
+  transform_ = transform;
+  composition_type_ = composition_type;
+  CommonLayerSetup();
+}
+
+void Layer::UpdateDirectBuffer(const std::shared_ptr<IonBuffer>& buffer) {
+  direct_buffer_ = buffer;
+}
+
+void Layer::SetBlending(hwc2_blend_mode_t blending) { blending_ = blending; }
+
+void Layer::SetZOrderIndex(int z_index) { surface_index_ = z_index; }
+
+IonBuffer* Layer::GetBuffer() {
+  if (direct_buffer_)
+    return direct_buffer_.get();
+  else if (acquired_buffer_.IsAvailable())
+    return acquired_buffer_.buffer()->buffer();
+  else
+    return nullptr;
+}
+
+void Layer::UpdateLayerSettings() {
+  if (!IsLayerSetup()) {
+    ALOGE("HardwareComposer: Trying to update layers data on an unused layer.");
+    return;
+  }
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  hwc2_display_t display = HWC_DISPLAY_PRIMARY;
+
+  ret = (int32_t)hwc2_hidl_->setLayerCompositionType(
+      display, hardware_composer_layer_,
+      (Hwc2::IComposerClient::Composition)composition_type_);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer composition type : %d",
+           ret);
+  // ret = (int32_t) hwc2_hidl_->setLayerTransform(display,
+  // hardware_composer_layer_,
+  //                                    (Hwc2::IComposerClient::Transform)
+  //                                    transform_);
+  // ALOGE_IF(ret, "HardwareComposer: Error setting layer transform : %d", ret);
+
+  // ret = hwc2_funcs_->set_layer_blend_mode_fn_(
+  //    hardware_composer_device_, display, hardware_composer_layer_,
+  //    blending_);
+  ret = (int32_t)hwc2_hidl_->setLayerBlendMode(
+      display, hardware_composer_layer_,
+      (Hwc2::IComposerClient::BlendMode)blending_);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer blend mode : %d", ret);
+
+  Hwc2::IComposerClient::Rect display_frame;
+  display_frame.left = 0;
+  display_frame.top = 0;
+  display_frame.right = display_metrics_->width;
+  display_frame.bottom = display_metrics_->height;
+  ret = (int32_t)hwc2_hidl_->setLayerDisplayFrame(
+      display, hardware_composer_layer_, display_frame);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer display frame : %d",
+           ret);
+
+  std::vector<Hwc2::IComposerClient::Rect> visible_region(1);
+  visible_region[0] = display_frame;
+  ret = (int32_t)hwc2_hidl_->setLayerVisibleRegion(
+      display, hardware_composer_layer_, visible_region);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer visible region : %d",
+           ret);
+
+  ret = (int32_t)hwc2_hidl_->setLayerPlaneAlpha(display,
+                                                hardware_composer_layer_, 1.0f);
+  ALOGE_IF(ret, "HardwareComposer: Error setting layer plane alpha : %d", ret);
+
+  ret = (int32_t)hwc2_hidl_->setLayerZOrder(display, hardware_composer_layer_,
+                                            surface_index_);
+  ALOGE_IF(ret, "HardwareComposer: Error, setting z order index : %d", ret);
+}
+
+void Layer::CommonLayerSetup() {
+  int32_t ret = (int32_t)hwc2_hidl_->createLayer(HWC_DISPLAY_PRIMARY,
+                                                 &hardware_composer_layer_);
+
+  ALOGE_IF(ret,
+           "HardwareComposer: Failed to create layer on primary display : %d",
+           ret);
+
+  UpdateLayerSettings();
+}
+
+void Layer::Prepare() {
+  int right, bottom;
+  buffer_handle_t handle;
+
+  if (surface_) {
+    // Only update the acquired buffer when one is either available or this is
+    // the first time through.
+    if (surface_->IsBufferAvailable()) {
+      // If we previously set this to a solid color layer to stall for time,
+      // revert it to a device layer.
+      if (acquired_buffer_.IsEmpty() &&
+          composition_type_ != HWC2_COMPOSITION_DEVICE) {
+        composition_type_ = HWC2_COMPOSITION_DEVICE;
+        hwc2_hidl_->setLayerCompositionType(
+            HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+            (Hwc2::IComposerClient::Composition)HWC2_COMPOSITION_DEVICE);
+      }
+
+      DebugHudData::data.AddLayerFrame(surface_index_);
+      acquired_buffer_.Release(std::move(release_fence_));
+      acquired_buffer_ = surface_->AcquireCurrentBuffer();
+
+      // Basic latency stopgap for when the application misses a frame:
+      // If the application recovers on the 2nd or 3rd (etc) frame after
+      // missing, this code will skip a frame to catch up by checking if
+      // the next frame is also available.
+      if (surface_->IsBufferAvailable()) {
+        DebugHudData::data.SkipLayerFrame(surface_index_);
+        ATRACE_NAME("DropToCatchUp");
+        ATRACE_ASYNC_END("BufferPost", acquired_buffer_.buffer()->id());
+        acquired_buffer_ = surface_->AcquireCurrentBuffer();
+      }
+      ATRACE_ASYNC_END("BufferPost", acquired_buffer_.buffer()->id());
+    } else if (acquired_buffer_.IsEmpty()) {
+      // While we are waiting for a buffer, set this to be an empty layer
+      if (composition_type_ != HWC2_COMPOSITION_SOLID_COLOR) {
+        composition_type_ = HWC2_COMPOSITION_SOLID_COLOR;
+        hwc2_hidl_->setLayerCompositionType(
+            HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+            (Hwc2::IComposerClient::Composition)HWC2_COMPOSITION_SOLID_COLOR);
+
+        Hwc2::IComposerClient::Color layer_color = {
+            0, 0, 0, 0,
+        };
+        hwc2_hidl_->setLayerColor(HWC_DISPLAY_PRIMARY, hardware_composer_layer_,
+                                  layer_color);
+      }
+      return;
+    }
+    right = acquired_buffer_.buffer()->width();
+    bottom = acquired_buffer_.buffer()->height();
+    handle = acquired_buffer_.buffer()->native_handle();
+    acquire_fence_fd_.Reset(acquired_buffer_.ClaimAcquireFence().Release());
+  } else {
+    right = direct_buffer_->width();
+    bottom = direct_buffer_->height();
+    handle = direct_buffer_->handle();
+    acquire_fence_fd_.Close();
+  }
+
+  int32_t ret = HWC2_ERROR_NONE;
+
+  if (composition_type_ == HWC2_COMPOSITION_DEVICE) {
+    ret = (int32_t)hwc2_hidl_->setLayerBuffer(HWC_DISPLAY_PRIMARY,
+                                              hardware_composer_layer_, 0,
+                                              handle,
+                                              acquire_fence_fd_.Get());
+
+    ALOGE_IF(ret, "HardwareComposer: Error setting layer buffer : %d", ret);
+  }
+
+  if (!surface_rect_functions_applied_) {
+    Hwc2::IComposerClient::FRect crop_rect = {
+        0, 0, static_cast<float>(right), static_cast<float>(bottom),
+    };
+    hwc2_hidl_->setLayerSourceCrop(HWC_DISPLAY_PRIMARY,
+                                   hardware_composer_layer_, crop_rect);
+
+    ALOGE_IF(ret, "HardwareComposer: Error setting layer source crop : %d",
+             ret);
+
+// TODO(skiazyk): why is this ifdef'd out. Is if a driver-specific issue where
+// it must/cannot be called?
+#ifdef QCOM_BSP
+    hwc_rect_t damage_rect = {
+        0, 0, right, bottom,
+    };
+    hwc_region_t damage = {
+        1, &damage_rect,
+    };
+    // ret = hwc2_funcs_->set_layer_surface_damage(
+    //    hardware_composer_device_, HWC_DISPLAY_PRIMARY,
+    //    hardware_composer_layer_, damage);
+    // uses a std::vector as the listing
+    // hwc2_hidl_->setLayerSurfaceDamage(HWC_DISPLAY_PRIMARY,
+    // hardware_composer_layer_, vector here);
+
+    ALOGE_IF(ret, "HardwareComposer: Error settings layer surface damage : %d",
+             ret);
+#endif
+
+    surface_rect_functions_applied_ = true;
+  }
+}
+
+void Layer::Finish(int release_fence_fd) {
+  release_fence_.Reset(release_fence_fd);
+}
+
+void Layer::Drop() { acquire_fence_fd_.Close(); }
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
new file mode 100644
index 0000000..e2a8b90
--- /dev/null
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -0,0 +1,426 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
+
+#include <log/log.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer2.h>
+
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/sync_util.h>
+
+#include <array>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+#include <pdx/file_handle.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/frame_time_history.h>
+#include <private/dvr/sync_util.h>
+
+#include "acquired_buffer.h"
+#include "compositor.h"
+#include "display_surface.h"
+
+#include "DisplayHardware/ComposerHal.h"
+
+// Hardware composer HAL doesn't define HWC_TRANSFORM_NONE as of this writing.
+#ifndef HWC_TRANSFORM_NONE
+#define HWC_TRANSFORM_NONE static_cast<hwc_transform_t>(0)
+#endif
+
+namespace android {
+namespace dvr {
+
+// Basic display metrics for physical displays. Dimensions and densities are
+// relative to the physical display orientation, which may be different from the
+// logical display orientation exposed to applications.
+struct HWCDisplayMetrics {
+  int width;
+  int height;
+  struct {
+    int x;
+    int y;
+  } dpi;
+  int vsync_period_ns;
+};
+
+// Layer represents the connection between a hardware composer layer and the
+// source supplying buffers for the layer's contents.
+class Layer {
+ public:
+  Layer();
+
+  // Sets the hardware composer layer and display metrics that this Layer should
+  // use each Prepare cycle. This class does not own either of these pointers,
+  // which MUST remain valid for its lifetime. This method MUST be called once
+  // in the life of the instance before any other method is valid to call.
+  void Initialize(Hwc2::Composer* hwc2_hidl, HWCDisplayMetrics* metrics);
+
+  // Releases any shared pointers and fence handles held by this instance.
+  void Reset();
+
+  // Sets up the layer to use a display surface as its content source. The Layer
+  // will automatically handle ACQUIRE/RELEASE phases for the surface's buffer
+  // train every frame.
+  //
+  // |blending| receives HWC_BLENDING_* values.
+  // |transform| receives HWC_TRANSFORM_* values.
+  // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
+  // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
+  // |index| is the index of this surface in the DisplaySurface array.
+  void Setup(const std::shared_ptr<DisplaySurface>& surface,
+             hwc2_blend_mode_t blending, hwc_transform_t transform,
+             hwc2_composition_t composition_type, size_t index);
+
+  // Sets up the layer to use a direct buffer as its content source. No special
+  // handling of the buffer is performed; responsibility for updating or
+  // changing the buffer each frame is on the caller.
+  //
+  // |blending| receives HWC_BLENDING_* values.
+  // |transform| receives HWC_TRANSFORM_* values.
+  // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
+  // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
+  void Setup(const std::shared_ptr<IonBuffer>& buffer,
+             hwc2_blend_mode_t blending, hwc_transform_t transform,
+             hwc2_composition_t composition_type, size_t z_order);
+
+  // Layers that use a direct IonBuffer should call this each frame to update
+  // which buffer will be used for the next PostLayers.
+  void UpdateDirectBuffer(const std::shared_ptr<IonBuffer>& buffer);
+
+  // Sets up the hardware composer layer for the next frame. When the layer is
+  // associated with a display surface, this method automatically ACQUIRES a new
+  // buffer if one is available.
+  void Prepare();
+
+  // After calling prepare, if this frame is to be dropped instead of passing
+  // along to the HWC, call Drop to close the contained fence(s).
+  void Drop();
+
+  // Performs fence bookkeeping after the frame has been posted to hardware
+  // composer.
+  void Finish(int release_fence_fd);
+
+  // Sets the blending for the layer. |blending| receives HWC_BLENDING_* values.
+  void SetBlending(hwc2_blend_mode_t blending);
+
+  // Sets the Z-order of this layer
+  void SetZOrderIndex(int surface_index);
+
+  // Gets the current IonBuffer associated with this layer. Ownership of the
+  // buffer DOES NOT pass to the caller and the pointer is not guaranteed to
+  // remain valid across calls to Layer::Setup(), Layer::Prepare(), or
+  // Layer::Reset(). YOU HAVE BEEN WARNED.
+  IonBuffer* GetBuffer();
+
+  hwc2_composition_t GetCompositionType() const { return composition_type_; }
+
+  hwc2_layer_t GetLayerHandle() const { return hardware_composer_layer_; }
+
+  bool UsesDirectBuffer() const { return direct_buffer_ != nullptr; }
+
+  bool IsLayerSetup() const {
+    return direct_buffer_ != nullptr || surface_ != nullptr;
+  }
+
+  // Applies all of the settings to this layer using the hwc functions
+  void UpdateLayerSettings();
+
+  int GetSurfaceId() const {
+    if (surface_ != nullptr) {
+      return surface_->surface_id();
+    } else {
+      return -1;
+    }
+  }
+
+ private:
+  void CommonLayerSetup();
+
+  Hwc2::Composer* hwc2_hidl_;
+
+  // Original display surface array index for tracking purposes.
+  size_t surface_index_;
+
+  // The hardware composer layer and metrics to use during the prepare cycle.
+  hwc2_layer_t hardware_composer_layer_;
+  HWCDisplayMetrics* display_metrics_;
+
+  // Layer properties used to setup the hardware composer layer during the
+  // Prepare phase.
+  hwc2_blend_mode_t blending_;
+  hwc_transform_t transform_;
+  hwc2_composition_t composition_type_;
+
+  // These two members are mutually exclusive. When direct_buffer_ is set the
+  // Layer gets its contents directly from that buffer; when surface_ is set the
+  // Layer gets it contents from the surface's buffer train.
+  std::shared_ptr<IonBuffer> direct_buffer_;
+  std::shared_ptr<DisplaySurface> surface_;
+
+  // State when associated with a display surface.
+  AcquiredBuffer acquired_buffer_;
+  pdx::LocalHandle release_fence_;
+
+  pdx::LocalHandle acquire_fence_fd_;
+  bool surface_rect_functions_applied_;
+
+  Layer(const Layer&) = delete;
+  void operator=(const Layer&) = delete;
+};
+
+// HardwareComposer encapsulates the hardware composer HAL, exposing a
+// simplified API to post buffers to the display.
+class HardwareComposer {
+ public:
+  // Type for vsync callback.
+  using VSyncCallback = std::function<void(int, int64_t, int64_t, uint32_t)>;
+
+  // Since there is no universal way to query the number of hardware layers,
+  // just set it to 4 for now.
+  static constexpr int kMaxHardwareLayers = 4;
+
+  HardwareComposer();
+  HardwareComposer(Hwc2::Composer* hidl);
+  ~HardwareComposer();
+
+  bool Initialize();
+
+  bool IsInitialized() const { return initialized_; }
+
+  bool Suspend();
+  bool Resume();
+
+  // Get the HMD display metrics for the current display.
+  DisplayMetrics GetHmdDisplayMetrics() const;
+
+  int32_t GetDisplayAttribute(hwc2_display_t display, hwc2_config_t config,
+                              hwc2_attribute_t attributes,
+                              int32_t* out_value) const;
+  int32_t GetDisplayMetrics(hwc2_display_t display, hwc2_config_t config,
+                            HWCDisplayMetrics* out_metrics) const;
+  void Dump(char* buffer, uint32_t* out_size);
+
+  void SetVSyncCallback(VSyncCallback callback);
+
+  // Metrics of the logical display, which is always landscape.
+  int DisplayWidth() const { return display_metrics_.width; }
+  int DisplayHeight() const { return display_metrics_.height; }
+  HWCDisplayMetrics display_metrics() const { return display_metrics_; }
+
+  // Metrics of the native display, which depends on the specific hardware
+  // implementation of the display.
+  HWCDisplayMetrics native_display_metrics() const {
+    return native_display_metrics_;
+  }
+
+  std::shared_ptr<IonBuffer> framebuffer_target() const {
+    return framebuffer_target_;
+  }
+
+  // Set the display surface stack to compose to the display each frame.
+  int SetDisplaySurfaces(std::vector<std::shared_ptr<DisplaySurface>> surfaces);
+
+  Compositor* GetCompositor() { return &compositor_; }
+
+  void OnHardwareComposerRefresh();
+
+ private:
+  int32_t EnableVsync(bool enabled);
+
+  class ComposerCallback : public Hwc2::IComposerCallback {
+   public:
+    ComposerCallback() {}
+
+    hardware::Return<void> onHotplug(Hwc2::Display /*display*/,
+                                     Connection /*connected*/) override {
+      // TODO(skiazyk): depending on how the server is implemented, we might
+      // have to set it up to synchronize with receiving this event, as it can
+      // potentially be a critical event for setting up state within the
+      // hwc2 module. That is, we (technically) should not call any other hwc
+      // methods until this method has been called after registering the
+      // callbacks.
+      return hardware::Void();
+    }
+
+    hardware::Return<void> onRefresh(Hwc2::Display /*display*/) override {
+      return hardware::Void();
+    }
+
+    hardware::Return<void> onVsync(Hwc2::Display /*display*/,
+                                   int64_t /*timestamp*/) override {
+      return hardware::Void();
+    }
+  };
+
+  int32_t Validate(hwc2_display_t display);
+  int32_t Present(hwc2_display_t display);
+
+  void SetBacklightBrightness(int brightness);
+
+  void PostLayers(bool is_geometry_changed);
+  void PostThread();
+
+  int ReadWaitPPState();
+  int BlockUntilVSync(/*out*/ bool* suspend_requested);
+  int ReadVSyncTimestamp(int64_t* timestamp);
+  int WaitForVSync(int64_t* timestamp);
+  int SleepUntil(int64_t wakeup_timestamp);
+
+  bool IsFramePendingInDriver() { return ReadWaitPPState() == 1; }
+
+  // Returns true if the layer config changed, false otherwise
+  bool UpdateLayerConfig(
+      std::vector<std::shared_ptr<DisplaySurface>>* compositor_surfaces);
+  void PostCompositorBuffers(
+      const std::vector<std::shared_ptr<DisplaySurface>>& compositor_surfaces);
+
+  void UpdateDisplayState();
+
+  struct FrameTimeMeasurementRecord {
+    int64_t start_time;
+    pdx::LocalHandle fence;
+
+    FrameTimeMeasurementRecord(FrameTimeMeasurementRecord&&) = default;
+    FrameTimeMeasurementRecord& operator=(FrameTimeMeasurementRecord&&) =
+        default;
+    FrameTimeMeasurementRecord(const FrameTimeMeasurementRecord&) = delete;
+    FrameTimeMeasurementRecord& operator=(const FrameTimeMeasurementRecord&) =
+        delete;
+  };
+
+  void UpdateFrameTimeHistory(std::vector<FrameTimeMeasurementRecord>* backlog,
+                              int backlog_max,
+                              FenceInfoBuffer* fence_info_buffer,
+                              FrameTimeHistory* history);
+
+  // Returns true if the frame finished rendering, false otherwise. If the frame
+  // finished the frame end time is stored in timestamp. Doesn't block.
+  bool CheckFrameFinished(int frame_fence_fd,
+                          FenceInfoBuffer* fence_info_buffer,
+                          int64_t* timestamp);
+
+  void HandlePendingScreenshots();
+
+  bool initialized_;
+
+  // Hardware composer HAL device.
+  std::unique_ptr<Hwc2::Composer> hwc2_hidl_;
+  sp<ComposerCallback> callbacks_;
+
+  // Display metrics of the physical display.
+  HWCDisplayMetrics native_display_metrics_;
+  // Display metrics of the logical display, adjusted so that orientation is
+  // landscape.
+  HWCDisplayMetrics display_metrics_;
+  // Transform required to get from native to logical display orientation.
+  hwc_transform_t display_transform_;
+
+  // Buffer for the background layer required by hardware composer.
+  std::shared_ptr<IonBuffer> framebuffer_target_;
+
+  // Protects access to the display surfaces and logical layers.
+  std::mutex layer_mutex_;
+
+  // Active display surfaces configured by the display manager.
+  std::vector<std::shared_ptr<DisplaySurface>> display_surfaces_;
+  std::vector<std::shared_ptr<DisplaySurface>> added_display_surfaces_;
+  bool display_surfaces_updated_;
+  bool hardware_layers_need_update_;
+
+  // Layer array for handling buffer flow into hardware composer layers.
+  // Note that the first array is the actual storage for the layer objects,
+  // and the latter is an array of pointers, which can be freely re-arranged
+  // without messing up the underlying objects.
+  std::array<Layer, kMaxHardwareLayers> layer_storage_;
+  std::array<Layer*, kMaxHardwareLayers> layers_;
+  size_t active_layer_count_;
+
+  // Set by the Post thread to the index of the GPU compositing output
+  // buffer in the layers_ array.
+  Layer* gpu_layer_;
+
+  // Handler to hook vsync events outside of this class.
+  VSyncCallback vsync_callback_;
+
+  // The layer posting thread. This thread wakes up a short time before vsync to
+  // hand buffers to post processing and the results to hardware composer.
+  std::thread post_thread_;
+
+  enum class PostThreadState {
+    // post_thread_state_ starts off paused. When suspending, the control thread
+    // will block until post_thread_state_ == kPaused, indicating the post
+    // thread has completed the transition to paused (most importantly: no more
+    // hardware composer calls).
+    kPaused,
+    // post_thread_state_ is set to kRunning by the control thread (either
+    // surface flinger's main thread or the vr flinger dispatcher thread). The
+    // post thread blocks until post_thread_state_ == kRunning.
+    kRunning,
+    // Set by the control thread to indicate the post thread should pause. The
+    // post thread will change post_thread_state_ from kPauseRequested to
+    // kPaused when it stops.
+    kPauseRequested
+  };
+  // Control variables to control the state of the post thread
+  PostThreadState post_thread_state_;
+  // Used to wake the post thread up while it's waiting for vsync, for faster
+  // transition to the paused state.
+  pdx::LocalHandle terminate_post_thread_event_fd_;
+  // post_thread_state_mutex_ should be held before checking or modifying
+  // post_thread_state_.
+  std::mutex post_thread_state_mutex_;
+  // Used to communicate between the control thread and the post thread.
+  std::condition_variable post_thread_state_cond_var_;
+
+  // Backlight LED brightness sysfs node.
+  pdx::LocalHandle backlight_brightness_fd_;
+
+  // Primary display vsync event sysfs node.
+  pdx::LocalHandle primary_display_vsync_event_fd_;
+
+  // Primary display wait_pingpong state sysfs node.
+  pdx::LocalHandle primary_display_wait_pp_fd_;
+
+  // VSync sleep timerfd.
+  pdx::LocalHandle vsync_sleep_timer_fd_;
+
+  // The timestamp of the last vsync.
+  int64_t last_vsync_timestamp_;
+
+  // Vsync count since display on.
+  uint32_t vsync_count_;
+
+  // Counter tracking the number of skipped frames.
+  int frame_skip_count_;
+
+  // After construction, only accessed on post_thread_.
+  Compositor compositor_;
+
+  // Fd array for tracking retire fences that are returned by hwc. This allows
+  // us to detect when the display driver begins queuing frames.
+  std::vector<pdx::LocalHandle> retire_fence_fds_;
+
+  // Pose client for frame count notifications. Pose client predicts poses
+  // out to display frame boundaries, so we need to tell it about vsyncs.
+  DvrPose* pose_client_;
+
+  static void HwcRefresh(hwc2_callback_data_t data, hwc2_display_t display);
+  static void HwcVSync(hwc2_callback_data_t data, hwc2_display_t display,
+                       int64_t timestamp);
+  static void HwcHotplug(hwc2_callback_data_t callbackData,
+                         hwc2_display_t display, hwc2_connection_t connected);
+
+  HardwareComposer(const HardwareComposer&) = delete;
+  void operator=(const HardwareComposer&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
new file mode 100644
index 0000000..17dce96
--- /dev/null
+++ b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
@@ -0,0 +1,34 @@
+#ifndef ANDROID_DVR_VR_FLINGER_H_
+#define ANDROID_DVR_VR_FLINGER_H_
+
+#include <thread>
+#include <memory>
+
+namespace android {
+
+namespace Hwc2 {
+class Composer;
+}  // namespace Hwc2
+
+namespace dvr {
+
+class DisplayService;
+
+class VrFlinger {
+ public:
+  VrFlinger();
+  int Run(Hwc2::Composer* hidl);
+
+  void EnterVrMode();
+  void ExitVrMode();
+  void OnHardwareComposerRefresh();
+
+ private:
+  std::thread displayd_thread_;
+  std::shared_ptr<android::dvr::DisplayService> display_service_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_VR_FLINGER_H_
diff --git a/libs/vr/libvrflinger/screenshot_service.cpp b/libs/vr/libvrflinger/screenshot_service.cpp
new file mode 100644
index 0000000..fd1c582
--- /dev/null
+++ b/libs/vr/libvrflinger/screenshot_service.cpp
@@ -0,0 +1,189 @@
+#include "screenshot_service.h"
+
+#include <utils/Trace.h>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/android_filesystem_config.h>
+#include <private/dvr/display_types.h>
+#include <private/dvr/trusted_uids.h>
+
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+using android::pdx::rpc::RemoteMethodError;
+using android::pdx::rpc::RemoteMethodReturn;
+
+namespace android {
+namespace dvr {
+
+ScreenshotService::~ScreenshotService() { instance_ = nullptr; }
+
+int ScreenshotService::HandleMessage(pdx::Message& message) {
+  switch (message.GetOp()) {
+    case DisplayScreenshotRPC::GetFormat::Opcode:
+      DispatchRemoteMethod<DisplayScreenshotRPC::GetFormat>(
+          *this, &ScreenshotService::OnGetFormat, message);
+      return 0;
+
+    case DisplayScreenshotRPC::TakeScreenshot::Opcode:
+      DispatchRemoteMethod<DisplayScreenshotRPC::TakeScreenshot>(
+          *this, &ScreenshotService::OnTakeScreenshot, message);
+      return 0;
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+int ScreenshotService::OnGetFormat(pdx::Message&) {
+  return HAL_PIXEL_FORMAT_RGB_888;
+}
+
+ScreenshotData ScreenshotService::OnTakeScreenshot(pdx::Message& message,
+                                                   int layer_index) {
+  // Also allow AID_SHELL to support vrscreencap commands.
+  if (message.GetEffectiveUserId() != AID_SHELL &&
+      !IsTrustedUid(message.GetEffectiveUserId())) {
+    REPLY_ERROR_RETURN(message, EACCES, {});
+  }
+
+  AddWaiter(std::move(message), layer_index);
+  return {};
+}
+
+void ScreenshotService::AddWaiter(pdx::Message&& message, int layer_index) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  waiters_.emplace_back(std::move(message), layer_index);
+}
+
+void ScreenshotService::TakeIfNeeded(
+    std::array<Layer*, HardwareComposer::kMaxHardwareLayers>& hw_layers,
+    Compositor& compositor) {
+  std::lock_guard<std::mutex> lock(mutex_);
+
+  // Send the buffer contents to all of our waiting clients.
+  for (auto& waiter : waiters_) {
+    if (waiter.IsDone())
+      continue;
+
+    if (waiter.layer_index() == 0) {
+      ALOGE(
+          "ScreenshotService::TakeIfNeeded: Capturing the composited display "
+          "output is not yet supported.");
+
+      waiter.Error(EINVAL);
+      continue;
+    }
+
+    if (waiter.layer_index() > 0) {
+      // Check for hardware layer screenshot requests.
+      // Hardware layers are requested with positive indices starting at 1.
+      const size_t layer_index = static_cast<size_t>(waiter.layer_index() - 1);
+
+      if (layer_index >= hw_layers.size()) {
+        waiter.Error(EINVAL);
+        continue;
+      }
+
+      auto buffer = hw_layers[layer_index]->GetBuffer();
+      if (!buffer) {
+        waiter.Error(ENOBUFS);
+        continue;
+      }
+
+      auto data = compositor.ReadBufferPixels(buffer);
+      if (data.empty()) {
+        waiter.Error(ENOBUFS);
+        continue;
+      }
+
+      Take(&waiter, data.data(), buffer->width(), buffer->height(),
+           buffer->width());
+    } else {
+      // Check for compositor input layer screenshot requests.
+      // Prewarp surfaces are requested with negative indices starting at -1.
+      const size_t layer_index = static_cast<size_t>(-waiter.layer_index() - 1);
+
+      if (layer_index >= compositor.GetLayerCount()) {
+        waiter.Error(EINVAL);
+        continue;
+      }
+
+      int width = 0;
+      int height = 0;
+      auto data = compositor.ReadLayerPixels(layer_index, &width, &height);
+      if (data.empty()) {
+        waiter.Error(ENOBUFS);
+        continue;
+      }
+
+      Take(&waiter, data.data(), width, height, width);
+    }
+  }
+
+  // Reply with error to requests that did not match up with a source layer.
+  for (auto& waiter : waiters_) {
+    if (!waiter.IsDone())
+      waiter.Error(EAGAIN);
+  }
+  waiters_.clear();
+}
+
+void ScreenshotWaiter::Reply(const ScreenshotData& screenshot) {
+  ALOGI("Returning screenshot: size=%zu recv_size=%zu",
+        screenshot.buffer.size(), message_.GetReceiveLength());
+  RemoteMethodReturn<DisplayScreenshotRPC::TakeScreenshot>(message_,
+                                                           screenshot);
+}
+
+void ScreenshotWaiter::Error(int error) { RemoteMethodError(message_, error); }
+
+void ScreenshotService::Take(ScreenshotWaiter* waiter, const void* rgba_data,
+                             int32_t width, int32_t height, int buffer_stride) {
+  ATRACE_NAME(__PRETTY_FUNCTION__);
+
+  bool is_portrait = height > width;
+  if (is_portrait) {
+    std::swap(width, height);
+  }
+  int response_stride = width;
+
+  // Convert from RGBA to RGB and if in portrait, rotates to landscape; store
+  // the result in the response buffer.
+  ScreenshotData screenshot{width, height,
+                            std::vector<uint8_t>(width * height * 3)};
+
+  const auto rgba_bytes = static_cast<const uint8_t*>(rgba_data);
+  for (int j = 0; j < height; ++j) {
+    for (int i = 0; i < width; ++i) {
+      // If the screenshot is in portrait mode, rotate into landscape mode.
+      const int response_index = is_portrait
+                                     ? (height - j - 1) * response_stride + i
+                                     : j * response_stride + i;
+      const int buffer_index =
+          is_portrait ? i * buffer_stride + j : j * buffer_stride + i;
+      screenshot.buffer[response_index * 3 + 0] =
+          rgba_bytes[buffer_index * 4 + 0];
+      screenshot.buffer[response_index * 3 + 1] =
+          rgba_bytes[buffer_index * 4 + 1];
+      screenshot.buffer[response_index * 3 + 2] =
+          rgba_bytes[buffer_index * 4 + 2];
+    }
+  }
+
+  waiter->Reply(screenshot);
+}
+
+ScreenshotService::ScreenshotService()
+    : BASE("ScreenshotService",
+           Endpoint::Create(DisplayScreenshotRPC::kClientPath)) {
+  instance_ = this;
+}
+
+ScreenshotService* ScreenshotService::GetInstance() { return instance_; }
+
+ScreenshotService* ScreenshotService::instance_ = nullptr;
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/screenshot_service.h b/libs/vr/libvrflinger/screenshot_service.h
new file mode 100644
index 0000000..ec4c527
--- /dev/null
+++ b/libs/vr/libvrflinger/screenshot_service.h
@@ -0,0 +1,82 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_SCREENSHOT_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_SCREENSHOT_SERVICE_H_
+
+#include <pdx/rpc/pointer_wrapper.h>
+#include <pdx/service.h>
+#include <private/dvr/ion_buffer.h>
+
+#include <mutex>
+#include <vector>
+
+#include "hardware_composer.h"
+
+namespace android {
+namespace dvr {
+
+class ScreenshotWaiter {
+ public:
+  explicit ScreenshotWaiter(pdx::Message&& message, int layer_index)
+      : message_(std::move(message)), layer_index_(layer_index) {}
+  ScreenshotWaiter(ScreenshotWaiter&&) = default;
+
+  void Reply(const ScreenshotData& screenshot);
+  void Error(int error);
+
+  bool IsDone() const { return message_.replied(); }
+  int layer_index() const { return layer_index_; }
+
+ private:
+  pdx::Message message_;
+  int layer_index_;
+
+  ScreenshotWaiter(const ScreenshotWaiter&) = delete;
+  void operator=(const ScreenshotWaiter&) = delete;
+};
+
+// The screenshot service allows clients to obtain screenshots from displayd.
+class ScreenshotService : public pdx::ServiceBase<ScreenshotService> {
+ public:
+  ~ScreenshotService();
+
+  int HandleMessage(pdx::Message& message) override;
+
+  // Returns true if there is a pending screenshot request.
+  bool IsScreenshotRequestPending() const {
+    std::lock_guard<std::mutex> lock(mutex_);
+    return !waiters_.empty();
+  }
+
+  // If any clients are currently waiting for a screenshot, read back the
+  // contents of requested layers and send the resulting
+  // image to the clients.
+  void TakeIfNeeded(
+      std::array<Layer*, HardwareComposer::kMaxHardwareLayers>& hw_layers,
+      Compositor& compositor);
+
+  static ScreenshotService* GetInstance();
+
+ private:
+  friend BASE;
+
+  ScreenshotService();
+
+  void AddWaiter(pdx::Message&& message, int layer_index);
+
+  ScreenshotData OnTakeScreenshot(pdx::Message& message, int index);
+  int OnGetFormat(pdx::Message& message);
+
+  // Copy the given screenshot data into the message reply.
+  void Take(ScreenshotWaiter* waiter, const void* rgba_data, int32_t width,
+            int32_t height, int buffer_stride);
+
+  static ScreenshotService* instance_;
+
+  // Protects access to subsequent member variables.
+  mutable std::mutex mutex_;
+  std::vector<ScreenshotWaiter> waiters_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_SCREENSHOT_SERVICE_H_
diff --git a/libs/vr/libvrflinger/surface_channel.cpp b/libs/vr/libvrflinger/surface_channel.cpp
new file mode 100644
index 0000000..8aa220b
--- /dev/null
+++ b/libs/vr/libvrflinger/surface_channel.cpp
@@ -0,0 +1,44 @@
+#include "surface_channel.h"
+
+using android::pdx::BorrowedChannelHandle;
+using android::pdx::Message;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+int SurfaceChannel::HandleMessage(Message& message) {
+  switch (message.GetOp()) {
+    case DisplayRPC::GetMetadataBuffer::Opcode:
+      DispatchRemoteMethod<DisplayRPC::GetMetadataBuffer>(
+          *this, &SurfaceChannel::OnGetMetadataBuffer, message);
+      break;
+  }
+
+  return 0;
+}
+
+BorrowedChannelHandle SurfaceChannel::OnGetMetadataBuffer(Message& message) {
+  if (EnsureMetadataBuffer()) {
+    return metadata_buffer_->GetChannelHandle().Borrow();
+  } else {
+    REPLY_ERROR_RETURN(message, -ENOMEM, {});
+  }
+}
+
+bool SurfaceChannel::EnsureMetadataBuffer() {
+  if (!metadata_buffer_) {
+    metadata_buffer_ =
+        BufferProducer::CreateUncachedBlob(metadata_size());
+    if (!metadata_buffer_) {
+      ALOGE(
+          "DisplaySurface::EnsureMetadataBuffer: could not allocate metadata "
+          "buffer");
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/surface_channel.h b/libs/vr/libvrflinger/surface_channel.h
new file mode 100644
index 0000000..870e1a4
--- /dev/null
+++ b/libs/vr/libvrflinger/surface_channel.h
@@ -0,0 +1,63 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_SURFACE_CHANNEL_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_SURFACE_CHANNEL_H_
+
+#include <pdx/service.h>
+#include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/display_rpc.h>
+
+namespace android {
+namespace dvr {
+
+class DisplayService;
+
+class SurfaceChannel : public pdx::Channel {
+ public:
+  SurfaceChannel(DisplayService* service, int channel_id, SurfaceType type,
+                 size_t metadata_size)
+      : service_(service),
+        surface_id_(channel_id),
+        type_(type),
+        metadata_size_(metadata_size) {}
+
+  ~SurfaceChannel() override = default;
+
+  DisplayService* service() const { return service_; }
+  int surface_id() const { return surface_id_; }
+  SurfaceType type() const { return type_; }
+  size_t metadata_size() const { return metadata_size_; }
+
+  pdx::LocalHandle GetMetadataBufferFd() {
+    return EnsureMetadataBuffer() ? metadata_buffer_->GetBlobFd()
+                                  : pdx::LocalHandle{};
+  }
+
+  // Dispatches surface channel messages to the appropriate handlers. This
+  // handler runs on the displayd message dispatch thread.
+  virtual int HandleMessage(pdx::Message& message);
+
+ protected:
+  // Contains the surface metadata.
+  std::shared_ptr<BufferProducer> metadata_buffer_;
+
+  // Returns the metadata buffer for this surface. The first call allocates the
+  // buffer, while subsequent calls return the same buffer.
+  pdx::BorrowedChannelHandle OnGetMetadataBuffer(pdx::Message& message);
+
+  // Allocates the single metadata buffer for this surface unless it is already
+  // allocated. Idempotent when called multiple times.
+  bool EnsureMetadataBuffer();
+
+ private:
+  DisplayService* service_;
+  int surface_id_;
+  SurfaceType type_;
+  size_t metadata_size_;
+
+  SurfaceChannel(const SurfaceChannel&) = delete;
+  void operator=(const SurfaceChannel&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_SURFACE_CHANNEL_H_
diff --git a/libs/vr/libvrflinger/video_compositor.cpp b/libs/vr/libvrflinger/video_compositor.cpp
new file mode 100644
index 0000000..6b39a3c
--- /dev/null
+++ b/libs/vr/libvrflinger/video_compositor.cpp
@@ -0,0 +1,129 @@
+#include "video_compositor.h"
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2ext.h>
+
+#include <private/dvr/debug.h>
+#include <private/dvr/display_rpc.h>
+
+namespace android {
+namespace dvr {
+
+VideoCompositor::Texture::Texture(
+    EGLDisplay display, const std::shared_ptr<BufferConsumer>& buffer_consumer)
+    : display_(display),
+      image_(EGL_NO_IMAGE_KHR),
+      texture_id_(0),
+      buffer_consumer_(buffer_consumer) {}
+
+VideoCompositor::Texture::~Texture() {
+  if (image_ != EGL_NO_IMAGE_KHR)
+    eglDestroyImageKHR(display_, image_);
+  if (texture_id_ != 0)
+    glDeleteTextures(1, &texture_id_);
+}
+
+GLuint VideoCompositor::Texture::EnsureTextureReady() {
+  if (!image_) {
+    native_buffer_ = new NativeBuffer(buffer_consumer_);
+    CHECK_GL();
+
+    image_ = eglCreateImageKHR(
+        display_, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+        static_cast<ANativeWindowBuffer*>(native_buffer_.get()), nullptr);
+    if (!image_) {
+      ALOGE("Failed to create EGLImage.");
+      return 0;
+    }
+
+    glGenTextures(1, &texture_id_);
+    glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_);
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image_);
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
+                    GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
+                    GL_CLAMP_TO_EDGE);
+    CHECK_GL();
+  }
+
+  return texture_id_;
+}
+
+void VideoCompositor::Texture::Release() {
+  const int ret = buffer_consumer_->Release({});
+  if (ret < 0) {
+    ALOGE(
+        "VideoCompositor::Texture::Release: Failed to release buffer, error: "
+        "%s",
+        strerror(-ret));
+  }
+}
+
+VideoCompositor::VideoCompositor(
+    const std::shared_ptr<VideoMeshSurface>& surface,
+    const volatile DisplaySurfaceMetadata* display_surface_metadata)
+    : surface_(surface),
+      consumer_queue_(surface->GetConsumerQueue()),
+      transform_metadata_(display_surface_metadata),
+      active_texture_slot_(-1) {}
+
+GLuint VideoCompositor::GetActiveTextureId(EGLDisplay display) {
+  size_t slot;
+  VideoMeshSurfaceBufferMetadata metadata;
+
+  while (true) {
+    // A native way to pick the active texture: always dequeue all buffers from
+    // the queue until it's empty. This works well as long as video frames are
+    // queued in order from the producer side.
+    // TODO(jwcai) Use |metadata.timestamp_ns| to schedule video frames
+    // accurately.
+    auto buffer_consumer = consumer_queue_->Dequeue(0, &slot, &metadata);
+
+    if (buffer_consumer) {
+      // Create a new texture if it hasn't been created yet, or the same slot
+      // has a new |buffer_consumer|.
+      if (textures_[slot] == nullptr ||
+          textures_[slot]->event_fd() != buffer_consumer->event_fd()) {
+        textures_[slot] =
+            std::unique_ptr<Texture>(new Texture(display, buffer_consumer));
+      }
+
+      if (active_texture_slot_ != static_cast<int>(slot)) {
+        if (active_texture_slot_ >= 0) {
+          // Release the last active texture and move on to use the new one.
+          textures_[active_texture_slot_]->Release();
+        }
+        active_texture_slot_ = slot;
+      }
+    } else {
+      break;
+    }
+  }
+
+  if (active_texture_slot_ < 0) {
+    // No texture is active yet.
+    return 0;
+  }
+
+  return textures_[active_texture_slot_]->EnsureTextureReady();
+}
+
+mat4 VideoCompositor::GetTransform(int eye, size_t render_buffer_index) {
+  volatile const VideoMeshSurfaceMetadata* transform_metadata =
+      surface_->GetMetadataBufferPtr();
+
+  mat4 screen_transform;
+  for (int i = 0; i < 4; ++i) {
+    for (int j = 0; j < 4; ++j) {
+      screen_transform(i, j) =
+          transform_metadata->transform[render_buffer_index][eye].val[i][j];
+    }
+  }
+
+  return screen_transform;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/video_compositor.h b/libs/vr/libvrflinger/video_compositor.h
new file mode 100644
index 0000000..d0e72e1
--- /dev/null
+++ b/libs/vr/libvrflinger/video_compositor.h
@@ -0,0 +1,84 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_COMPOSITOR_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_COMPOSITOR_H_
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <private/dvr/buffer_hub_queue_core.h>
+#include <private/dvr/types.h>
+
+#include <vector>
+
+#include "display_surface.h"
+#include "video_mesh_surface.h"
+
+namespace android {
+namespace dvr {
+
+using pdx::LocalHandle;
+
+// Manages video buffer consumers, texture mapping, and playback timing.
+class VideoCompositor {
+ public:
+  VideoCompositor(
+      const std::shared_ptr<VideoMeshSurface>& surface,
+      const volatile DisplaySurfaceMetadata* display_surface_metadata);
+
+  int surface_id() const { return surface_ ? surface_->surface_id() : -1; }
+
+  // Returns a GL texture id that should be composited by displayd during the
+  // current rendering loop. Note that this function must be called in
+  // displayd's GL context.
+  GLuint GetActiveTextureId(EGLDisplay display);
+
+  // Returns a basic video mesh tranform.
+  mat4 GetTransform(int eye, size_t render_buffer_index);
+
+ private:
+  class Texture {
+   public:
+    Texture(EGLDisplay display,
+            const std::shared_ptr<BufferConsumer>& buffer_consumer);
+    ~Texture();
+
+    // Returns the |event_fd| of the underlying buffer consumer. Caller can use
+    // this to decided whether the Texture need to be recreated for a different
+    // buffer consumer.
+    int event_fd() const { return buffer_consumer_->event_fd(); }
+
+    // Method to map a dvr::BufferConsumer to a GL texture within the current GL
+    // context. If the current Texture object's |image_| hasn't been
+    // initialized, the method will do so based on the |buffer_consumer| and a
+    // new GL texture will be generated, cached, and returned. Otherwise, the
+    // cached |texture_id_| will be returned directly.
+    GLuint EnsureTextureReady();
+
+    // Signal bufferhub that the texture is done rendering so that the buffer
+    // can be re-gained by the producer for future use.
+    void Release();
+
+   private:
+    using NativeBuffer = BufferHubQueueCore::NativeBuffer;
+
+    EGLDisplay display_;
+    EGLImageKHR image_;
+    GLuint texture_id_;
+    sp<NativeBuffer> native_buffer_;
+    std::shared_ptr<BufferConsumer> buffer_consumer_;
+  };
+
+  std::shared_ptr<VideoMeshSurface> surface_;
+  std::shared_ptr<ConsumerQueue> consumer_queue_;
+  std::array<std::unique_ptr<Texture>, BufferHubQueue::kMaxQueueCapacity>
+      textures_;
+
+  const volatile DisplaySurfaceMetadata* transform_metadata_;
+  int active_texture_slot_;
+
+  VideoCompositor(const VideoCompositor&) = delete;
+  void operator=(const VideoCompositor&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_COMPOSITOR_H_
diff --git a/libs/vr/libvrflinger/video_mesh_surface.cpp b/libs/vr/libvrflinger/video_mesh_surface.cpp
new file mode 100644
index 0000000..a961a3d
--- /dev/null
+++ b/libs/vr/libvrflinger/video_mesh_surface.cpp
@@ -0,0 +1,59 @@
+#include "video_mesh_surface.h"
+
+#include <private/dvr/display_rpc.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+VideoMeshSurface::VideoMeshSurface(DisplayService* service, int surface_id)
+    : SurfaceChannel(service, surface_id, SurfaceTypeEnum::VideoMesh,
+                     sizeof(VideoMeshSurfaceMetadata)) {}
+
+VideoMeshSurface::~VideoMeshSurface() {}
+
+int VideoMeshSurface::HandleMessage(Message& message) {
+  ATRACE_NAME("VideoMeshSurface::HandleMessage");
+
+  switch (message.GetOp()) {
+    case DisplayRPC::VideoMeshSurfaceCreateProducerQueue::Opcode:
+      DispatchRemoteMethod<DisplayRPC::VideoMeshSurfaceCreateProducerQueue>(
+          *this, &VideoMeshSurface::OnCreateProducerQueue, message);
+      break;
+
+    default:
+      return SurfaceChannel::HandleMessage(message);
+  }
+
+  return 0;
+}
+
+std::shared_ptr<ConsumerQueue> VideoMeshSurface::GetConsumerQueue() {
+  if (!consumer_queue_) {
+    ALOGE(
+        "VideoMeshSurface::GetConsumerQueue: consumer_queue is uninitialized.");
+  }
+
+  return consumer_queue_;
+}
+
+LocalChannelHandle VideoMeshSurface::OnCreateProducerQueue(Message& message) {
+  ATRACE_NAME("VideoMeshSurface::OnCreateProducerQueue");
+
+  if (consumer_queue_ != nullptr) {
+    ALOGE(
+        "VideoMeshSurface::OnCreateProducerQueue: A ProdcuerQueue has already "
+        "been created and transported to VideoMeshSurfaceClient.");
+    REPLY_ERROR_RETURN(message, EALREADY, {});
+  }
+
+  auto producer = ProducerQueue::Create<VideoMeshSurfaceBufferMetadata>();
+  consumer_queue_ = producer->CreateConsumerQueue();
+
+  return std::move(producer->GetChannelHandle());
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/video_mesh_surface.h b/libs/vr/libvrflinger/video_mesh_surface.h
new file mode 100644
index 0000000..1370793
--- /dev/null
+++ b/libs/vr/libvrflinger/video_mesh_surface.h
@@ -0,0 +1,52 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_MESH_SURFACE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_MESH_SURFACE_H_
+
+#include <private/dvr/buffer_hub_queue_client.h>
+
+#include "surface_channel.h"
+
+namespace android {
+namespace dvr {
+
+class DisplayService;
+
+// VideoMeshSurface takes three inputs: 1) buffers filled by Android system
+// components (e.g. MediaCodec or camera stack) other than applications' GL
+// context; 2) a 3D mesh choosen by application to define the shape of the
+// surface; 3) a transformation matrix from application to define the rotation,
+// position, and scaling of the video surface.
+class VideoMeshSurface : public SurfaceChannel {
+ public:
+  using Message = pdx::Message;
+  using LocalChannelHandle = pdx::LocalChannelHandle;
+
+  VideoMeshSurface(DisplayService* service, int channel_id);
+  ~VideoMeshSurface() override;
+
+  volatile const VideoMeshSurfaceMetadata* GetMetadataBufferPtr() {
+    if (EnsureMetadataBuffer()) {
+      void* addr = nullptr;
+      metadata_buffer_->GetBlobReadWritePointer(metadata_size(), &addr);
+      return static_cast<const volatile VideoMeshSurfaceMetadata*>(addr);
+    } else {
+      return nullptr;
+    }
+  }
+
+  int HandleMessage(Message& message) override;
+
+  std::shared_ptr<ConsumerQueue> GetConsumerQueue();
+
+ private:
+  LocalChannelHandle OnCreateProducerQueue(Message& message);
+
+  std::shared_ptr<ConsumerQueue> consumer_queue_;
+
+  VideoMeshSurface(const VideoMeshSurface&) = delete;
+  void operator=(const VideoMeshSurface&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_VIDEO_MESH_SURFACE_H_
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
new file mode 100644
index 0000000..9163e71
--- /dev/null
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -0,0 +1,118 @@
+#include <dvr/vr_flinger.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <memory>
+
+#include <binder/ProcessState.h>
+#include <log/log.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <private/dvr/display_client.h>
+#include <sys/resource.h>
+
+#include <pdx/default_transport/service_dispatcher.h>
+
+#include <functional>
+
+#include "DisplayHardware/ComposerHal.h"
+#include "display_manager_service.h"
+#include "display_service.h"
+#include "screenshot_service.h"
+#include "vsync_service.h"
+
+namespace android {
+namespace dvr {
+
+VrFlinger::VrFlinger() {}
+
+int VrFlinger::Run(Hwc2::Composer* hidl) {
+  if (!hidl)
+    return EINVAL;
+
+  std::shared_ptr<android::pdx::Service> service;
+
+  ALOGI("Starting up VrFlinger...");
+
+  setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY);
+  set_sched_policy(0, SP_FOREGROUND);
+
+  // We need to be able to create endpoints with full perms.
+  umask(0000);
+
+  android::ProcessState::self()->startThreadPool();
+
+  std::shared_ptr<android::pdx::ServiceDispatcher> dispatcher =
+      android::pdx::default_transport::ServiceDispatcher::Create();
+  CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher.");
+
+  display_service_ = android::dvr::DisplayService::Create(hidl);
+  CHECK_ERROR(!display_service_, error, "Failed to create display service.");
+  dispatcher->AddService(display_service_);
+
+  service = android::dvr::DisplayManagerService::Create(display_service_);
+  CHECK_ERROR(!service, error, "Failed to create display manager service.");
+  dispatcher->AddService(service);
+
+  service = android::dvr::ScreenshotService::Create();
+  CHECK_ERROR(!service, error, "Failed to create screenshot service.");
+  dispatcher->AddService(service);
+
+  service = android::dvr::VSyncService::Create();
+  CHECK_ERROR(!service, error, "Failed to create vsync service.");
+  dispatcher->AddService(service);
+
+  display_service_->SetVSyncCallback(
+      std::bind(&android::dvr::VSyncService::VSyncEvent,
+                std::static_pointer_cast<android::dvr::VSyncService>(service),
+                std::placeholders::_1, std::placeholders::_2,
+                std::placeholders::_3, std::placeholders::_4));
+
+  displayd_thread_ = std::thread([this, dispatcher]() {
+    ALOGI("Entering message loop.");
+
+    int ret = dispatcher->EnterDispatchLoop();
+    if (ret < 0) {
+      ALOGE("Dispatch loop exited because: %s\n", strerror(-ret));
+    }
+  });
+
+  return NO_ERROR;
+
+error:
+  display_service_.reset();
+
+  return -1;
+}
+
+void VrFlinger::EnterVrMode() {
+  if (display_service_) {
+    display_service_->SetActive(true);
+  } else {
+    ALOGE("Failed to enter VR mode : Display service is not started.");
+  }
+}
+
+void VrFlinger::ExitVrMode() {
+  if (display_service_) {
+    display_service_->SetActive(false);
+  } else {
+    ALOGE("Failed to exit VR mode : Display service is not started.");
+  }
+}
+
+void VrFlinger::OnHardwareComposerRefresh() {
+  if (display_service_) {
+    display_service_->OnHardwareComposerRefresh();
+  } else {
+    ALOGE("OnHardwareComposerRefresh failed : Display service is not started.");
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
new file mode 100644
index 0000000..48fa2c2
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.cpp
@@ -0,0 +1,208 @@
+#include "vsync_service.h"
+
+#include <log/log.h>
+#include <hardware/hwcomposer.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <utils/Trace.h>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/display_rpc.h>
+#include <private/dvr/display_types.h>
+
+using android::pdx::Channel;
+using android::pdx::Message;
+using android::pdx::MessageInfo;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+VSyncService::VSyncService()
+    : BASE("VSyncService", Endpoint::Create(DisplayVSyncRPC::kClientPath)),
+      last_vsync_(0),
+      current_vsync_(0),
+      compositor_time_ns_(0),
+      current_vsync_count_(0) {}
+
+VSyncService::~VSyncService() {}
+
+void VSyncService::VSyncEvent(int display, int64_t timestamp_ns,
+                              int64_t compositor_time_ns,
+                              uint32_t vsync_count) {
+  ATRACE_NAME("VSyncService::VSyncEvent");
+  std::lock_guard<std::mutex> autolock(mutex_);
+
+  if (display == HWC_DISPLAY_PRIMARY) {
+    last_vsync_ = current_vsync_;
+    current_vsync_ = timestamp_ns;
+    compositor_time_ns_ = compositor_time_ns;
+    current_vsync_count_ = vsync_count;
+
+    NotifyWaiters();
+    UpdateClients();
+  }
+}
+
+std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) {
+  const MessageInfo& info = message.GetInfo();
+
+  auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid);
+  AddClient(client);
+
+  return client;
+}
+
+void VSyncService::OnChannelClose(pdx::Message& /*message*/,
+                                  const std::shared_ptr<Channel>& channel) {
+  auto client = std::static_pointer_cast<VSyncChannel>(channel);
+  if (!client) {
+    ALOGW("WARNING: VSyncChannel was NULL!!!\n");
+    return;
+  }
+
+  RemoveClient(client);
+}
+
+void VSyncService::AddWaiter(pdx::Message& message) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message));
+  waiters_.push_back(std::move(waiter));
+}
+
+void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  clients_.push_back(client);
+}
+
+void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  clients_.remove(client);
+}
+
+// Private. Assumes mutex is held.
+void VSyncService::NotifyWaiters() {
+  ATRACE_NAME("VSyncService::NotifyWaiters");
+  auto first = waiters_.begin();
+  auto last = waiters_.end();
+
+  while (first != last) {
+    (*first)->Notify(current_vsync_);
+    waiters_.erase(first++);
+  }
+}
+
+// Private. Assumes mutex is held.
+void VSyncService::UpdateClients() {
+  ATRACE_NAME("VSyncService::UpdateClients");
+  auto first = clients_.begin();
+  auto last = clients_.end();
+
+  while (first != last) {
+    (*first)->Signal();
+    first++;
+  }
+}
+
+int VSyncService::HandleMessage(pdx::Message& message) {
+  switch (message.GetOp()) {
+    case DisplayVSyncRPC::Wait::Opcode:
+      AddWaiter(message);
+      return 0;
+
+    case DisplayVSyncRPC::GetLastTimestamp::Opcode:
+      DispatchRemoteMethod<DisplayVSyncRPC::GetLastTimestamp>(
+          *this, &VSyncService::OnGetLastTimestamp, message);
+      return 0;
+
+    case DisplayVSyncRPC::GetSchedInfo::Opcode:
+      DispatchRemoteMethod<DisplayVSyncRPC::GetSchedInfo>(
+          *this, &VSyncService::OnGetSchedInfo, message);
+      return 0;
+
+    case DisplayVSyncRPC::Acknowledge::Opcode:
+      DispatchRemoteMethod<DisplayVSyncRPC::Acknowledge>(
+          *this, &VSyncService::OnAcknowledge, message);
+      return 0;
+
+    default:
+      return Service::HandleMessage(message);
+  }
+}
+
+int64_t VSyncService::OnGetLastTimestamp(pdx::Message& message) {
+  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+  std::lock_guard<std::mutex> autolock(mutex_);
+
+  // Getting the timestamp has the side effect of ACKing.
+  client->Ack();
+  return current_vsync_;
+}
+
+VSyncSchedInfo VSyncService::OnGetSchedInfo(pdx::Message& message) {
+  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+  std::lock_guard<std::mutex> autolock(mutex_);
+
+  // Getting the timestamp has the side effect of ACKing.
+  client->Ack();
+
+  uint32_t next_vsync_count = current_vsync_count_ + 1;
+  int64_t current_time = GetSystemClockNs();
+  int64_t vsync_period_ns = 0;
+  int64_t next_warp;
+  if (current_vsync_ == 0 || last_vsync_ == 0) {
+    // Handle startup when current_vsync_ or last_vsync_ are 0.
+    // Normally should not happen because vsync_service is running before
+    // applications, but in case it does a sane time prevents applications
+    // from malfunctioning.
+    vsync_period_ns = 20000000;
+    next_warp = current_time;
+  } else {
+    // TODO(jbates) When we have an accurate reading of the true vsync
+    // period, use that instead of this estimated value.
+    vsync_period_ns = current_vsync_ - last_vsync_;
+    // Clamp the period, because when there are no surfaces the last_vsync_
+    // value will get stale. Note this is temporary and goes away as soon
+    // as we have an accurate vsync period reported by the system.
+    vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000));
+    next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_;
+    // If the request missed the present window, move up to the next vsync.
+    if (current_time > next_warp) {
+      next_warp += vsync_period_ns;
+      ++next_vsync_count;
+    }
+  }
+
+  return {vsync_period_ns, next_warp, next_vsync_count};
+}
+
+int VSyncService::OnAcknowledge(pdx::Message& message) {
+  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
+  std::lock_guard<std::mutex> autolock(mutex_);
+  client->Ack();
+  return 0;
+}
+
+void VSyncWaiter::Notify(int64_t timestamp) {
+  timestamp_ = timestamp;
+  DispatchRemoteMethod<DisplayVSyncRPC::Wait>(*this, &VSyncWaiter::OnWait,
+                                              message_);
+}
+
+int64_t VSyncWaiter::OnWait(pdx::Message& /*message*/) { return timestamp_; }
+
+void VSyncChannel::Ack() {
+  ALOGD_IF(TRACE, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_);
+  service_.ModifyChannelEvents(cid_, POLLPRI, 0);
+}
+
+void VSyncChannel::Signal() {
+  ALOGD_IF(TRACE, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_);
+  service_.ModifyChannelEvents(cid_, 0, POLLPRI);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h
new file mode 100644
index 0000000..ba1d4df
--- /dev/null
+++ b/libs/vr/libvrflinger/vsync_service.h
@@ -0,0 +1,107 @@
+#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
+#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
+
+#include <pdx/service.h>
+
+#include <list>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "display_service.h"
+
+namespace android {
+namespace dvr {
+
+// VSyncWaiter encapsulates a client blocked waiting for the next vsync.
+// It is used to enqueue the Message to reply to when the next vsync event
+// occurs.
+class VSyncWaiter {
+ public:
+  explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {}
+
+  void Notify(int64_t timestamp);
+
+ private:
+  int64_t OnWait(pdx::Message& message);
+
+  pdx::Message message_;
+  int64_t timestamp_ = 0;
+
+  VSyncWaiter(const VSyncWaiter&) = delete;
+  void operator=(const VSyncWaiter&) = delete;
+};
+
+// VSyncChannel manages the service-side per-client context for each client
+// using the service.
+class VSyncChannel : public pdx::Channel {
+ public:
+  VSyncChannel(pdx::Service& service, int pid, int cid)
+      : service_(service), pid_(pid), cid_(cid) {}
+
+  void Ack();
+  void Signal();
+
+ private:
+  pdx::Service& service_;
+  pid_t pid_;
+  int cid_;
+
+  VSyncChannel(const VSyncChannel&) = delete;
+  void operator=(const VSyncChannel&) = delete;
+};
+
+// VSyncService implements the displayd vsync service over ServiceFS.
+class VSyncService : public pdx::ServiceBase<VSyncService> {
+ public:
+  ~VSyncService() override;
+
+  int HandleMessage(pdx::Message& message) override;
+
+  std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
+  void OnChannelClose(pdx::Message& message,
+                      const std::shared_ptr<pdx::Channel>& channel) override;
+
+  // Called by the hardware composer HAL, or similar,
+  // whenever a vsync event occurs.
+  // |compositor_time_ns| is the number of ns before the next vsync when the
+  // compositor will preempt the GPU to do EDS and lens warp.
+  void VSyncEvent(int display, int64_t timestamp_ns, int64_t compositor_time_ns,
+                  uint32_t vsync_count);
+
+ private:
+  friend BASE;
+
+  VSyncService();
+
+  int64_t OnGetLastTimestamp(pdx::Message& message);
+  VSyncSchedInfo OnGetSchedInfo(pdx::Message& message);
+  int OnAcknowledge(pdx::Message& message);
+
+  void NotifierThreadFunction();
+
+  void AddWaiter(pdx::Message& message);
+  void NotifyWaiters();
+  void UpdateClients();
+
+  void AddClient(const std::shared_ptr<VSyncChannel>& client);
+  void RemoveClient(const std::shared_ptr<VSyncChannel>& client);
+
+  int64_t last_vsync_;
+  int64_t current_vsync_;
+  int64_t compositor_time_ns_;
+  uint32_t current_vsync_count_;
+
+  std::mutex mutex_;
+
+  std::list<std::unique_ptr<VSyncWaiter>> waiters_;
+  std::list<std::shared_ptr<VSyncChannel>> clients_;
+
+  VSyncService(const VSyncService&) = delete;
+  void operator=(VSyncService&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 8b48032..740ead3 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
@@ -618,6 +619,16 @@
 #endif
 #endif
 
+#ifndef EGL_ANDROID_get_native_client_buffer
+#define EGL_ANDROID_get_native_client_buffer 1
+struct AHardwareBuffer;
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLClientBuffer eglGetNativeClientBufferANDROID (const struct AHardwareBuffer *buffer);
+#else
+typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLGETNATIVECLIENTBUFFERANDROID) (const struct AHardwareBuffer *buffer);
+#endif
+#endif
+
 #ifndef EGL_ANDROID_front_buffer_auto_refresh
 #define EGL_ANDROID_front_buffer_auto_refresh 1
 #define EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID 0x314C
@@ -631,21 +642,41 @@
 #ifndef EGL_ANDROID_get_frame_timestamps
 #define EGL_ANDROID_get_frame_timestamps 1
 #define EGL_TIMESTAMPS_ANDROID 0x314D
-#define EGL_QUEUE_TIME_ANDROID 0x314E
-#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
-#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430
-#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
-#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
-#define EGL_READS_DONE_TIME_ANDROID 0x3433
+#define EGL_COMPOSITE_DEADLINE_ANDROID 0x314E
+#define EGL_COMPOSITE_INTERVAL_ANDROID 0x314F
+#define EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3150
+#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3151
+#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3152
+#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3153
+#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3154
+#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3155
+#define EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID 0x3156
+#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3157
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3158
+#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3159
+#define EGL_READS_DONE_TIME_ANDROID 0x315A
 #ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
-EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
+EGLAPI EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId);
+EGLAPI EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values);
+EGLAPI EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name);
+EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+EGLAPI EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
 #else
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETNEXTFRAMEIDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCOMPOSITORTIMINGANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCOMPOSITORTIMINGSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint name);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
 #endif
 #endif
 
+#ifndef EGL_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/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 48bf676..04f6d6d 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -33,6 +33,7 @@
 #include <ui/ANativeObjectBase.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBufferMapper.h>
+#include <ui/Rect.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 60c4b36..4fa6a33 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -80,6 +80,7 @@
         "libbinder",
         "libutils",
         "libui",
+        "libnativewindow",
     ],
 }
 
@@ -108,8 +109,6 @@
     ],
     static_libs: ["libEGL_getProcAddress"],
     ldflags: ["-Wl,--exclude-libs=ALL"],
-
-    required: ["egl.cfg"],
 }
 
 cc_defaults {
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 21e76f5..2f42ab6 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -1,12 +1 @@
 LOCAL_PATH:= $(call my-dir)
-
-# OpenGL drivers config file
-ifneq ($(BOARD_EGL_CFG),)
-include $(CLEAR_VARS)
-LOCAL_MODULE := egl.cfg
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/egl
-LOCAL_SRC_FILES := ../../../../$(BOARD_EGL_CFG)
-include $(BUILD_PREBUILT)
-endif
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 69e3c13..9aedc61 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -17,25 +17,21 @@
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <array>
-#include <ctype.h>
+#include "Loader.h"
+
 #include <dirent.h>
 #include <dlfcn.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 
 #include <android/dlext.h>
 #include <cutils/properties.h>
 #include <log/log.h>
+
+#include <utils/String8.h>
 #include <utils/Trace.h>
 
-#include <EGL/egl.h>
+#include <ui/GraphicsEnv.h>
 
 #include "egldefs.h"
-#include "Loader.h"
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -102,24 +98,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);
@@ -165,26 +143,11 @@
 // ----------------------------------------------------------------------------
 
 Loader::Loader()
-    : getProcAddress(NULL),
-      mLibGui(nullptr),
-      mGetDriverNamespace(nullptr)
+    : getProcAddress(NULL)
 {
-    // FIXME: See note in GraphicsEnv.h about android_getDriverNamespace().
-    // libgui should already be loaded in any process that uses libEGL, but
-    // if for some reason it isn't, then we're not going to get a driver
-    // namespace anyway, so don't force it to be loaded.
-    mLibGui = dlopen("libgui.so", RTLD_NOLOAD | RTLD_LOCAL | RTLD_LAZY);
-    if (!mLibGui) {
-        ALOGD("failed to load libgui: %s", dlerror());
-        return;
-    }
-    mGetDriverNamespace = reinterpret_cast<decltype(mGetDriverNamespace)>(
-            dlsym(mLibGui, "android_getDriverNamespace"));
 }
 
 Loader::~Loader() {
-    if (mLibGui)
-        dlclose(mLibGui);
 }
 
 static void* load_wrapper(const char* path) {
@@ -469,10 +432,10 @@
     return android_dlopen_ext(path, mode, info);
 }
 
-static const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
+static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
     "ro.hardware.egl",
     "ro.board.platform",
-}};
+};
 
 static void* load_updated_driver(const char* kind, android_namespace_t* ns) {
     ATRACE_CALL();
@@ -486,10 +449,10 @@
         if (property_get(key, prop, nullptr) > 0) {
             String8 name;
             name.appendFormat("lib%s_%s.so", kind, prop);
-            so = do_android_dlopen_ext(name.string(), RTLD_LOCAL | RTLD_NOW,
-                    &dlextinfo);
-            if (so)
+            so = do_android_dlopen_ext(name.string(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
+            if (so) {
                 return so;
+            }
         }
     }
     return nullptr;
@@ -501,11 +464,9 @@
     ATRACE_CALL();
 
     void* dso = nullptr;
-    if (mGetDriverNamespace) {
-        android_namespace_t* ns = mGetDriverNamespace();
-        if (ns) {
-            dso = load_updated_driver(kind, ns);
-        }
+    android_namespace_t* ns = android_getDriverNamespace();
+    if (ns) {
+        dso = load_updated_driver(kind, ns);
     }
     if (!dso) {
         dso = load_system_driver(kind);
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index d0435e7..2dd4206 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -17,15 +17,10 @@
 #ifndef ANDROID_EGL_LOADER_H
 #define ANDROID_EGL_LOADER_H
 
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
+#include <stdint.h>
 
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
-#include <utils/String8.h>
-
-#include <gui/GraphicsEnv.h>
 
 #include <EGL/egl.h>
 
@@ -35,12 +30,10 @@
 
 struct egl_connection_t;
 
-class Loader : public Singleton<Loader>
-{
+class Loader : public Singleton<Loader> {
     friend class Singleton<Loader>;
 
-    typedef __eglMustCastToProperFunctionPointerType (*getProcAddressType)(
-            const char*);
+    typedef __eglMustCastToProperFunctionPointerType (* getProcAddressType)(const char*);
    
     enum {
         EGL         = 0x01,
@@ -56,9 +49,6 @@
     
     getProcAddressType getProcAddress;
 
-    void* mLibGui;
-    decltype(android_getDriverNamespace)* mGetDriverNamespace;
-
 public:
     ~Loader();
     
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index ee83ada..34b0ba2 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,30 +14,26 @@
  ** limitations under the License.
  */
 
-#include <ctype.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include <hardware/gralloc.h>
-#include <system/window.h>
 
 #include <EGL/egl.h>
-#include <EGL/eglext.h>
 
 #include <cutils/atomic.h>
 #include <cutils/properties.h>
+
 #include <log/log.h>
+
 #include <utils/CallStack.h>
-#include <utils/String8.h>
 
 #include "../egl_impl.h"
 
-#include "egl_tls.h"
 #include "egldefs.h"
-#include "Loader.h"
-
+#include "egl_tls.h"
 #include "egl_display.h"
 #include "egl_object.h"
+#include "Loader.h"
 
 typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
 
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index f8e25b4..e52713f 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -21,15 +21,15 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <hardware/gralloc.h>
-#include <system/window.h>
+#include <hardware/gralloc1.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <cutils/atomic.h>
+#include <android/hardware_buffer.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
 #include <cutils/compiler.h>
-#include <cutils/memory.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 
@@ -37,27 +37,24 @@
 
 #include <ui/GraphicBuffer.h>
 
+
 #include <utils/KeyedVector.h>
-#include <utils/SortedVector.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
+#include <utils/Thread.h>
 
 #include "binder/Binder.h"
 #include "binder/Parcel.h"
 #include "binder/IServiceManager.h"
 
 #include "../egl_impl.h"
-#include "../hooks.h"
 
 #include "egl_display.h"
 #include "egl_object.h"
 #include "egl_tls.h"
-#include "egldefs.h"
 
 using namespace android;
 
-#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0
-
 // ----------------------------------------------------------------------------
 
 namespace android {
@@ -82,17 +79,21 @@
  *
  * NOTE: Both strings MUST have a single space as the last character.
  */
-extern char const * const gBuiltinExtensionString =
+
+extern char const * const gBuiltinExtensionString;
+extern char const * const gExtensionString;
+
+char const * const gBuiltinExtensionString =
         "EGL_KHR_get_all_proc_addresses "
         "EGL_ANDROID_presentation_time "
         "EGL_KHR_swap_buffers_with_damage "
         "EGL_ANDROID_create_native_client_buffer "
+        "EGL_ANDROID_get_native_client_buffer "
         "EGL_ANDROID_front_buffer_auto_refresh "
-#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
         "EGL_ANDROID_get_frame_timestamps "
-#endif
         ;
-extern char const * const gExtensionString  =
+
+char const * const gExtensionString  =
         "EGL_KHR_image "                        // mandatory
         "EGL_KHR_image_base "                   // mandatory
         "EGL_KHR_image_pixmap "
@@ -118,12 +119,14 @@
         "EGL_KHR_wait_sync "                    // strongly recommended
         "EGL_ANDROID_recordable "               // mandatory
         "EGL_KHR_partial_update "               // strongly recommended
+        "EGL_EXT_pixel_format_float "
         "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
         "EGL_KHR_create_context_no_error "
         "EGL_KHR_mutable_render_buffer "
         "EGL_EXT_yuv_surface "
         "EGL_EXT_protected_content "
         "EGL_IMG_context_priority "
+        "EGL_KHR_no_config_context "
         ;
 
 // extensions not exposed to applications but used by the ANDROID system
@@ -181,10 +184,14 @@
     { "eglSwapBuffersWithDamageKHR",
             (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
 
-    // EGL_ANDROID_native_client_buffer
+    // EGL_ANDROID_create_native_client_buffer
     { "eglCreateNativeClientBufferANDROID",
             (__eglMustCastToProperFunctionPointerType)&eglCreateNativeClientBufferANDROID },
 
+    // EGL_ANDROID_get_native_client_buffer
+    { "eglGetNativeClientBufferANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+
     // EGL_KHR_partial_update
     { "eglSetDamageRegionKHR",
             (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
@@ -215,10 +222,16 @@
             (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
 
     // EGL_ANDROID_get_frame_timestamps
+    { "eglGetNextFrameIdANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+    { "eglGetCompositorTimingANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+    { "eglGetCompositorTimingSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
     { "eglGetFrameTimestampsANDROID",
             (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
-    { "eglQueryTimestampSupportedANDROID",
-            (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID },
+    { "eglGetFrameTimestampSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
 };
 
 /*
@@ -292,7 +305,7 @@
     clearError();
 
     egl_display_ptr dp = get_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res = dp->initialize(major, minor);
 
@@ -308,7 +321,7 @@
     clearError();
 
     egl_display_ptr dp = get_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res = dp->terminate();
 
@@ -329,7 +342,7 @@
     if (!dp) return EGL_FALSE;
 
     if (num_config==0) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
     EGLBoolean res = EGL_FALSE;
@@ -354,7 +367,7 @@
     if (!dp) return EGL_FALSE;
 
     if (num_config==0) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
     EGLBoolean res = EGL_FALSE;
@@ -386,6 +399,8 @@
                         case EGL_CONFIG_CAVEAT:
                             attribCaveat = &attrib_list[attribCount];
                             break;
+                        default:
+                            break;
                     }
                     attribCount++;
                 }
@@ -477,26 +492,44 @@
         // modify the EGLconfig's format before setting the native window's
         // format.
 
-        // by default, just pick RGBA_8888
-        EGLint format = HAL_PIXEL_FORMAT_RGBA_8888;
-        android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN;
+        EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_COLOR_COMPONENT_TYPE_EXT,
+                                    &componentType);
 
+        EGLint format;
+        android_dataspace dataSpace = HAL_DATASPACE_UNKNOWN;
         EGLint a = 0;
+        EGLint r, g, b;
+        r = g = b = 0;
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_RED_SIZE,   &r);
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_GREEN_SIZE, &g);
+        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_BLUE_SIZE,  &b);
         cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_ALPHA_SIZE, &a);
-        if (a > 0) {
-            // alpha-channel requested, there's really only one suitable format
-            format = HAL_PIXEL_FORMAT_RGBA_8888;
-        } else {
-            EGLint r, g, b;
-            r = g = b = 0;
-            cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_RED_SIZE,   &r);
-            cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_GREEN_SIZE, &g);
-            cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_BLUE_SIZE,  &b);
-            EGLint colorDepth = r + g + b;
+        EGLint colorDepth = r + g + b;
+
+        if (a == 0) {
             if (colorDepth <= 16) {
                 format = HAL_PIXEL_FORMAT_RGB_565;
             } else {
-                format = HAL_PIXEL_FORMAT_RGBX_8888;
+                if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+                    if (colorDepth > 24) {
+                        format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                    } else {
+                        format = HAL_PIXEL_FORMAT_RGBX_8888;
+                    }
+                } else {
+                    format = HAL_PIXEL_FORMAT_RGBA_FP16;
+                }
+            }
+        } else {
+            if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+                if (colorDepth > 24) {
+                    format = HAL_PIXEL_FORMAT_RGBA_1010102;
+                } else {
+                    format = HAL_PIXEL_FORMAT_RGBA_8888;
+                }
+            } else {
+                format = HAL_PIXEL_FORMAT_RGBA_FP16;
             }
         }
 
@@ -597,7 +630,7 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t * const s = get_surface(surface);
     EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
@@ -617,7 +650,7 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     return s->cnx->egl.eglQuerySurface(
@@ -636,7 +669,6 @@
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
         setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return;
     }
 }
 
@@ -695,7 +727,7 @@
 
     ContextRef _c(dp.get(), ctx);
     if (!_c.get())
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     egl_context_t * const c = get_context(ctx);
     EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
@@ -711,14 +743,14 @@
     clearError();
 
     egl_display_ptr dp = validate_display(dpy);
-    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 
     // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
     // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
     // a valid but uninitialized display.
     if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
          (draw != EGL_NO_SURFACE) ) {
-        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
+        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
     }
 
     // get a reference to the object passed in
@@ -729,7 +761,7 @@
     // validate the context (if not EGL_NO_CONTEXT)
     if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
         // EGL_NO_CONTEXT is valid
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
     }
 
     // these are the underlying implementation's object
@@ -752,7 +784,7 @@
         // no context given, use the implementation of the current context
         if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
             // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
-            return setError(EGL_BAD_MATCH, EGL_FALSE);
+            return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
         }
         if (cur_c == NULL) {
             // no current context
@@ -763,14 +795,14 @@
 
     // retrieve the underlying implementation's draw EGLSurface
     if (draw != EGL_NO_SURFACE) {
-        if (!_d.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
         d = get_surface(draw);
         impl_draw = d->surface;
     }
 
     // retrieve the underlying implementation's read EGLSurface
     if (read != EGL_NO_SURFACE) {
-        if (!_r.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
         r = get_surface(read);
         impl_read = r->surface;
     }
@@ -794,7 +826,7 @@
     } else {
         // this will ALOGE the error
         egl_connection_t* const cnx = &gEGLImpl;
-        result = setError(cnx->egl.eglGetError(), EGL_FALSE);
+        result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
     }
     return result;
 }
@@ -809,7 +841,7 @@
     if (!dp) return EGL_FALSE;
 
     ContextRef _c(dp.get(), ctx);
-    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     egl_context_t * const c = get_context(ctx);
     return c->cnx->egl.eglQueryContext(
@@ -870,7 +902,7 @@
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     return cnx->egl.eglWaitGL();
 }
@@ -881,7 +913,7 @@
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     return cnx->egl.eglWaitNative(engine);
 }
@@ -1009,7 +1041,7 @@
             thread->mQueue.push_back(sync);
             thread->mCondition.signal();
             thread->mFramesQueued++;
-            ATRACE_INT("GPU Frames Outstanding", thread->mQueue.size());
+            ATRACE_INT("GPU Frames Outstanding", int32_t(thread->mQueue.size()));
         }
     }
 
@@ -1043,7 +1075,7 @@
             Mutex::Autolock lock(mMutex);
             mQueue.removeAt(0);
             mFramesCompleted++;
-            ATRACE_INT("GPU Frames Outstanding", mQueue.size());
+            ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
         }
         return true;
     }
@@ -1066,7 +1098,7 @@
 
     SurfaceRef _s(dp.get(), draw);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(draw);
 
@@ -1131,7 +1163,7 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
@@ -1141,6 +1173,16 @@
 {
     clearError();
 
+    // Generate an error quietly when client extensions (as defined by
+    // EGL_EXT_client_extensions) are queried.  We do not want to rely on
+    // validate_display to generate the error as validate_display would log
+    // the error, which can be misleading.
+    //
+    // If we want to support EGL_EXT_client_extensions later, we can return
+    // the client extension string here instead.
+    if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS)
+        return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)0);
+
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return (const char *) NULL;
 
@@ -1153,6 +1195,8 @@
             return dp->getExtensionString();
         case EGL_CLIENT_APIS:
             return dp->getClientApiString();
+        default:
+            break;
     }
     return setError(EGL_BAD_PARAMETER, (const char *)0);
 }
@@ -1173,6 +1217,8 @@
             return dp->disp.queryString.extensions;
         case EGL_CLIENT_APIS:
             return dp->disp.queryString.clientApi;
+        default:
+            break;
     }
     return setError(EGL_BAD_PARAMETER, (const char *)0);
 }
@@ -1191,29 +1237,31 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t * const s = get_surface(surface);
 
     if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
-        int err = native_window_set_auto_refresh(s->win.get(),
-            value ? true : false);
-        return (err == NO_ERROR) ? EGL_TRUE :
+        if (!s->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 : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
     if (attribute == EGL_TIMESTAMPS_ANDROID) {
-        s->enableTimestamps = value;
-        return EGL_TRUE;
+        if (!s->win.get()) {
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)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, (EGLBoolean)EGL_FALSE);
     }
-#endif
 
     if (s->cnx->egl.eglSurfaceAttrib) {
         return s->cnx->egl.eglSurfaceAttrib(
                 dp->disp.dpy, s->surface, attribute, value);
     }
-    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
 EGLBoolean eglBindTexImage(
@@ -1226,14 +1274,14 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglBindTexImage) {
         return s->cnx->egl.eglBindTexImage(
                 dp->disp.dpy, s->surface, buffer);
     }
-    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
 EGLBoolean eglReleaseTexImage(
@@ -1246,14 +1294,14 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglReleaseTexImage) {
         return s->cnx->egl.eglReleaseTexImage(
                 dp->disp.dpy, s->surface, buffer);
     }
-    return setError(EGL_BAD_SURFACE, EGL_FALSE);
+    return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 }
 
 EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
@@ -1283,7 +1331,7 @@
 
     egl_connection_t* const cnx = &gEGLImpl;
     if (!cnx->dso)
-        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+        return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
 
     EGLBoolean res;
     if (cnx->egl.eglWaitClient) {
@@ -1299,7 +1347,7 @@
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
     // bind this API on all EGLs
@@ -1316,7 +1364,7 @@
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
     }
 
     egl_connection_t* const cnx = &gEGLImpl;
@@ -1374,14 +1422,14 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglLockSurfaceKHR) {
         return s->cnx->egl.eglLockSurfaceKHR(
                 dp->disp.dpy, s->surface, attrib_list);
     }
-    return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 }
 
 EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
@@ -1393,13 +1441,13 @@
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
     if (s->cnx->egl.eglUnlockSurfaceKHR) {
         return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
     }
-    return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
 }
 
 EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
@@ -1497,7 +1545,7 @@
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    EGLBoolean result = EGL_FALSE;
+    EGLint result = EGL_FALSE;
     egl_connection_t* const cnx = &gEGLImpl;
     if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
         result = cnx->egl.eglClientWaitSyncKHR(
@@ -1790,10 +1838,12 @@
 {
     clearError();
 
-    int usage = 0;
+    uint64_t producerUsage = 0;
+    uint64_t consumerUsage = 0;
     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,15 +1869,16 @@
                 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;
+                        producerUsage |= GRALLOC1_PRODUCER_USAGE_PROTECTED;
                     }
                     if (value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) {
-                        usage |= GRALLOC_USAGE_HW_RENDER;
+                        producerUsage |= GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET;
                     }
                     if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID) {
-                        usage |= GRALLOC_USAGE_HW_TEXTURE;
+                        consumerUsage |= GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE;
                     }
                     break;
                 default:
@@ -1848,7 +1899,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,8 +1944,12 @@
     CHECK_ERROR_CONDITION("Unable to write height");
     err = data.writeInt32(static_cast<int32_t>(format));
     CHECK_ERROR_CONDITION("Unable to write format");
-    err = data.writeUint32(usage);
-    CHECK_ERROR_CONDITION("Unable to write usage");
+    err = data.writeUint32(layer_count);
+    CHECK_ERROR_CONDITION("Unable to write layer count");
+    err = data.writeUint64(producerUsage);
+    CHECK_ERROR_CONDITION("Unable to write producer usage");
+    err = data.writeUint64(consumerUsage);
+    CHECK_ERROR_CONDITION("Unable to write consumer usage");
     err = data.writeUtf8AsUtf16(
             std::string("[eglCreateNativeClientBufferANDROID pid ") +
             std::to_string(getpid()) + ']');
@@ -1910,12 +1965,16 @@
 
     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, pu=%" PRIx64 " cu=%" PRIx64 ", lc=%u} %#x",
+                width, height, format, producerUsage, consumerUsage,
+                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, pu=%" PRIx64
+          " cu=%" PRIx64 ", lc=%u}",
+            gBuffer, width, height, format, producerUsage, consumerUsage,
+            layer_count);
     return static_cast<EGLClientBuffer>(gBuffer->getNativeBuffer());
 
 #undef CHECK_ERROR_CONDITION
@@ -1926,6 +1985,15 @@
     return setError(EGL_BAD_ALLOC, (EGLClientBuffer)0);
 }
 
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
+    clearError();
+
+    if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
+
+    const GraphicBuffer* graphicBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    return static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
+}
+
 // ----------------------------------------------------------------------------
 // NVIDIA extensions
 // ----------------------------------------------------------------------------
@@ -1934,7 +2002,7 @@
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
     }
 
     EGLuint64NV ret = 0;
@@ -1944,7 +2012,7 @@
         return cnx->egl.eglGetSystemTimeFrequencyNV();
     }
 
-    return setErrorQuiet(EGL_BAD_DISPLAY, 0);
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
 }
 
 EGLuint64NV eglGetSystemTimeNV()
@@ -1952,7 +2020,7 @@
     clearError();
 
     if (egl_init_drivers() == EGL_FALSE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+        return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
     }
 
     EGLuint64NV ret = 0;
@@ -1962,7 +2030,7 @@
         return cnx->egl.eglGetSystemTimeNV();
     }
 
-    return setErrorQuiet(EGL_BAD_DISPLAY, 0);
+    return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
 }
 
 // ----------------------------------------------------------------------------
@@ -1994,103 +2062,266 @@
     return EGL_FALSE;
 }
 
+EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR *frameId) {
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->win.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    uint64_t nextFrameId = 0;
+    status_t ret = native_window_get_next_frame_id(s->win.get(), &nextFrameId);
+
+    if (ret != NO_ERROR) {
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        ALOGE("eglGetNextFrameId: Unexpected error.");
+        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+
+    *frameId = nextFrameId;
+    return EGL_TRUE;
+}
+
+EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface,
+        EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
+{
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->win.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    nsecs_t* compositeDeadline = nullptr;
+    nsecs_t* compositeInterval = nullptr;
+    nsecs_t* compositeToPresentLatency = nullptr;
+
+    for (int i = 0; i < numTimestamps; i++) {
+        switch (names[i]) {
+            case EGL_COMPOSITE_DEADLINE_ANDROID:
+                compositeDeadline = &values[i];
+                break;
+            case EGL_COMPOSITE_INTERVAL_ANDROID:
+                compositeInterval = &values[i];
+                break;
+            case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+                compositeToPresentLatency = &values[i];
+                break;
+            default:
+                return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        }
+    }
+
+    status_t ret = native_window_get_compositor_timing(s->win.get(),
+            compositeDeadline, compositeInterval, compositeToPresentLatency);
+
+    switch (ret) {
+      case NO_ERROR:
+        return EGL_TRUE;
+      case INVALID_OPERATION:
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+      default:
+        // This should not happen. Return an error that is not in the spec
+        // so it's obvious something is very wrong.
+        ALOGE("eglGetCompositorTiming: Unexpected error.");
+        return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+    }
+}
+
+EGLBoolean eglGetCompositorTimingSupportedANDROID(
+        EGLDisplay dpy, EGLSurface surface, EGLint name)
+{
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->win.get();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    switch (name) {
+        case EGL_COMPOSITE_DEADLINE_ANDROID:
+        case EGL_COMPOSITE_INTERVAL_ANDROID:
+        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+            return EGL_TRUE;
+        default:
+            return EGL_FALSE;
+    }
+}
+
 EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
+        EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
         EGLnsecsANDROID *values)
 {
     clearError();
 
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) {
-        setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
     egl_surface_t const * const s = get_surface(surface);
 
-    if (!s->enableTimestamps) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
+    if (!s->win.get()) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
-    nsecs_t* postedTime = nullptr;
+    nsecs_t* requestedPresentTime = nullptr;
     nsecs_t* acquireTime = nullptr;
-    nsecs_t* refreshStartTime = nullptr;
-    nsecs_t* GLCompositionDoneTime = nullptr;
+    nsecs_t* latchTime = nullptr;
+    nsecs_t* firstRefreshStartTime = nullptr;
+    nsecs_t* gpuCompositionDoneTime = 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:
-                GLCompositionDoneTime = &values[i];
+            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_GPU_FINISHED_TIME_ANDROID:
+                gpuCompositionDoneTime = &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, (EGLBoolean)EGL_FALSE);
         }
     }
 
-    status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
-            postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime,
-            displayRetireTime, releaseTime);
+    status_t ret = native_window_get_frame_timestamps(s->win.get(), frameId,
+            requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+            lastRefreshStartTime, gpuCompositionDoneTime, 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, (EGLBoolean)EGL_FALSE);
+        case INVALID_OPERATION:
+            return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+        case BAD_VALUE:
+            return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+        default:
+            // This should not happen. Return an error that is not in the spec
+            // so it's obvious something is very wrong.
+            ALOGE("eglGetFrameTimestamps: Unexpected error.");
+            return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
     }
-
-    return EGL_TRUE;
 }
 
-EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
-        EGLint timestamp)
+EGLBoolean eglGetFrameTimestampSupportedANDROID(
+        EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
 {
     clearError();
 
     const egl_display_ptr dp = validate_display(dpy);
     if (!dp) {
-        setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
     }
 
     SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
-        setError(EGL_BAD_SURFACE, EGL_FALSE);
-        return EGL_FALSE;
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    ANativeWindow* window = s->win.get();
+    if (!window) {
+        return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
     }
 
     switch (timestamp) {
-#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
-        case EGL_QUEUE_TIME_ANDROID:
+        case EGL_COMPOSITE_DEADLINE_ANDROID:
+        case EGL_COMPOSITE_INTERVAL_ANDROID:
+        case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+        case EGL_REQUESTED_PRESENT_TIME_ANDROID:
         case EGL_RENDERING_COMPLETE_TIME_ANDROID:
-        case EGL_COMPOSITION_START_TIME_ANDROID:
-        case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
-        case EGL_DISPLAY_RETIRE_TIME_ANDROID:
+        case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+        case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+        case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+        case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+        case EGL_DEQUEUE_READY_TIME_ANDROID:
         case EGL_READS_DONE_TIME_ANDROID:
             return EGL_TRUE;
-#endif
+        case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
+            int value = 0;
+            window->query(window, NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+            return value == 0 ? EGL_FALSE : EGL_TRUE;
+        }
+        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;
+        }
         default:
             return EGL_FALSE;
     }
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 1fe322d..e46716c 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -14,18 +14,17 @@
  ** limitations under the License.
  */
 
+#include "egl_cache.h"
+
 #include "../egl_impl.h"
 
-#include "egl_cache.h"
 #include "egl_display.h"
-#include "egldefs.h"
 
-#include <fcntl.h>
 #include <inttypes.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
+
+#include <utils/Thread.h>
 
 // Cache size limits.
 static const size_t maxKeySize = 12 * 1024;
@@ -87,7 +86,7 @@
         bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1);
         bool atEnd = (bcExtLen+1) < extsLen &&
                 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1));
-        bool inMiddle = strstr(exts, " " BC_EXT_STR " ");
+        bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr;
         if (equal || atStart || atEnd || inMiddle) {
             PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID;
             eglSetBlobCacheFuncsANDROID =
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 8760009..e7f1712 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -21,6 +21,7 @@
 #include <EGL/eglext.h>
 
 #include <utils/BlobCache.h>
+#include <utils/Mutex.h>
 #include <utils/String8.h>
 #include <utils/StrongPointer.h>
 
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index d7df40c..c804fa7 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -17,16 +17,16 @@
 #define __STDC_LIMIT_MACROS 1
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <string.h>
+#include "egl_display.h"
 
 #include "../egl_impl.h"
 
 #include "egl_cache.h"
-#include "egl_display.h"
 #include "egl_object.h"
 #include "egl_tls.h"
 #include "Loader.h"
 #include <cutils/properties.h>
+
 #include <utils/Trace.h>
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index e17558c..f5e7294 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -18,16 +18,16 @@
 #define ANDROID_EGL_DISPLAY_H
 
 
-#include <ctype.h>
 #include <stdint.h>
-#include <stdlib.h>
+#include <stddef.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
 #include <cutils/compiler.h>
 #include <utils/SortedVector.h>
-#include <utils/threads.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
 #include <utils/String8.h>
 
 #include "egldefs.h"
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 2b56718..b587a16 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -81,6 +81,7 @@
 EGL_ENTRY(EGLClientBuffer, eglGetRenderBufferANDROID, EGLDisplay, EGLSurface)
 EGL_ENTRY(EGLint, eglDupNativeFenceFDANDROID, EGLDisplay, EGLSyncKHR)
 EGL_ENTRY(EGLClientBuffer, eglCreateNativeClientBufferANDROID, const EGLint *)
+EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffer *)
 
 /* NVIDIA extensions */
 
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 6a76737..b553d71 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -14,19 +14,10 @@
  ** limitations under the License.
  */
 
-#include <string>
+#include "egl_object.h"
+
 #include <sstream>
 
-#include <ctype.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <utils/threads.h>
-
-#include "egl_object.h"
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -68,7 +59,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..f4012ab 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -17,15 +17,13 @@
 #ifndef ANDROID_EGL_OBJECT_H
 #define ANDROID_EGL_OBJECT_H
 
-#include <atomic>
-#include <ctype.h>
 #include <stdint.h>
-#include <stdlib.h>
+#include <stddef.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <utils/threads.h>
+#include <utils/StrongPointer.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
@@ -62,8 +60,8 @@
     template <typename N, typename T>
     class LocalRef {
         egl_object_t* ref;
-        LocalRef();
-        explicit LocalRef(const LocalRef* rhs);
+        LocalRef() = delete;
+        LocalRef(const LocalRef* rhs) = delete;
     public:
         ~LocalRef();
         explicit LocalRef(egl_object_t* rhs);
@@ -139,7 +137,6 @@
     EGLConfig config;
     sp<ANativeWindow> win;
     egl_connection_t const* cnx;
-    bool enableTimestamps;
 private:
     bool connected;
     void disconnect();
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index 6de5f27..ca8684e 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -14,16 +14,14 @@
  ** limitations under the License.
  */
 
-#include <pthread.h>
+#include "egl_tls.h"
+
 #include <stdlib.h>
 
 #include <cutils/properties.h>
 #include <log/log.h>
 #include <utils/CallStack.h>
 
-#include <EGL/egl.h>
-
-#include "egl_tls.h"
 
 namespace android {
 
@@ -31,7 +29,7 @@
 pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT;
 
 egl_tls_t::egl_tls_t()
-    : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) {
+    : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(true) {
 }
 
 const char *egl_tls_t::egl_strerror(EGLint err) {
@@ -85,11 +83,12 @@
 
 bool egl_tls_t::logNoContextCall() {
     egl_tls_t* tls = getTLS();
-    if (tls->logCallWithNoContext == true) {
+    if (tls->logCallWithNoContext) {
         tls->logCallWithNoContext = false;
         return true;
     }
     return false;
+
 }
 
 egl_tls_t* egl_tls_t::getTLS() {
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 00eae0b..9feae68 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -17,11 +17,9 @@
 #ifndef ANDROID_EGL_TLS_H
 #define ANDROID_EGL_TLS_H
 
-#include <pthread.h>
-
 #include <EGL/egl.h>
 
-#include "egldefs.h"
+#include <pthread.h>
 
 // ----------------------------------------------------------------------------
 namespace android {
@@ -36,7 +34,7 @@
 
     EGLint      error;
     EGLContext  ctx;
-    EGLBoolean  logCallWithNoContext;
+    bool        logCallWithNoContext;
 
     egl_tls_t();
     static void validateTLSKey();
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index c0990ec..a8855ef 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_EGL_IMPL_H
 #define ANDROID_EGL_IMPL_H
 
-#include <ctype.h>
-
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <EGL/eglplatform.h>
@@ -30,8 +28,7 @@
 // ----------------------------------------------------------------------------
 
 EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
-EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name,
-                                                          GLuint index);
+EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index);
 EGLAPI GLint egl_get_num_extensions_for_current_context();
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt
index c8b83f5..89269a0 100644
--- a/opengl/libs/libEGL.map.txt
+++ b/opengl/libs/libEGL.map.txt
@@ -28,6 +28,7 @@
     eglGetCurrentSurface;
     eglGetDisplay;
     eglGetError;
+    eglGetNativeClientBufferANDROID; # introduced=26
     eglGetProcAddress;
     eglGetStreamFileDescriptorKHR; # introduced=23
     eglGetSyncAttribKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
diff --git a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
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..e32d9e6 100644
--- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -8,11 +8,19 @@
 
 Contributors
 
+    Brian Anderson
+    Dan Stoza
     Pablo Ceballos
+    Jesse Hall
+    Fabien Sanglard
 
 Contact
 
+    Brian Anderson, Google Inc. (brianderson 'at' google.com)
+    Dan Stoza, Google Inc. (stoza 'at' google.com)
     Pablo Ceballos, Google Inc. (pceballos 'at' google.com)
+    Jesse Hall, Google Inc. (jessehall 'at' google.com)
+    Fabien Sanglard, Google Inc. (sanglardf 'at' google.com)
 
 Status
 
@@ -20,7 +28,7 @@
 
 Version
 
-    Version 1, May 31, 2016
+    Version 1, January 13, 2017
 
 Number
 
@@ -38,8 +46,8 @@
     and display of window surfaces.
 
     Some examples of how this might be used:
-        - The display retire time can be used to calculate end-to-end latency of
-          the entire graphics pipeline.
+        - The display present 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
@@ -57,22 +65,36 @@
 
 New Procedures and Functions
 
-    EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
-            EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
-            EGLnsecsANDROID *values);
+    EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR *frameId);
 
-    EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
-            surface, EGLint timestamp);
+    EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLint numTimestamps,
+            const EGLint *names, EGLnsecsANDROID *values);
+
+    EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR frameId, EGLint numTimestamps,
+            const EGLint *timestamps, EGLnsecsANDROID *values);
+
+    EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLint timestamp);
 
 New Tokens
 
     EGL_TIMESTAMPS_ANDROID 0x314D
-    EGL_QUEUE_TIME_ANDROID 0x314E
-    EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
-    EGL_COMPOSITION_START_TIME_ANDROID 0x3430
-    EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
-    EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
-    EGL_READS_DONE_TIME_ANDROID 0x3433
+    EGL_COMPOSITE_DEADLINE_ANDROID 0x314E
+    EGL_COMPOSITE_INTERVAL_ANDROID 0x314F
+    EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3150
+    EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3151
+    EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3152
+    EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3153
+    EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3154
+    EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3155
+    EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID 0x3156
+    EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3157
+    EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3158
+    EGL_DEQUEUE_READY_TIME_ANDROID 0x3159
+    EGL_READS_DONE_TIME_ANDROID 0x315A
 
 Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
 "Surface Attributes", page 43:
@@ -82,7 +104,6 @@
     enables timestamp collection, while a value of EGL_FALSE disables it. The
     initial value is false. If surface is not a window surface this has no
     effect.
-
 Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors)
 
     Add a new subsection under Section 3,
@@ -91,49 +112,112 @@
 
     The function
 
-        EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface
-            surface, EGLint framesAgo, EGLint numTimestamps,
+        EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLuint64KHR *frameId);
+
+    Returns an identifier for the next frame to be swapped. The identifier can
+    be used to correlate a particular eglSwapBuffers with its timestamps in
+    eglGetFrameTimestampsANDROID. If any error is generated, the function will
+    return EGL_FALSE.
+
+    The function
+
+        EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy,
+                EGLSurface surface, EGLint numTimestamps,
+                const EGLint *names, EGLnsecsANDROID *values);
+
+    allows querying anticipated timestamps and durations related to the
+    composition and display of a window surface. The values are not associated
+    with a particular frame and can be retrieved before the first swap.
+
+    The eglGetCompositorTimingANDROID function takes an array of names to
+    query and returns their values in the corresponding indices of the values
+    array. The possible names that can be queried are:
+        - EGL_COMPOSITE_DEADLINE_ANDROID - The timestamp of the next time the
+          compositor will begin composition. This is effectively the deadline
+          for when the compositor must receive a newly queued frame.
+        - EGL_COMPOSITE_INTERVAL_ANDROID - The time delta between subsequent
+          composition events.
+        - EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID - The time delta between
+          the start of composition and the expected present time of that
+          composition. This can be used to estimate the latency of the
+          actual present time.
+
+    The function
+
+        EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps,
             const EGLint *timestamps, EGLnsecsANDROID *values);
 
-    allows querying various timestamps related to the composition and display of
-    a window surface.
+    allows querying various timestamps related to the composition and display
+    of specific frames of a window surface.
 
-    The framesAgo parameter indicates how many frames before the last posted
-    frame to query. So a value of zero would indicate that the query is for the
-    last posted frame. Note that the implementation maintains a limited history
-    of timestamp data. If a query is made for a frame whose timestamp history
-    no longer exists then EGL_BAD_ACCESS is generated. If timestamp collection
-    has not been enabled for the surface then EGL_BAD_SURFACE is generated.
-    Timestamps for events that will not occur or have not yet occurred will be
-    zero. Timestamp queries that are not supported will generate an
-    EGL_BAD_PARAMETER error. If any error is generated the function will return
-    EGL_FALSE.
+    The frameId indicates which frame to query. The implementation maintains a
+    limited history of timestamp data. If a query is made for a frame whose
+    timestamp history no longer exists then EGL_BAD_ACCESS is generated. If
+    timestamp collection has not been enabled for the surface then
+    EGL_BAD_SURFACE is generated.  Timestamps for events that will not occur or
+    have not yet occurred will be zero. Timestamp queries that are not
+    supported will generate an EGL_BAD_PARAMETER error. If any error is
+    generated the function will return EGL_FALSE.
+
+    The application can poll for the timestamp of particular events by calling
+    eglGetFrameTimestamps over and over without needing to call any other EGL
+    function between calls. This is true even for the most recently swapped
+    frame. eglGetFrameTimestamps is thread safe and can be called from a
+    different thread than the swapping thread.
 
     The eglGetFrameTimestampsANDROID function takes an array of timestamps to
     query and returns timestamps in the corresponding indices of the values
     array. The possible timestamps that can be queried are:
-        - EGL_QUEUE_TIME_ANDROID - The time this frame was queued by the
-          application.
+        - EGL_REQUESTED_PRESENT_TIME_ANDROID - The time the application
+          requested this frame be presented. See EGL_ANDROID_presentation_time.
+          If the application does not request a presentation time explicitly,
+          this will correspond to buffer's queue time.
         - EGL_RENDERING_COMPLETE_TIME_ANDROID - The time when all of the
           application's rendering to the surface was completed.
-        - EGL_COMPOSITION_START_TIME_ANDROID - The time at which the compositor
-          began preparing composition for this frame.
-        - EGL_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the
-          compositor's rendering work for this frame finished. This will be zero
-          if composition was handled by the display and the compositor didn't do
-          any rendering.
+        - EGL_COMPOSITION_LATCH_TIME_ANDROID - The time when the compositor
+          selected this frame as the one to use for the next composition. The
+          latch is the earliest indication that the frame was submitted in time
+          to be composited.
+        - EGL_FIRST_COMPOSITION_START_TIME_ANDROID - The first time at which
+          the compositor began preparing composition for this frame.
+        - EGL_LAST_COMPOSITION_START_TIME_ANDROID - The last time at which the
+          compositor began preparing composition for this frame. If this frame
+          is composited only once, it will have the same value as
+          EGL_FIRST_COMPOSITION_START_TIME_ANDROID. If the value is not equal,
+          that indicates the subsequent frame was not submitted in time to be
+          latched by the compositor. Note: The value may not be updated for
+          every display refresh if the compositor becomes idle.
+        - EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID - The time at which
+          the compositor's rendering work for this frame finished. This will be
+          zero if composition was handled by the display and the compositor
+          didn't do any rendering.
+        - EGL_DISPLAY_PRESENT_TIME_ANDROID - The time at which this frame
+          started to scan out to the physical display.
         - EGL_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
-    function
+    Not all implementations may support all of the above timestamp queries. The
+    functions
 
-        EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
-            surface, EGLint timestamp);
+        EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLint name);
 
-    allows querying which timestamps are supported on the implementation."
+    and
+
+        EGLBoolean eglGetFrameTimestampsSupportedANDROID(EGLDisplay dpy,
+            EGLSurface surface, EGLint timestamp);
+
+    allows querying which values are supported by the implementations of
+    eglGetCompositoTimingANDROID and eglGetFrameTimestampsSupportedANDROID
+    respectively."
 
 Issues
 
@@ -141,5 +225,21 @@
 
 Revision History
 
+#5 (Brian Anderson, January 13, 2017)
+    - Add eglGetCompositorTimingANDROID.
+
+#4 (Brian Anderson, January 10, 2017)
+    - Use an absolute frameId rather than a relative framesAgo.
+
+#3 (Brian Anderson, November 30, 2016)
+    - Add EGL_COMPOSITION_LATCH_TIME_ANDROID,
+      EGL_LAST_COMPOSITION_START_TIME_ANDROID, and
+      EGL_DEQUEUE_READY_TIME_ANDROID.
+
+#2 (Brian Anderson, July 22, 2016)
+    - Replace EGL_QUEUE_TIME_ANDROID with EGL_REQUESTED_PRESENT_TIME_ANDROID.
+    - Add DISPLAY_PRESENT_TIME_ANDROID.
+
 #1 (Pablo Ceballos, May 31, 2016)
     - Initial draft.
+
diff --git a/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt
new file mode 100644
index 0000000..772b21a
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt
@@ -0,0 +1,99 @@
+Name
+
+    ANDROID_get_native_client_buffer
+
+Name Strings
+
+    EGL_ANDROID_get_native_client_buffer
+
+Contributors
+
+    Craig Donner
+
+Contact
+
+    Craig Donner, Google Inc. (cdonner 'at' google.com)
+
+Status
+
+    Draft
+
+Version
+
+    Version 1.0, January 27, 2017
+
+Number
+
+    EGL Extension #XXX
+
+Dependencies
+
+    Requires EGL 1.2.
+
+    EGL_ANDROID_image_native_buffer and EGL_KHR_image_base are required.
+
+    This extension is written against the wording of the EGL 1.2
+    Specification as modified by EGL_KHR_image_base and
+    EGL_ANDROID_image_native_buffer.
+
+Overview
+
+    This extension allows creating an EGLClientBuffer from an Android
+    AHardwareBuffer object which can be later used to create an EGLImage.
+
+New Types
+
+struct AHardwareBuffer
+
+New Procedures and Functions
+
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer)
+
+New Tokens
+
+    None
+
+Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors)
+
+    Add the following to section 2.5.1 "EGLImage Specification" (as modified by
+    the EGL_KHR_image_base and EGL_ANDROID_image_native_buffer specifications),
+    below the description of eglCreateImageKHR:
+
+   "The command
+
+        EGLClientBuffer eglGetNativeClientBufferANDROID(
+                                AHardwareBuffer *buffer)
+
+    may be used to create an EGLClientBuffer from an AHardwareBuffer object.
+    EGL implementations must guarantee that the lifetime of the returned
+    EGLClientBuffer is at least as long as the EGLImage(s) it is bound to,
+    following the lifetime semantics described below in section 2.5.2; the
+    EGLClientBuffer must be destroyed no earlier than when all of its associated
+    EGLImages are destroyed by eglDestroyImageKHR.
+
+    Errors
+
+        If eglGetNativeClientBufferANDROID fails, NULL will be returned, no
+        memory will be allocated, and the following error will be generated:
+
+       * If the value of buffer is NULL, the error EGL_BAD_PARAMETER is
+         generated.
+
+Issues
+
+    1. Should this extension define what particular AHardwareBuffer formats EGL
+    implementations are required to support?
+
+    RESOLVED: No.
+
+    The set of valid formats is implementation-specific and may depend on
+    additional EGL extensions. The particular valid combinations for a given
+    Android version and implementation should be documented by that version.
+
+Revision History
+
+#2 (Craig Donner, February 17, 2017)
+    - Fix typographical errors.
+
+#1 (Craig Donner, January 27, 2017)
+    - Initial draft.
diff --git a/opengl/specs/README b/opengl/specs/README
index f0c024e..a7a9785 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -1,5 +1,5 @@
 This directory contains OpenGL ES and EGL extension specifications that have
-been or are being defined for Android.  
+been or are being defined for Android.
 
 The table below tracks usage of EGL enumerant values that have been reserved
 for use by Android extensions.
@@ -20,10 +20,17 @@
 0x314B               EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
 0x314C               EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
 0x314D               EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x314E               EGL_QUEUE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x314F               EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3430               EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3431               EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3432               EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3433               EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
-0x3434 - 0x343F      (unused)
+0x314E               EGL_COMPOSITE_DEADLINE_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x314F               EGL_COMPOSITE_INTERVAL_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3150               EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3151               EGL_REQUESTED_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3152               EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3153               EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3154               EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3155               EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3156               EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3157               EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3158               EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3159               EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x315A               EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x315B - 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..1b3086b 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -20,10 +20,15 @@
 
 #include <EGL/egl.h>
 #include <gui/Surface.h>
-
+#include <gui/IConsumerListener.h>
+#include <gui/IProducerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/BufferQueue.h>
 
 namespace android {
 
+#define EGL_UNSIGNED_TRUE static_cast<EGLBoolean>(EGL_TRUE)
+
 class EGLTest : public ::testing::Test {
 protected:
     EGLDisplay mEglDisplay;
@@ -48,7 +53,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 +70,20 @@
     };
 
     success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     ASSERT_GE(numConfigs, 1);
 
     EGLint components[3];
 
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
-    ASSERT_EQ(EGL_TRUE, success);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
     EXPECT_GE(components[0], 8);
@@ -101,9 +106,9 @@
     EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs));
 
     struct DummyConsumer : public BnConsumerListener {
-        virtual void onFrameAvailable(const BufferItem& /* item */) {}
-        virtual void onBuffersReleased() {}
-        virtual void onSidebandStreamChanged() {}
+        void onFrameAvailable(const BufferItem& /* item */) override {}
+        void onBuffersReleased() override {}
+        void onSidebandStreamChanged() override {}
     };
 
     // Create a EGLSurface
@@ -139,23 +144,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/tests/hwc/Android.mk b/opengl/tests/hwc/Android.mk
index 693fba4..13337c2 100644
--- a/opengl/tests/hwc/Android.mk
+++ b/opengl/tests/hwc/Android.mk
@@ -25,6 +25,8 @@
 LOCAL_C_INCLUDES += system/extras/tests/include \
     $(call include-path-for, opengl-tests-includes) \
 
+LOCAL_STATIC_LIBRARIES := libarect
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/opengl/tests/lib/Android.mk b/opengl/tests/lib/Android.mk
index 4407e7b..0f1c925 100644
--- a/opengl/tests/lib/Android.mk
+++ b/opengl/tests/lib/Android.mk
@@ -24,4 +24,6 @@
 
 LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -Wall -Wextra -Werror
 
+LOCAL_STATIC_LIBRARIES := libarect
+
 include $(BUILD_STATIC_LIBRARY)
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 6caf076..e8691bb 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -985,6 +985,7 @@
         boolean emitExceptionCheck = ((numArrays > 0 || numStrings > 0)
                                              && (hasNonConstArg(jfunc, cfunc, nonPrimitiveArgs)
                                                  || (cfunc.hasPointerArg() && numArrays > 0))
+                                         || (numBufferArgs > 0)
                                          || hasCheckTest(cfunc)
                                          || hasIfTest(cfunc))
                                          || (stringArgs.size() > 0);
@@ -1308,6 +1309,8 @@
 
                     out.println();
                 } else if (jfunc.getArgType(idx).isBuffer()) {
+                    needsExit = needsExit || (!nullAllowed && !isPointerFunc);
+
                     String array = numBufferArgs <= 1 ? "_array" :
                         "_" + cfunc.getArgName(cIndex) + "Array";
                     String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
@@ -1318,6 +1321,17 @@
                         out.println(indent + "if (" + cname + "_buf) {");
                         out.print(indent);
                     }
+                    else
+                    {
+                        out.println(indent + "if (!" + cname + "_buf) {");
+                        out.println(indent + indent + "_exception = 1;");
+                        out.println(indent + indent + "_exceptionType = " +
+                                "\"java/lang/IllegalArgumentException\";");
+                        out.println(indent + indent + "_exceptionMessage = \"" +
+                                cname +" == null\";");
+                        out.println(indent + indent + "goto exit;");
+                        out.println(indent + "}");
+                    }
 
                     if (isPointerFunc) {
                         out.println(indent +
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
index a5a8968..523bc57 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if
@@ -28,6 +28,7 @@
     public static final int EGL_CONTEXT_MINOR_VERSION_KHR   = 0x30FB;
     public static final int EGL_CONTEXT_FLAGS_KHR           = 0x30FC;
     public static final int EGL_OPENGL_ES3_BIT_KHR          = 0x0040;
+    public static final int EGL_RECORDABLE_ANDROID          = 0x3142;
 
     native private static void _nativeClassInit();
     static {
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
new file mode 100644
index 0000000..3eacf3c
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp
@@ -0,0 +1,9 @@
+/* EGLSurface eglCreatePixmapSurface ( EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list ) */
+static jobject
+android_eglCreatePixmapSurface
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jint pixmap, jintArray attrib_list_ref, jint offset) {
+    jniThrowException(_env, "java/lang/UnsupportedOperationException",
+        "eglCreatePixmapSurface");
+    return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0);
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java
new file mode 100644
index 0000000..1750b32
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.java
@@ -0,0 +1,11 @@
+    // C function EGLSurface eglCreatePixmapSurface ( EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list )
+
+    @Deprecated
+    public static native EGLSurface eglCreatePixmapSurface(
+        EGLDisplay dpy,
+        EGLConfig config,
+        int pixmap,
+        int[] attrib_list,
+        int offset
+    );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg
new file mode 100644
index 0000000..fa260d8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.nativeReg
@@ -0,0 +1 @@
+{"eglCreatePixmapSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;I[II)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePixmapSurface },
diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp
index 18ec5e3..7062c57 100644
--- a/opengl/tools/glgen/stubs/gles11/common.cpp
+++ b/opengl/tools/glgen/stubs/gles11/common.cpp
@@ -246,6 +246,19 @@
  */
 static int getNeededCount(GLint pname) {
     int needed = 1;
+#ifdef GL_ES_VERSION_3_0
+    // GLES 3.x pnames
+    switch (pname) {
+        case GL_MAX_VIEWPORT_DIMS:
+            needed = 2;
+            break;
+
+        case GL_PROGRAM_BINARY_FORMATS:
+            glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &needed);
+            break;
+    }
+#endif
+
 #ifdef GL_ES_VERSION_2_0
     // GLES 2.x pnames
     switch (pname) {
diff --git a/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java b/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
index c966e11..57338c7 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
+++ b/opengl/tools/glgen/stubs/gles11/glGetBufferPointerv.java
@@ -1,5 +1,9 @@
     // C function void glGetBufferPointerv ( GLenum target, GLenum pname, GLvoid** params )
 
+    /**
+     * The {@link java.nio.Buffer} instance returned by this method is guaranteed
+     * to be an instance of {@link java.nio.ByteBuffer}.
+     */
     public static native java.nio.Buffer glGetBufferPointerv(
         int target,
         int pname
diff --git a/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp b/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
index dd656b6..6d42e56 100644
--- a/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
+++ b/opengl/tools/glgen/stubs/gles11/glGetShaderInfoLog.cpp
@@ -5,15 +5,16 @@
     GLint infoLen = 0;
     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
     if (!infoLen) {
-        return _env->NewStringUTF("");
+        infoLen = 512;
     }
     char* buf = (char*) malloc(infoLen);
     if (buf == NULL) {
         jniThrowException(_env, "java/lang/IllegalArgumentException", "out of memory");
         return NULL;
     }
-    glGetShaderInfoLog(shader, infoLen, NULL, buf);
-    jstring result = _env->NewStringUTF(buf);
+    GLsizei outLen = 0;
+    glGetShaderInfoLog(shader, infoLen, &outLen, buf);
+    jstring result = _env->NewStringUTF(outLen == 0 ? "" : buf);
     free(buf);
     return result;
 }
diff --git a/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java b/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
index 482ea99..7b1966b 100644
--- a/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
+++ b/opengl/tools/glgen/stubs/gles11/glMapBufferRange.java
@@ -1,5 +1,9 @@
     // C function GLvoid * glMapBufferRange ( GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access )
 
+    /**
+     * The {@link java.nio.Buffer} instance returned by this method is guaranteed
+     * to be an instance of {@link java.nio.ByteBuffer}.
+     */
     public static native java.nio.Buffer glMapBufferRange(
         int target,
         int offset,
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index c9384ce..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..e8a9c34
--- /dev/null
+++ b/services/audiomanager/IPlayer.cpp
@@ -0,0 +1,189 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "IPlayer"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <audiomanager/IPlayer.h>
+
+namespace android {
+
+enum {
+    START      = IBinder::FIRST_CALL_TRANSACTION,
+    PAUSE      = IBinder::FIRST_CALL_TRANSACTION + 1,
+    STOP       = IBinder::FIRST_CALL_TRANSACTION + 2,
+    SET_VOLUME = IBinder::FIRST_CALL_TRANSACTION + 3,
+    SET_PAN    = IBinder::FIRST_CALL_TRANSACTION + 4,
+    SET_START_DELAY_MS = IBinder::FIRST_CALL_TRANSACTION + 5,
+    APPLY_VOLUME_SHAPER = IBinder::FIRST_CALL_TRANSACTION + 6,
+};
+
+class BpPlayer : public BpInterface<IPlayer>
+{
+public:
+    explicit BpPlayer(const sp<IBinder>& impl)
+        : BpInterface<IPlayer>(impl)
+    {
+    }
+
+    virtual void start()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        remote()->transact(START, data, &reply);
+    }
+
+    virtual void pause()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        remote()->transact(PAUSE, data, &reply);
+    }
+
+    virtual void stop()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        remote()->transact(STOP, data, &reply);
+    }
+
+    virtual void setVolume(float vol)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        data.writeFloat(vol);
+        remote()->transact(SET_VOLUME, data, &reply);
+    }
+
+    virtual void setPan(float pan)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        data.writeFloat(pan);
+        remote()->transact(SET_PAN, data, &reply);
+    }
+
+    virtual void setStartDelayMs(int32_t delayMs) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+        data.writeInt32(delayMs);
+        remote()->transact(SET_START_DELAY_MS, data, &reply);
+    }
+
+    virtual void applyVolumeShaper(
+            const sp<VolumeShaper::Configuration>& configuration,
+            const sp<VolumeShaper::Operation>& operation) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPlayer::getInterfaceDescriptor());
+
+        status_t status = configuration.get() == nullptr
+                ? data.writeInt32(0)
+                :  data.writeInt32(1)
+                    ?: configuration->writeToParcel(&data);
+        if (status != NO_ERROR) {
+            ALOGW("applyVolumeShaper failed configuration parceling: %d", status);
+            return; // ignore error
+        }
+
+        status = operation.get() == nullptr
+                ? status = data.writeInt32(0)
+                : data.writeInt32(1)
+                    ?: operation->writeToParcel(&data);
+        if (status != NO_ERROR) {
+            ALOGW("applyVolumeShaper failed operation parceling: %d", status);
+            return; // ignore error
+        }
+
+        status = remote()->transact(APPLY_VOLUME_SHAPER, data, &reply);
+
+        ALOGW_IF(status != NO_ERROR, "applyVolumeShaper failed transact: %d", status);
+        return; // one way transaction, ignore error
+    }
+};
+
+IMPLEMENT_META_INTERFACE(Player, "android.media.IPlayer");
+
+// ----------------------------------------------------------------------
+
+status_t BnPlayer::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch (code) {
+        case START: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            start();
+            return NO_ERROR;
+        } break;
+        case PAUSE: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            pause();
+            return NO_ERROR;
+        }
+        case STOP: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            stop();
+            return NO_ERROR;
+        } break;
+        case SET_VOLUME: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            setVolume(data.readFloat());
+            return NO_ERROR;
+        } break;
+        case SET_PAN: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            setPan(data.readFloat());
+            return NO_ERROR;
+        } break;
+        case SET_START_DELAY_MS: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            setStartDelayMs(data.readInt32());
+            return NO_ERROR;
+        } break;
+        case APPLY_VOLUME_SHAPER: {
+            CHECK_INTERFACE(IPlayer, data, reply);
+            sp<VolumeShaper::Configuration> configuration;
+            sp<VolumeShaper::Operation> operation;
+
+            int32_t present;
+            status_t status = data.readInt32(&present);
+            if (status == NO_ERROR && present != 0) {
+                configuration = new VolumeShaper::Configuration();
+                status = configuration->readFromParcel(data);
+            }
+            status = status ?: data.readInt32(&present);
+            if (status == NO_ERROR && present != 0) {
+                operation = new VolumeShaper::Operation();
+                status = operation->readFromParcel(data);
+            }
+            if (status == NO_ERROR) {
+                // one way transaction, no error returned
+                applyVolumeShaper(configuration, operation);
+            }
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+} // namespace android
diff --git a/services/batteryservice/Android.bp b/services/batteryservice/Android.bp
index 79db871..e441bda 100644
--- a/services/batteryservice/Android.bp
+++ b/services/batteryservice/Android.bp
@@ -1,4 +1,4 @@
-cc_library_static {
+cc_library {
     name: "libbatteryservice",
 
     srcs: [
@@ -8,7 +8,7 @@
         "IBatteryPropertiesRegistrar.cpp",
     ],
 
-    static_libs: [
+    shared_libs: [
         "libutils",
         "libbinder",
     ],
@@ -19,4 +19,4 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-}
+}
\ No newline at end of file
diff --git a/services/batteryservice/BatteryProperties.cpp b/services/batteryservice/BatteryProperties.cpp
index d89d4c9..8fa111d 100644
--- a/services/batteryservice/BatteryProperties.cpp
+++ b/services/batteryservice/BatteryProperties.cpp
@@ -41,6 +41,7 @@
     batteryLevel = p->readInt32();
     batteryVoltage = p->readInt32();
     batteryTemperature = p->readInt32();
+    batteryFullCharge = p->readInt32();
     batteryChargeCounter = p->readInt32();
     batteryTechnology = String8((p->readString16()).string());
     return OK;
@@ -58,6 +59,7 @@
     p->writeInt32(batteryLevel);
     p->writeInt32(batteryVoltage);
     p->writeInt32(batteryTemperature);
+    p->writeInt32(batteryFullCharge);
     p->writeInt32(batteryChargeCounter);
     p->writeString16(String16(batteryTechnology));
     return OK;
diff --git a/services/batteryservice/IBatteryPropertiesListener.cpp b/services/batteryservice/IBatteryPropertiesListener.cpp
index 7555f4b..6e5bcfe 100644
--- a/services/batteryservice/IBatteryPropertiesListener.cpp
+++ b/services/batteryservice/IBatteryPropertiesListener.cpp
@@ -43,4 +43,22 @@
 
 // ----------------------------------------------------------------------------
 
+status_t BnBatteryPropertiesListener::onTransact(uint32_t code, const Parcel& data,
+                                                 Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case TRANSACT_BATTERYPROPERTIESCHANGED: {
+            CHECK_INTERFACE(IBatteryPropertiesListener, data, reply);
+            struct BatteryProperties props = {};
+            if (data.readInt32() != 0) {
+                props.readFromParcel((Parcel*)&data);
+            }
+            batteryPropertiesChanged(props);
+            return NO_ERROR;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+};
+
 }; // namespace android
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 89475e9..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..cfb7231 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -11,6 +11,7 @@
     RecentEventLogger.cpp \
     RotationVectorSensor.cpp \
     SensorDevice.cpp \
+    SensorDirectConnection.cpp \
     SensorEventConnection.cpp \
     SensorFusion.cpp \
     SensorInterface.cpp \
@@ -19,7 +20,6 @@
     SensorService.cpp \
     SensorServiceUtils.cpp \
 
-
 LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
 
 LOCAL_CFLAGS += -Wall -Werror -Wextra
@@ -33,9 +33,20 @@
     libutils \
     liblog \
     libbinder \
-    libui \
-    libgui \
-    libcrypto
+    libsensor \
+    libcrypto \
+    libbase \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
+    android.hardware.sensors@1.0
+
+LOCAL_STATIC_LIBRARIES := \
+    android.hardware.sensors@1.0-convert
+
+# our public headers depend on libsensor
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+    libsensor \
 
 LOCAL_MODULE:= libsensorservice
 
diff --git a/services/sensorservice/CorrectedGyroSensor.h b/services/sensorservice/CorrectedGyroSensor.h
index 68acd43..1d49e01 100644
--- a/services/sensorservice/CorrectedGyroSensor.h
+++ b/services/sensorservice/CorrectedGyroSensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorInterface.h"
 
diff --git a/services/sensorservice/GravitySensor.h b/services/sensorservice/GravitySensor.h
index 8e33a73..483f468 100644
--- a/services/sensorservice/GravitySensor.h
+++ b/services/sensorservice/GravitySensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorInterface.h"
 
diff --git a/services/sensorservice/LinearAccelerationSensor.h b/services/sensorservice/LinearAccelerationSensor.h
index 428baa6..aa4e54a 100644
--- a/services/sensorservice/LinearAccelerationSensor.h
+++ b/services/sensorservice/LinearAccelerationSensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorInterface.h"
 #include "GravitySensor.h"
diff --git a/services/sensorservice/OrientationSensor.h b/services/sensorservice/OrientationSensor.h
index 30ff226..a3f2a99 100644
--- a/services/sensorservice/OrientationSensor.h
+++ b/services/sensorservice/OrientationSensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorInterface.h"
 
diff --git a/services/sensorservice/RotationVectorSensor.h b/services/sensorservice/RotationVectorSensor.h
index 265b4c4..34deaa9 100644
--- a/services/sensorservice/RotationVectorSensor.h
+++ b/services/sensorservice/RotationVectorSensor.h
@@ -20,7 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "SensorDevice.h"
 #include "SensorInterface.h"
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index ac03742..7bd495f 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -13,133 +13,190 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "SensorDevice.h"
+#include "SensorService.h"
 
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <sys/types.h>
-
+#include <android-base/logging.h>
+#include <sensors/convert.h>
 #include <utils/Atomic.h>
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 
-#include <binder/BinderService.h>
-#include <binder/Parcel.h>
-#include <binder/IServiceManager.h>
+#include <chrono>
+#include <cinttypes>
+#include <thread>
 
-#include <hardware/sensors.h>
+using android::hardware::hidl_vec;
 
-#include "SensorDevice.h"
-#include "SensorService.h"
+using namespace android::hardware::sensors::V1_0;
+using namespace android::hardware::sensors::V1_0::implementation;
 
 namespace android {
 // ---------------------------------------------------------------------------
 
 ANDROID_SINGLETON_STATIC_INSTANCE(SensorDevice)
 
-SensorDevice::SensorDevice()
-    :  mSensorDevice(0),
-       mSensorModule(0) {
-    status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,
-            (hw_module_t const**)&mSensorModule);
-
-    ALOGE_IF(err, "couldn't load %s module (%s)",
-            SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-    if (mSensorModule) {
-        err = sensors_open_1(&mSensorModule->common, &mSensorDevice);
-
-        ALOGE_IF(err, "couldn't open device for module %s (%s)",
-                SENSORS_HARDWARE_MODULE_ID, strerror(-err));
-
-        if (mSensorDevice) {
-            if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||
-                mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {
-                ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");
-            }
-
-            sensor_t const* list;
-            ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);
-            mActivationCount.setCapacity(count);
-            Info model;
-            for (size_t i=0 ; i<size_t(count) ; i++) {
-                mActivationCount.add(list[i].handle, model);
-                mSensorDevice->activate(
-                        reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                        list[i].handle, 0);
-            }
-        }
+static status_t StatusFromResult(Result result) {
+    switch (result) {
+        case Result::OK:
+            return OK;
+        case Result::BAD_VALUE:
+            return BAD_VALUE;
+        case Result::PERMISSION_DENIED:
+            return PERMISSION_DENIED;
+        case Result::INVALID_OPERATION:
+            return INVALID_OPERATION;
+        case Result::NO_MEMORY:
+            return NO_MEMORY;
     }
 }
 
+SensorDevice::SensorDevice() {
+    // SensorDevice may wait upto 100ms * 10 = 1s for hidl service.
+    constexpr auto RETRY_DELAY = std::chrono::milliseconds(100);
+    size_t retry = 10;
+
+    while (true) {
+        int initStep = 0;
+        mSensors = ISensors::getService();
+        if (mSensors != nullptr) {
+            ++initStep;
+            // Poke ISensor service. If it has lingering connection from previous generation of
+            // system server, it will kill itself. There is no intention to handle the poll result,
+            // which will be done since the size is 0.
+            if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
+                // ok to continue
+                break;
+            }
+            // hidl service is restarting, pointer is invalid.
+            mSensors = nullptr;
+        }
+
+        if (--retry <= 0) {
+            ALOGE("Cannot connect to ISensors hidl service!");
+            return;
+        }
+        // Delay 100ms before retry, hidl service is expected to come up in short time after
+        // crash.
+        ALOGI("%s unsuccessful, try again soon (remaining retry %zu).",
+                (initStep == 0) ? "getService()" : "poll() check", retry);
+        std::this_thread::sleep_for(RETRY_DELAY);
+    }
+
+    checkReturn(mSensors->getSensorsList(
+            [&](const auto &list) {
+                const size_t count = list.size();
+
+                mActivationCount.setCapacity(count);
+                Info model;
+                for (size_t i=0 ; i < count; i++) {
+                    sensor_t sensor;
+                    convertToSensor(list[i], &sensor);
+                    mSensorList.push_back(sensor);
+
+                    mActivationCount.add(list[i].sensorHandle, model);
+
+                    checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
+                }
+            }));
+
+    mIsDirectReportSupported =
+           (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION);
+}
+
 void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) {
     if (connected) {
         Info model;
         mActivationCount.add(handle, model);
-        mSensorDevice->activate(
-                reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice), handle, 0);
+        checkReturn(mSensors->activate(handle, 0 /* enabled */));
     } else {
         mActivationCount.removeItem(handle);
     }
 }
 
 std::string SensorDevice::dump() const {
-    if (!mSensorModule) return "HAL not initialized\n";
+    if (mSensors == NULL) return "HAL not initialized\n";
 
     String8 result;
-    sensor_t const* list;
-    int count = mSensorModule->get_sensors_list(mSensorModule, &list);
+    checkReturn(mSensors->getSensorsList([&](const auto &list) {
+            const size_t count = list.size();
 
-    result.appendFormat("HAL: %s (%s), version %#010x\n",
-                        mSensorModule->common.name,
-                        mSensorModule->common.author,
-                        getHalDeviceVersion());
-    result.appendFormat("Total %d h/w sensors, %zu running:\n", count, mActivationCount.size());
+            result.appendFormat(
+                "Total %zu h/w sensors, %zu running:\n",
+                count,
+                mActivationCount.size());
 
-    Mutex::Autolock _l(mLock);
-    for (int i = 0 ; i < count ; i++) {
-        const Info& info = mActivationCount.valueFor(list[i].handle);
-        if (info.batchParams.isEmpty()) continue;
-        result.appendFormat("0x%08x) active-count = %zu; ", list[i].handle,
-                            info.batchParams.size());
+            Mutex::Autolock _l(mLock);
+            for (size_t i = 0 ; i < count ; i++) {
+                const Info& info = mActivationCount.valueFor(
+                    list[i].sensorHandle);
 
-        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);
+                if (info.batchParams.isEmpty()) continue;
+                result.appendFormat(
+                    "0x%08x) active-count = %zu; ",
+                    list[i].sensorHandle,
+                    info.batchParams.size());
 
-        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);
-    }
+                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) {
-    if (!mSensorModule) return NO_INIT;
-    ssize_t count = mSensorModule->get_sensors_list(mSensorModule, list);
-    return count;
+    *list = &mSensorList[0];
+
+    return mSensorList.size();
 }
 
 status_t SensorDevice::initCheck() const {
-    return mSensorDevice && mSensorModule ? NO_ERROR : NO_INIT;
+    return mSensors != NULL ? NO_ERROR : NO_INIT;
 }
 
 ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
-    if (!mSensorDevice) return NO_INIT;
-    ssize_t c;
-    do {
-        c = mSensorDevice->poll(reinterpret_cast<struct sensors_poll_device_t *> (mSensorDevice),
-                                buffer, count);
-    } while (c == -EINTR);
-    return c;
+    if (mSensors == NULL) return NO_INIT;
+
+    ssize_t err;
+
+    checkReturn(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) {
@@ -149,7 +206,8 @@
 }
 
 status_t SensorDevice::activate(void* ident, int handle, int enabled) {
-    if (!mSensorDevice) return NO_INIT;
+    if (mSensors == NULL) return NO_INIT;
+
     status_t err(NO_ERROR);
     bool actuateHardware = false;
 
@@ -164,6 +222,8 @@
         ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
 
         if (isClientDisabledLocked(ident)) {
+            ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
+                    ident, handle);
             return INVALID_OPERATION;
         }
 
@@ -179,24 +239,30 @@
     } else {
         ALOGD_IF(DEBUG_CONNECTIONS, "disable index=%zd", info.batchParams.indexOfKey(ident));
 
+        // If a connected dynamic sensor is deactivated, remove it from the
+        // dictionary.
+        auto it = mConnectedDynamicSensors.find(handle);
+        if (it != mConnectedDynamicSensors.end()) {
+            delete it->second;
+            mConnectedDynamicSensors.erase(it);
+        }
+
         if (info.removeBatchParamsForIdent(ident) >= 0) {
             if (info.numActiveClients() == 0) {
                 // This is the last connection, we need to de-activate the underlying h/w sensor.
                 actuateHardware = true;
             } else {
-                const int halVersion = getHalDeviceVersion();
-                if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
-                    // Call batch for this sensor with the previously calculated best effort
-                    // batch_rate and timeout. One of the apps has unregistered for sensor
-                    // events, and the best effort batch parameters might have changed.
-                    ALOGD_IF(DEBUG_CONNECTIONS,
-                             "\t>>> actuating h/w batch %d %d %" PRId64 " %" PRId64, handle,
-                             info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
-                             info.bestBatchParams.batchTimeout);
-                    mSensorDevice->batch(mSensorDevice, handle,info.bestBatchParams.flags,
-                                         info.bestBatchParams.batchDelay,
-                                         info.bestBatchParams.batchTimeout);
-                }
+                // Call batch for this sensor with the previously calculated best effort
+                // batch_rate and timeout. One of the apps has unregistered for sensor
+                // events, and the best effort batch parameters might have changed.
+                ALOGD_IF(DEBUG_CONNECTIONS,
+                         "\t>>> actuating h/w batch %d %d %" PRId64 " %" PRId64, handle,
+                         info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+                         info.bestBatchParams.batchTimeout);
+                checkReturn(mSensors->batch(
+                        handle,
+                        info.bestBatchParams.batchDelay,
+                        info.bestBatchParams.batchTimeout));
             }
         } else {
             // sensor wasn't enabled for this ident
@@ -210,8 +276,7 @@
     if (actuateHardware) {
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
                  enabled);
-        err = mSensorDevice->activate(
-                reinterpret_cast<struct sensors_poll_device_t *> (mSensorDevice), handle, enabled);
+        err = StatusFromResult(checkReturn(mSensors->activate(handle, enabled)));
         ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
                  strerror(-err));
 
@@ -221,31 +286,21 @@
         }
     }
 
-    // On older devices which do not support batch, call setDelay().
-    if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1 && info.numActiveClients() > 0) {
-        ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w setDelay %d %" PRId64, handle,
-                 info.bestBatchParams.batchDelay);
-        mSensorDevice->setDelay(
-                reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                handle, info.bestBatchParams.batchDelay);
-    }
     return err;
 }
 
-status_t SensorDevice::batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
-                             int64_t maxBatchReportLatencyNs) {
-    if (!mSensorDevice) return NO_INIT;
+status_t SensorDevice::batch(
+        void* ident,
+        int handle,
+        int flags,
+        int64_t samplingPeriodNs,
+        int64_t maxBatchReportLatencyNs) {
+    if (mSensors == NULL) return NO_INIT;
 
     if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
         samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
     }
 
-    const int halVersion = getHalDeviceVersion();
-    if (halVersion < SENSORS_DEVICE_API_VERSION_1_1 && maxBatchReportLatencyNs != 0) {
-        // Batch is not supported on older devices return invalid operation.
-        return INVALID_OPERATION;
-    }
-
     ALOGD_IF(DEBUG_CONNECTIONS,
              "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64,
              ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
@@ -274,21 +329,17 @@
     status_t err(NO_ERROR);
     // If the min period or min timeout has changed since the last batch call, call batch.
     if (prevBestBatchParams != info.bestBatchParams) {
-        if (halVersion >= SENSORS_DEVICE_API_VERSION_1_1) {
-            ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %" PRId64 " %" PRId64, handle,
-                     info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
-                     info.bestBatchParams.batchTimeout);
-            err = mSensorDevice->batch(mSensorDevice, handle, info.bestBatchParams.flags,
-                                       info.bestBatchParams.batchDelay,
-                                       info.bestBatchParams.batchTimeout);
-        } else {
-            // For older devices which do not support batch, call setDelay() after activate() is
-            // called. Some older devices may not support calling setDelay before activate(), so
-            // call setDelay in SensorDevice::activate() method.
-        }
+        ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH %d %d %" PRId64 " %" PRId64, handle,
+                 info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
+                 info.bestBatchParams.batchTimeout);
+        err = StatusFromResult(
+                checkReturn(mSensors->batch(
+                    handle,
+                    info.bestBatchParams.batchDelay,
+                    info.bestBatchParams.batchTimeout)));
         if (err != NO_ERROR) {
             ALOGE("sensor batch failed %p %d %d %" PRId64 " %" PRId64 " err=%s",
-                  mSensorDevice, handle,
+                  mSensors.get(), handle,
                   info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
                   info.bestBatchParams.batchTimeout, strerror(-err));
             info.removeBatchParamsForIdent(ident);
@@ -298,7 +349,7 @@
 }
 
 status_t SensorDevice::setDelay(void* ident, int handle, int64_t samplingPeriodNs) {
-    if (!mSensorDevice) return NO_INIT;
+    if (mSensors == NULL) return NO_INIT;
     if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
         samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
     }
@@ -317,22 +368,20 @@
     BatchParams& params = info.batchParams.editValueAt(index);
     params.batchDelay = samplingPeriodNs;
     info.selectBatchParams();
-    return mSensorDevice->setDelay(reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                                   handle, info.bestBatchParams.batchDelay);
+
+    return StatusFromResult(
+            checkReturn(mSensors->batch(handle, info.bestBatchParams.batchDelay, 0)));
 }
 
 int SensorDevice::getHalDeviceVersion() const {
-    if (!mSensorDevice) return -1;
-    return mSensorDevice->common.version;
+    if (mSensors == NULL) return -1;
+    return SENSORS_DEVICE_API_VERSION_1_4;
 }
 
 status_t SensorDevice::flush(void* ident, int handle) {
-    if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_1) {
-        return INVALID_OPERATION;
-    }
     if (isClientDisabled(ident)) return INVALID_OPERATION;
     ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
-    return mSensorDevice->flush(mSensorDevice, handle);
+    return StatusFromResult(checkReturn(mSensors->flush(handle)));
 }
 
 bool SensorDevice::isClientDisabled(void* ident) {
@@ -347,7 +396,7 @@
 void SensorDevice::enableAllSensors() {
     Mutex::Autolock _l(mLock);
     mDisabledClients.clear();
-    const int halVersion = getHalDeviceVersion();
+    ALOGI("cleared mDisabledClients");
     for (size_t i = 0; i< mActivationCount.size(); ++i) {
         Info& info = mActivationCount.editValueAt(i);
         if (info.batchParams.isEmpty()) continue;
@@ -355,27 +404,18 @@
         const int sensor_handle = mActivationCount.keyAt(i);
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
                    sensor_handle);
-        status_t err(NO_ERROR);
-        if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
-            err = mSensorDevice->batch(mSensorDevice, sensor_handle,
-                 info.bestBatchParams.flags, info.bestBatchParams.batchDelay,
-                 info.bestBatchParams.batchTimeout);
-            ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
-        }
+        status_t err = StatusFromResult(
+                checkReturn(mSensors->batch(
+                    sensor_handle,
+                    info.bestBatchParams.batchDelay,
+                    info.bestBatchParams.batchTimeout)));
+        ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
 
         if (err == NO_ERROR) {
-            err = mSensorDevice->activate(
-                    reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                    sensor_handle, 1);
+            err = StatusFromResult(
+                    checkReturn(mSensors->activate(sensor_handle, 1 /* enabled */)));
             ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
         }
-
-        if (halVersion <= SENSORS_DEVICE_API_VERSION_1_0) {
-             err = mSensorDevice->setDelay(
-                    reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),
-                    sensor_handle, info.bestBatchParams.batchDelay);
-             ALOGE_IF(err, "Error calling setDelay sensor %d (%s)", sensor_handle, strerror(-err));
-        }
     }
 }
 
@@ -388,37 +428,39 @@
            const int sensor_handle = mActivationCount.keyAt(i);
            ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
                    sensor_handle);
-           mSensorDevice->activate(
-                   reinterpret_cast<struct sensors_poll_device_t *> (mSensorDevice),
-                   sensor_handle, 0);
+           checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */));
+
            // Add all the connections that were registered for this sensor to the disabled
            // clients list.
            for (size_t j = 0; j < info.batchParams.size(); ++j) {
                mDisabledClients.add(info.batchParams.keyAt(j));
+               ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
            }
         }
     }
 }
 
-status_t SensorDevice::injectSensorData(const sensors_event_t *injected_sensor_event) {
-      ALOGD_IF(DEBUG_CONNECTIONS,
-              "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
-               injected_sensor_event->sensor,
-               injected_sensor_event->timestamp, injected_sensor_event->data[0],
-               injected_sensor_event->data[1], injected_sensor_event->data[2],
-               injected_sensor_event->data[3], injected_sensor_event->data[4],
-               injected_sensor_event->data[5]);
-      if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
-          return INVALID_OPERATION;
-      }
-      return mSensorDevice->inject_sensor_data(mSensorDevice, injected_sensor_event);
+status_t SensorDevice::injectSensorData(
+        const sensors_event_t *injected_sensor_event) {
+    ALOGD_IF(DEBUG_CONNECTIONS,
+            "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
+            injected_sensor_event->sensor,
+            injected_sensor_event->timestamp, injected_sensor_event->data[0],
+            injected_sensor_event->data[1], injected_sensor_event->data[2],
+            injected_sensor_event->data[3], injected_sensor_event->data[4],
+            injected_sensor_event->data[5]);
+
+    Event ev;
+    convertFromSensorEvent(*injected_sensor_event, &ev);
+
+    return StatusFromResult(checkReturn(mSensors->injectSensorData(ev)));
 }
 
 status_t SensorDevice::setMode(uint32_t mode) {
-     if (getHalDeviceVersion() < SENSORS_DEVICE_API_VERSION_1_4) {
-          return INVALID_OPERATION;
-     }
-     return mSensorModule->set_operation_mode(mode);
+
+     return StatusFromResult(
+             checkReturn(mSensors->setOperationMode(
+                 static_cast<hardware::sensors::V1_0::OperationMode>(mode))));
 }
 
 // ---------------------------------------------------------------------------
@@ -480,6 +522,145 @@
     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;
+    checkReturn(mSensors->registerDirectChannel(mem,
+            [&ret](auto result, auto channelHandle) {
+                if (result == Result::OK) {
+                    ret = channelHandle;
+                } else {
+                    ret = StatusFromResult(result);
+                }
+            }));
+    return ret;
+}
+
+void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
+    Mutex::Autolock _l(mLock);
+    checkReturn(mSensors->unregisterDirectChannel(channelHandle));
+}
+
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
+        int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
+    Mutex::Autolock _l(mLock);
+
+    RateLevel rate;
+    switch(config->rate_level) {
+        case SENSOR_DIRECT_RATE_STOP:
+            rate = RateLevel::STOP;
+            break;
+        case SENSOR_DIRECT_RATE_NORMAL:
+            rate = RateLevel::NORMAL;
+            break;
+        case SENSOR_DIRECT_RATE_FAST:
+            rate = RateLevel::FAST;
+            break;
+        case SENSOR_DIRECT_RATE_VERY_FAST:
+            rate = RateLevel::VERY_FAST;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    int32_t ret;
+    checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
+            [&ret, rate] (auto result, auto token) {
+                if (rate == RateLevel::STOP) {
+                    ret = StatusFromResult(result);
+                } else {
+                    if (result == Result::OK) {
+                        ret = token;
+                    } else {
+                        ret = StatusFromResult(result);
+                    }
+                }
+            }));
+
+    return ret;
+}
+
+bool SensorDevice::isDirectReportSupported() const {
+    return mIsDirectReportSupported;
+}
+
+void SensorDevice::convertToSensorEvent(
+        const Event &src, sensors_event_t *dst) {
+    ::android::hardware::sensors::V1_0::implementation::convertToSensorEvent(
+            src, dst);
+
+    if (src.sensorType == SensorType::DYNAMIC_SENSOR_META) {
+        const DynamicSensorInfo &dyn = src.u.dynamic;
+
+        dst->dynamic_sensor_meta.connected = dyn.connected;
+        dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
+        if (dyn.connected) {
+            auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
+            CHECK(it != mConnectedDynamicSensors.end());
+
+            dst->dynamic_sensor_meta.sensor = it->second;
+
+            memcpy(dst->dynamic_sensor_meta.uuid,
+                   dyn.uuid.data(),
+                   sizeof(dst->dynamic_sensor_meta.uuid));
+        }
+    }
+}
+
+void SensorDevice::convertToSensorEvents(
+        const hidl_vec<Event> &src,
+        const hidl_vec<SensorInfo> &dynamicSensorsAdded,
+        sensors_event_t *dst) {
+    // Allocate a sensor_t structure for each dynamic sensor added and insert
+    // it into the dictionary of connected dynamic sensors keyed by handle.
+    for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
+        const SensorInfo &info = dynamicSensorsAdded[i];
+
+        auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+        CHECK(it == mConnectedDynamicSensors.end());
+
+        sensor_t *sensor = new sensor_t;
+        convertToSensor(info, sensor);
+
+        mConnectedDynamicSensors.insert(
+                std::make_pair(sensor->handle, sensor));
+    }
+
+    for (size_t i = 0; i < src.size(); ++i) {
+        convertToSensorEvent(src[i], &dst[i]);
+    }
+}
+
+void SensorDevice::handleHidlDeath(const std::string & detail) {
+    // restart is the only option at present.
+    LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
-
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index d340da3..03552f6 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -19,27 +19,36 @@
 
 #include "SensorServiceUtils.h"
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
+#include <stdint.h>
+#include <sys/types.h>
 #include <utils/KeyedVector.h>
 #include <utils/Singleton.h>
 #include <utils/String8.h>
 
-#include <stdint.h>
-#include <sys/types.h>
+#include <string>
+#include <map>
+
+#include "android/hardware/sensors/1.0/ISensors.h"
 
 // ---------------------------------------------------------------------------
 
 namespace android {
+
 // ---------------------------------------------------------------------------
 using SensorServiceUtil::Dumpable;
+using hardware::Return;
 
 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 +56,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 +74,11 @@
     virtual std::string dump() const;
 private:
     friend class Singleton<SensorDevice>;
-    sensors_poll_device_1_t* mSensorDevice;
-    struct sensors_module_t* mSensorModule;
+
+    sp<android::hardware::sensors::V1_0::ISensors> mSensors;
+    Vector<sensor_t> mSensorList;
+    std::map<int32_t, sensor_t*> mConnectedDynamicSensors;
+
     static const nsecs_t MINIMUM_EVENTS_PERIOD =   1000000; // 1000 Hz
     mutable Mutex mLock; // protect mActivationCount[].batchParams
     // fixed-size array after construction
@@ -109,8 +129,29 @@
     SortedVector<void *> mDisabledClients;
     SensorDevice();
 
+    static void handleHidlDeath(const std::string &detail);
+    template<typename T>
+    static Return<T> checkReturn(Return<T> &&ret) {
+        if (!ret.isOk()) {
+            handleHidlDeath(ret.description());
+        }
+        return std::move(ret);
+    }
+
     bool isClientDisabled(void* ident);
     bool isClientDisabledLocked(void* ident);
+
+    using Event = hardware::sensors::V1_0::Event;
+    using SensorInfo = hardware::sensors::V1_0::SensorInfo;
+
+    void convertToSensorEvent(const Event &src, sensors_event_t *dst);
+
+    void convertToSensorEvents(
+            const hardware::hidl_vec<Event> &src,
+            const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded,
+            sensors_event_t *dst);
+
+    bool mIsDirectReportSupported;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
new file mode 100644
index 0000000..b096e1c
--- /dev/null
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -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.
+ */
+
+#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;
+                }
+                break;
+            }
+            case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+                LOG_FATAL("%s: Implement GRALLOC or remove", __FUNCTION__);
+                ret = true;
+                break;
+            default:
+                ALOGE("Unexpected mem type %d", mMem.type);
+                ret = true;
+                break;
+        }
+    }
+    return ret;
+}
+
+} // namespace android
+
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
new file mode 100644
index 0000000..27458d4
--- /dev/null
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
+#define ANDROID_SENSOR_DIRECT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/BinderService.h>
+
+#include <sensor/Sensor.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
+
+#include "SensorService.h"
+
+namespace android {
+
+class SensorService;
+class BitTube;
+
+class SensorService::SensorDirectConnection: public BnSensorEventConnection {
+public:
+    SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
+            const sensors_direct_mem_t *mem, int32_t halChannelHandle,
+            const String16& opPackageName);
+    void dump(String8& result) const;
+    uid_t getUid() const { return mUid; }
+    int32_t getHalChannelHandle() const;
+    bool isEquivalent(const sensors_direct_mem_t *mem) const;
+
+    // stop all active sensor report. if backupRecord is set to false,
+    // those report can be recovered by recoverAll
+    // called by SensorService when enter restricted mode
+    void stopAll(bool clearRecord = false);
+
+    // recover sensor reports previously stopped by stopAll(true)
+    // called by SensorService when return to NORMAL mode.
+    void recoverAll();
+
+protected:
+    virtual ~SensorDirectConnection();
+    // ISensorEventConnection functions
+    virtual void onFirstRef();
+    virtual sp<BitTube> getSensorChannel() const;
+    virtual status_t enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs,
+                                   nsecs_t maxBatchReportLatencyNs, int reservedFlags);
+    virtual status_t setEventRate(int handle, nsecs_t samplingPeriodNs);
+    virtual status_t flush();
+    virtual int32_t configureChannel(int handle, int rateLevel);
+
+private:
+    const sp<SensorService> mService;
+    const uid_t mUid;
+    const sensors_direct_mem_t mMem;
+    const int32_t mHalChannelHandle;
+    const String16 mOpPackageName;
+
+    mutable Mutex mConnectionLock;
+    std::unordered_map<int, int> mActivated;
+    std::unordered_map<int, int> mActivatedBackup;
+};
+
+} // namepsace android
+
+#endif // ANDROID_SENSOR_DIRECT_CONNECTION_H
+
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index f2f1444..fad046c 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -17,12 +17,14 @@
 #include <sys/socket.h>
 #include <utils/threads.h>
 
-#include <gui/SensorEventQueue.h>
+#include <sensor/SensorEventQueue.h>
 
 #include "vec.h"
 #include "SensorEventConnection.h"
 #include "SensorDevice.h"
 
+#define UNUSED(x) (void)(x)
+
 namespace android {
 
 SensorService::SensorEventConnection::SensorEventConnection(
@@ -524,6 +526,13 @@
     return  mService->flushSensor(this, mOpPackageName);
 }
 
+int32_t SensorService::SensorEventConnection::configureChannel(int handle, int rateLevel) {
+    // SensorEventConnection does not support configureChannel, parameters not used
+    UNUSED(handle);
+    UNUSED(rateLevel);
+    return INVALID_OPERATION;
+}
+
 int SensorService::SensorEventConnection::handleEvent(int fd, int events, void* /*data*/) {
     if (events & ALOOPER_EVENT_HANGUP || events & ALOOPER_EVENT_ERROR) {
         {
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 883c16e..c81e015 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -31,10 +31,10 @@
 
 #include <binder/BinderService.h>
 
-#include <gui/Sensor.h>
-#include <gui/BitTube.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
+#include <sensor/BitTube.h>
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
 
 #include "SensorService.h"
 
@@ -74,6 +74,8 @@
                                    nsecs_t maxBatchReportLatencyNs, int reservedFlags);
     virtual status_t setEventRate(int handle, nsecs_t samplingPeriodNs);
     virtual status_t flush();
+    virtual int32_t configureChannel(int handle, int rateLevel);
+
     // Count the number of flush complete events which are about to be dropped in the buffer.
     // Increment mPendingFlushEventsToSend in mSensorInfo. These flush complete events will be sent
     // separately before the next batch of events.
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index 9863f62..414f673 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -163,7 +163,7 @@
     }
     mSensorDevice.batch(ident, mAcc.getHandle(), 0, ns, 0);
     if (mode != FUSION_NOMAG) {
-        mSensorDevice.batch(ident, mMag.getHandle(), 0, ms2ns(20), 0);
+        mSensorDevice.batch(ident, mMag.getHandle(), 0, ms2ns(10), 0);
     }
     if (mode != FUSION_NOGYRO) {
         mSensorDevice.batch(ident, mGyro.getHandle(), 0, mTargetDelayNs, 0);
diff --git a/services/sensorservice/SensorFusion.h b/services/sensorservice/SensorFusion.h
index ad636d5..8c0fbf9 100644
--- a/services/sensorservice/SensorFusion.h
+++ b/services/sensorservice/SensorFusion.h
@@ -24,7 +24,7 @@
 #include <utils/Singleton.h>
 #include <utils/String8.h>
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 
 #include "Fusion.h"
 
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index 0867dc2..b5375cb 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_SENSOR_INTERFACE_H
 #define ANDROID_SENSOR_INTERFACE_H
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 #include <utils/RefBase.h>
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index e0101c1..ab08cac 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -124,7 +124,7 @@
     forEachSensor([&result] (const Sensor& s) -> bool {
             result.appendFormat(
                     "%#010x) %-25s | %-15s | ver: %" PRId32 " | type: %20s(%" PRId32
-                        ") | perm: %s\n\t",
+                        ") | perm: %s\n",
                     s.getHandle(),
                     s.getName().string(),
                     s.getVendor().string(),
@@ -133,17 +133,18 @@
                     s.getType(),
                     s.getRequiredPermission().size() ? s.getRequiredPermission().string() : "n/a");
 
+            result.append("\t");
             const int reportingMode = s.getReportingMode();
             if (reportingMode == AREPORTING_MODE_CONTINUOUS) {
-                result.append(" continuous | ");
+                result.append("continuous | ");
             } else if (reportingMode == AREPORTING_MODE_ON_CHANGE) {
-                result.append(" on-change | ");
+                result.append("on-change | ");
             } else if (reportingMode == AREPORTING_MODE_ONE_SHOT) {
-                result.append(" one-shot | ");
+                result.append("one-shot | ");
             } else if (reportingMode == AREPORTING_MODE_SPECIAL_TRIGGER) {
-                result.append(" special-trigger | ");
+                result.append("special-trigger | ");
             } else {
-                result.append(" unknown-mode | ");
+                result.append("unknown-mode | ");
             }
 
             if (s.getMaxDelay() > 0) {
@@ -178,8 +179,20 @@
             if (s.hasAdditionalInfo()) {
                 result.appendFormat("has-additional-info, ");
             }
-
             result.append("\n");
+
+            if (s.getHighestDirectReportRateLevel() > SENSOR_DIRECT_RATE_STOP) {
+                result.appendFormat("\thighest rate level = %d, support shared mem: ",
+                        s.getHighestDirectReportRateLevel());
+                if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_ASHMEM)) {
+                    result.append("ashmem, ");
+                }
+                if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC)) {
+                    result.append("gralloc, ");
+                }
+                result.appendFormat("flag =0x%08x", static_cast<int>(s.getFlags()));
+                result.append("\n");
+            }
             return true;
         });
     return std::string(result.string());
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 8209d96..6b90ad9 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -20,7 +20,7 @@
 #include "SensorInterface.h"
 #include "SensorServiceUtils.h"
 
-#include <gui/Sensor.h>
+#include <sensor/Sensor.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 7b47709..c11df13 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -21,7 +21,8 @@
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
 
-#include <gui/SensorEventQueue.h>
+#include <cutils/ashmem.h>
+#include <sensor/SensorEventQueue.h>
 
 #include <hardware/sensors.h>
 #include <hardware_legacy/power.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..5583dad 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -22,9 +22,9 @@
 
 #include <binder/BinderService.h>
 #include <cutils/compiler.h>
-#include <gui/ISensorServer.h>
-#include <gui/ISensorEventConnection.h>
-#include <gui/Sensor.h>
+#include <sensor/ISensorServer.h>
+#include <sensor/ISensorEventConnection.h>
+#include <sensor/Sensor.h>
 
 #include <utils/AndroidThreads.h>
 #include <utils/KeyedVector.h>
@@ -67,9 +67,11 @@
 {
     // nested class/struct for internal use
     class SensorEventConnection;
+    class SensorDirectConnection;
 
 public:
     void cleanupConnection(SensorEventConnection* connection);
+    void cleanupConnection(SensorDirectConnection* c);
 
     status_t enable(const sp<SensorEventConnection>& connection, int handle,
                     nsecs_t samplingPeriodNs,  nsecs_t maxBatchReportLatencyNs, int reservedFlags,
@@ -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/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index 1996a00..34cd8dd 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -54,6 +54,7 @@
         case SENSOR_TYPE_STATIONARY_DETECT:
         case SENSOR_TYPE_MOTION_DETECT:
         case SENSOR_TYPE_HEART_BEAT:
+        case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT:
             return 1;
 
         default:
diff --git a/services/sensorservice/tests/Android.mk b/services/sensorservice/tests/Android.mk
index 45296dd..e894655 100644
--- a/services/sensorservice/tests/Android.mk
+++ b/services/sensorservice/tests/Android.mk
@@ -5,7 +5,7 @@
 	sensorservicetest.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libcutils libutils libui libgui
+	libutils libsensor libandroid
 
 LOCAL_MODULE:= test-sensorservice
 
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 186b60c..1cb0489 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -16,9 +16,9 @@
 
 #include <inttypes.h>
 #include <android/sensor.h>
-#include <gui/Sensor.h>
-#include <gui/SensorManager.h>
-#include <gui/SensorEventQueue.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorManager.h>
+#include <sensor/SensorEventQueue.h>
 #include <utils/Looper.h>
 
 using namespace android;
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
new file mode 100644
index 0000000..cc93105
--- /dev/null
+++ b/services/surfaceflinger/Android.bp
@@ -0,0 +1,4 @@
+cc_library_static {
+    name: "libsurfaceflingerincludes",
+    export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index be537dc..a317ea2 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -9,19 +9,24 @@
     DisplayDevice.cpp \
     DispSync.cpp \
     EventControlThread.cpp \
+    StartBootAnimThread.cpp \
     EventThread.cpp \
-    FenceTracker.cpp \
     FrameTracker.cpp \
     GpuService.cpp \
     Layer.cpp \
     LayerDim.cpp \
+    LayerRejecter.cpp \
+    LayerVector.cpp \
     MessageQueue.cpp \
     MonitoredProducer.cpp \
     SurfaceFlingerConsumer.cpp \
+    SurfaceInterceptor.cpp \
     Transform.cpp \
+    DisplayHardware/ComposerHal.cpp \
     DisplayHardware/FramebufferSurface.cpp \
     DisplayHardware/HWC2.cpp \
     DisplayHardware/HWC2On1Adapter.cpp \
+    DisplayHardware/MiniFence.cpp \
     DisplayHardware/PowerHAL.cpp \
     DisplayHardware/VirtualDisplaySurface.cpp \
     Effects/Daltonizer.cpp \
@@ -34,13 +39,13 @@
     RenderEngine/GLExtensions.cpp \
     RenderEngine/RenderEngine.cpp \
     RenderEngine/Texture.cpp \
-    RenderEngine/GLES10RenderEngine.cpp \
-    RenderEngine/GLES11RenderEngine.cpp \
-    RenderEngine/GLES20RenderEngine.cpp
+    RenderEngine/GLES20RenderEngine.cpp \
 
+LOCAL_MODULE := libsurfaceflinger
 LOCAL_C_INCLUDES := \
-	frameworks/native/vulkan/include \
-	external/vulkan-validation-layers/libs/vkjson
+    frameworks/native/vulkan/include \
+    external/vulkan-validation-layers/libs/vkjson \
+    system/libhwbinder/fast_msgq/include \
 
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
@@ -49,8 +54,13 @@
     LOCAL_CFLAGS += -DUSE_HWC2
     LOCAL_SRC_FILES += \
         SurfaceFlinger.cpp \
+        VrStateCallbacks.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 +73,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 +105,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 +132,29 @@
 
 LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
 
-LOCAL_STATIC_LIBRARIES := libvkjson
+LOCAL_HEADER_LIBRARIES := \
+    android.hardware.configstore-utils
+
+LOCAL_STATIC_LIBRARIES := \
+    libhwcomposer-command-buffer \
+    libtrace_proto \
+    libvkjson \
+    libvr_manager \
+    libvrflinger
+
 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 +163,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
 
@@ -172,9 +205,11 @@
     liblog \
     libbinder \
     libutils \
+    libui \
     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..9ddae2b 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -35,7 +35,13 @@
 // ---------------------------------------------------------------------------
 
 Client::Client(const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger)
+    : Client(flinger, nullptr)
+{
+}
+
+Client::Client(const sp<SurfaceFlinger>& flinger, const sp<Layer>& parentLayer)
+    : mFlinger(flinger),
+      mParentLayer(parentLayer)
 {
 }
 
@@ -43,10 +49,17 @@
 {
     const size_t count = mLayers.size();
     for (size_t i=0 ; i<count ; i++) {
-        mFlinger->removeLayer(mLayers.valueAt(i));
+        sp<Layer> l = mLayers.valueAt(i).promote();
+        if (l != nullptr) {
+            mFlinger->removeLayer(l);
+        }
     }
 }
 
+void Client::setParentLayer(const sp<Layer>& parentLayer) {
+    mParentLayer = parentLayer;
+}
+
 status_t Client::initCheck() const {
     return NO_ERROR;
 }
@@ -90,12 +103,17 @@
      const int pid = ipc->getCallingPid();
      const int uid = ipc->getCallingUid();
      const int self_pid = getpid();
-     if (CC_UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != 0)) {
+     // If we are called from another non root process without the GRAPHICS, SYSTEM, or ROOT
+     // uid we require the sAccessSurfaceFlinger permission.
+     // We grant an exception in the case that the Client has a "parent layer", as its
+     // effects will be scoped to that layer.
+     if (CC_UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != 0)
+             && (mParentLayer.promote() == nullptr)) {
          // we're called from a different process, do the real check
          if (!PermissionCache::checkCallingPermission(sAccessSurfaceFlinger))
          {
              ALOGE("Permission Denial: "
-                     "can't openGlobalTransaction pid=%d, uid=%d", pid, uid);
+                     "can't openGlobalTransaction pid=%d, uid<=%d", pid, uid);
              return PERMISSION_DENIED;
          }
      }
@@ -106,14 +124,30 @@
 status_t Client::createSurface(
         const String8& name,
         uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
+        const sp<IBinder>& parentHandle, uint32_t windowType, uint32_t ownerUid,
         sp<IBinder>* handle,
         sp<IGraphicBufferProducer>* gbp)
 {
+    sp<Layer> parent = nullptr;
+    if (parentHandle != nullptr) {
+        parent = getLayerUser(parentHandle);
+        if (parent == nullptr) {
+            return NAME_NOT_FOUND;
+        }
+    }
+    if (parent == nullptr && mParentLayer != nullptr) {
+        parent = mParentLayer.promote();
+        // If we had a parent, but it died, we've lost all
+        // our capabilities.
+        if (parent == nullptr) {
+            return NAME_NOT_FOUND;
+        }
+    }
+
     /*
      * createSurface must be called from the GL thread so that it can
      * have access to the GL context.
      */
-
     class MessageCreateLayer : public MessageBase {
         SurfaceFlinger* flinger;
         Client* client;
@@ -124,26 +158,32 @@
         uint32_t w, h;
         PixelFormat format;
         uint32_t flags;
+        sp<Layer>* parent;
+        uint32_t windowType;
+        uint32_t ownerUid;
     public:
         MessageCreateLayer(SurfaceFlinger* flinger,
                 const String8& name, Client* client,
                 uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-                sp<IBinder>* handle,
-                sp<IGraphicBufferProducer>* gbp)
+                sp<IBinder>* handle, uint32_t windowType, uint32_t ownerUid,
+                sp<IGraphicBufferProducer>* gbp,
+                sp<Layer>* parent)
             : flinger(flinger), client(client),
               handle(handle), gbp(gbp), result(NO_ERROR),
-              name(name), w(w), h(h), format(format), flags(flags) {
+              name(name), w(w), h(h), format(format), flags(flags),
+              parent(parent), windowType(windowType), ownerUid(ownerUid) {
         }
         status_t getResult() const { return result; }
         virtual bool handler() {
             result = flinger->createLayer(name, client, w, h, format, flags,
-                    handle, gbp);
+                    windowType, ownerUid, handle, gbp, parent);
             return true;
         }
     };
 
     sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
-            name, this, w, h, format, flags, handle, gbp);
+            name, this, w, h, format, flags, handle,
+            windowType, ownerUid, gbp, &parent);
     mFlinger->postMessageSync(msg);
     return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
 }
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..9af4402 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -47,6 +47,9 @@
 #include "SurfaceFlinger.h"
 #include "Layer.h"
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
 // ----------------------------------------------------------------------------
 using namespace android;
 // ----------------------------------------------------------------------------
@@ -57,6 +60,14 @@
 static constexpr bool kEGLAndroidSwapRectangle = false;
 #endif
 
+// retrieve triple buffer setting from configstore
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
+static bool useTripleFramebuffer = getBool<
+        ISurfaceFlingerConfigs,
+        &ISurfaceFlingerConfigs::useTripleFramebuffer>(false);
+
 #if !defined(EGL_EGLEXT_PROTOTYPES) || !defined(EGL_ANDROID_swap_rectangle)
 // Dummy implementation in case it is missing.
 inline void eglSetSwapRectangleANDROID (EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint) {
@@ -165,9 +176,9 @@
     // initialize the display orientation transform.
     setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
 
-#ifdef NUM_FRAMEBUFFER_SURFACE_BUFFERS
-    surface->allocateBuffers();
-#endif
+    if (useTripleFramebuffer) {
+        surface->allocateBuffers();
+    }
 }
 
 DisplayDevice::~DisplayDevice() {
@@ -613,3 +624,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..caa7adc 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -242,7 +242,11 @@
     static status_t orientationToTransfrom(int orientation,
             int w, int h, Transform* tr);
 
+    // The identifier of the active layer stack for this display. Several displays
+    // can use the same layer stack: A z-ordered group of layers (sometimes called
+    // "surfaces"). Any given layer can only be on a single layer stack.
     uint32_t mLayerStack;
+
     int mOrientation;
     static uint32_t sPrimaryDisplayOrientation;
     // user-provided visible area of the layer stack
@@ -263,6 +267,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..c6e6dcb
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -0,0 +1,985 @@
+/*
+ * 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 <gui/BufferQueue.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(bool useVrComposer)
+    : mWriter(kWriterInitialSize),
+      mIsUsingVrComposer(useVrComposer)
+{
+    if (mIsUsingVrComposer) {
+        mComposer = IComposer::getService("vr_hwcomposer");
+    } else {
+        mComposer = IComposer::getService(); // use default name
+    }
+
+    if (mComposer == nullptr) {
+        LOG_ALWAYS_FATAL("failed to get hwcomposer service");
+    }
+
+    mComposer->createClient(
+            [&](const auto& tmpError, const auto& tmpClient)
+            {
+                if (tmpError == Error::NONE) {
+                    mClient = tmpClient;
+                }
+            });
+    if (mClient == nullptr) {
+        LOG_ALWAYS_FATAL("failed to create composer client");
+    }
+}
+
+std::vector<IComposer::Capability> Composer::getCapabilities()
+{
+    std::vector<IComposer::Capability> capabilities;
+    mComposer->getCapabilities(
+            [&](const auto& tmpCapabilities) {
+                capabilities = tmpCapabilities;
+            });
+
+    return capabilities;
+}
+
+std::string Composer::dumpDebugInfo()
+{
+    std::string info;
+    mComposer->dumpDebugInfo([&](const auto& tmpInfo) {
+        info = tmpInfo.c_str();
+    });
+
+    return info;
+}
+
+void Composer::registerCallback(const sp<IComposerCallback>& callback)
+{
+    auto ret = mClient->registerCallback(callback);
+    if (!ret.isOk()) {
+        ALOGE("failed to register IComposerCallback");
+    }
+}
+
+uint32_t Composer::getMaxVirtualDisplayCount()
+{
+    auto ret = mClient->getMaxVirtualDisplayCount();
+    return unwrapRet(ret, 0);
+}
+
+Error Composer::createVirtualDisplay(uint32_t width, uint32_t height,
+            PixelFormat* format, Display* outDisplay)
+{
+    const uint32_t bufferSlotCount = 1;
+    Error error = kDefaultError;
+    mClient->createVirtualDisplay(width, height, *format, bufferSlotCount,
+            [&](const auto& tmpError, const auto& tmpDisplay,
+                const auto& tmpFormat) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outDisplay = tmpDisplay;
+                *format = tmpFormat;
+            });
+
+    return error;
+}
+
+Error Composer::destroyVirtualDisplay(Display display)
+{
+    auto ret = mClient->destroyVirtualDisplay(display);
+    return unwrapRet(ret);
+}
+
+Error Composer::acceptDisplayChanges(Display display)
+{
+    mWriter.selectDisplay(display);
+    mWriter.acceptDisplayChanges();
+    return Error::NONE;
+}
+
+Error Composer::createLayer(Display display, Layer* outLayer)
+{
+    Error error = kDefaultError;
+    mClient->createLayer(display, BufferQueue::NUM_BUFFER_SLOTS,
+            [&](const auto& tmpError, const auto& tmpLayer) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outLayer = tmpLayer;
+            });
+
+    return error;
+}
+
+Error Composer::destroyLayer(Display display, Layer layer)
+{
+    auto ret = mClient->destroyLayer(display, layer);
+    return unwrapRet(ret);
+}
+
+Error Composer::getActiveConfig(Display display, Config* outConfig)
+{
+    Error error = kDefaultError;
+    mClient->getActiveConfig(display,
+            [&](const auto& tmpError, const auto& tmpConfig) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outConfig = tmpConfig;
+            });
+
+    return error;
+}
+
+Error Composer::getChangedCompositionTypes(Display display,
+        std::vector<Layer>* outLayers,
+        std::vector<IComposerClient::Composition>* outTypes)
+{
+    mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
+    return Error::NONE;
+}
+
+Error Composer::getColorModes(Display display,
+        std::vector<ColorMode>* outModes)
+{
+    Error error = kDefaultError;
+    mClient->getColorModes(display,
+            [&](const auto& tmpError, const auto& tmpModes) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outModes = tmpModes;
+            });
+
+    return error;
+}
+
+Error Composer::getDisplayAttribute(Display display, Config config,
+        IComposerClient::Attribute attribute, int32_t* outValue)
+{
+    Error error = kDefaultError;
+    mClient->getDisplayAttribute(display, config, attribute,
+            [&](const auto& tmpError, const auto& tmpValue) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outValue = tmpValue;
+            });
+
+    return error;
+}
+
+Error Composer::getDisplayConfigs(Display display,
+        std::vector<Config>* outConfigs)
+{
+    Error error = kDefaultError;
+    mClient->getDisplayConfigs(display,
+            [&](const auto& tmpError, const auto& tmpConfigs) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outConfigs = tmpConfigs;
+            });
+
+    return error;
+}
+
+Error Composer::getDisplayName(Display display, std::string* outName)
+{
+    Error error = kDefaultError;
+    mClient->getDisplayName(display,
+            [&](const auto& tmpError, const auto& tmpName) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outName = tmpName.c_str();
+            });
+
+    return error;
+}
+
+Error Composer::getDisplayRequests(Display display,
+        uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
+        std::vector<uint32_t>* outLayerRequestMasks)
+{
+    mReader.takeDisplayRequests(display, outDisplayRequestMask,
+            outLayers, outLayerRequestMasks);
+    return Error::NONE;
+}
+
+Error Composer::getDisplayType(Display display,
+        IComposerClient::DisplayType* outType)
+{
+    Error error = kDefaultError;
+    mClient->getDisplayType(display,
+            [&](const auto& tmpError, const auto& tmpType) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outType = tmpType;
+            });
+
+    return error;
+}
+
+Error Composer::getDozeSupport(Display display, bool* outSupport)
+{
+    Error error = kDefaultError;
+    mClient->getDozeSupport(display,
+            [&](const auto& tmpError, const auto& tmpSupport) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outSupport = tmpSupport;
+            });
+
+    return error;
+}
+
+Error Composer::getHdrCapabilities(Display display,
+        std::vector<Hdr>* outTypes, float* outMaxLuminance,
+        float* outMaxAverageLuminance, float* outMinLuminance)
+{
+    Error error = kDefaultError;
+    mClient->getHdrCapabilities(display,
+            [&](const auto& tmpError, const auto& tmpTypes,
+                const auto& tmpMaxLuminance,
+                const auto& tmpMaxAverageLuminance,
+                const auto& tmpMinLuminance) {
+                error = tmpError;
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                *outTypes = tmpTypes;
+                *outMaxLuminance = tmpMaxLuminance;
+                *outMaxAverageLuminance = tmpMaxAverageLuminance;
+                *outMinLuminance = tmpMinLuminance;
+            });
+
+    return error;
+}
+
+Error Composer::getReleaseFences(Display display,
+        std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
+{
+    mReader.takeReleaseFences(display, outLayers, outReleaseFences);
+    return Error::NONE;
+}
+
+Error Composer::presentDisplay(Display display, int* outPresentFence)
+{
+    mWriter.selectDisplay(display);
+    mWriter.presentDisplay();
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    mReader.takePresentFence(display, outPresentFence);
+
+    return Error::NONE;
+}
+
+Error Composer::setActiveConfig(Display display, Config config)
+{
+    auto ret = mClient->setActiveConfig(display, config);
+    return unwrapRet(ret);
+}
+
+Error Composer::setClientTarget(Display display, uint32_t slot,
+        const native_handle_t* target,
+        int acquireFence, Dataspace dataspace,
+        const std::vector<IComposerClient::Rect>& damage)
+{
+    mWriter.selectDisplay(display);
+    mWriter.setClientTarget(slot, 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 = BufferQueue::NUM_BUFFER_SLOTS;
+    auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
+    return unwrapRet(ret);
+}
+
+Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
+        uint32_t* outNumRequests)
+{
+    mWriter.selectDisplay(display);
+    mWriter.validateDisplay();
+
+    Error error = execute();
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    mReader.hasChanges(display, outNumTypes, outNumRequests);
+
+    return Error::NONE;
+}
+
+Error Composer::setCursorPosition(Display display, Layer layer,
+        int32_t x, int32_t y)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerCursorPosition(x, y);
+    return Error::NONE;
+}
+
+Error Composer::setLayerBuffer(Display display, Layer layer,
+        uint32_t slot, const native_handle_t* buffer, int acquireFence)
+{
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerBuffer(slot, 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 (mIsUsingVrComposer) {
+        mWriter.selectDisplay(display);
+        mWriter.selectLayer(layer);
+        mWriter.setLayerInfo(type, appId);
+    }
+    return Error::NONE;
+}
+
+Error Composer::execute()
+{
+    // prepare input command queue
+    bool queueChanged = false;
+    uint32_t commandLength = 0;
+    hidl_vec<hidl_handle> commandHandles;
+    if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
+        mWriter.reset();
+        return Error::NO_RESOURCES;
+    }
+
+    // set up new input command queue if necessary
+    if (queueChanged) {
+        auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
+        auto error = unwrapRet(ret);
+        if (error != Error::NONE) {
+            mWriter.reset();
+            return error;
+        }
+    }
+
+    Error error = kDefaultError;
+    mClient->executeCommands(commandLength, commandHandles,
+            [&](const auto& tmpError, const auto& tmpOutChanged,
+                const auto& tmpOutLength, const auto& tmpOutHandles)
+            {
+                error = tmpError;
+
+                // set up new output command queue if necessary
+                if (error == Error::NONE && tmpOutChanged) {
+                    error = kDefaultError;
+                    mClient->getOutputCommandQueue(
+                            [&](const auto& tmpError,
+                                const auto& tmpDescriptor)
+                            {
+                                error = tmpError;
+                                if (error != Error::NONE) {
+                                    return;
+                                }
+
+                                mReader.setMQDescriptor(tmpDescriptor);
+                            });
+                }
+
+                if (error != Error::NONE) {
+                    return;
+                }
+
+                if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
+                    error = mReader.parse();
+                    mReader.reset();
+                } else {
+                    error = Error::NO_RESOURCES;
+                }
+            });
+
+    if (error == Error::NONE) {
+        std::vector<CommandReader::CommandError> commandErrors =
+            mReader.takeErrors();
+
+        for (const auto& cmdErr : commandErrors) {
+            auto command = mWriter.getCommand(cmdErr.location);
+
+            if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
+                command == IComposerClient::Command::PRESENT_DISPLAY) {
+                error = cmdErr.error;
+            } else {
+                ALOGW("command 0x%x generated error %d",
+                        command, cmdErr.error);
+            }
+        }
+    }
+
+    mWriter.reset();
+
+    return error;
+}
+
+CommandReader::~CommandReader()
+{
+    resetData();
+}
+
+Error CommandReader::parse()
+{
+    resetData();
+
+    IComposerClient::Command command;
+    uint16_t length = 0;
+
+    while (!isEmpty()) {
+        if (!beginCommand(&command, &length)) {
+            break;
+        }
+
+        bool parsed = false;
+        switch (command) {
+        case IComposerClient::Command::SELECT_DISPLAY:
+            parsed = parseSelectDisplay(length);
+            break;
+        case IComposerClient::Command::SET_ERROR:
+            parsed = parseSetError(length);
+            break;
+        case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
+            parsed = parseSetChangedCompositionTypes(length);
+            break;
+        case IComposerClient::Command::SET_DISPLAY_REQUESTS:
+            parsed = parseSetDisplayRequests(length);
+            break;
+        case IComposerClient::Command::SET_PRESENT_FENCE:
+            parsed = parseSetPresentFence(length);
+            break;
+        case IComposerClient::Command::SET_RELEASE_FENCES:
+            parsed = parseSetReleaseFences(length);
+            break;
+        default:
+            parsed = false;
+            break;
+        }
+
+        endCommand();
+
+        if (!parsed) {
+            ALOGE("failed to parse command 0x%x length %" PRIu16,
+                    command, length);
+            break;
+        }
+    }
+
+    return isEmpty() ? Error::NONE : Error::NO_RESOURCES;
+}
+
+bool CommandReader::parseSelectDisplay(uint16_t length)
+{
+    if (length != CommandWriterBase::kSelectDisplayLength) {
+        return false;
+    }
+
+    mCurrentReturnData = &mReturnData[read64()];
+
+    return true;
+}
+
+bool CommandReader::parseSetError(uint16_t length)
+{
+    if (length != CommandWriterBase::kSetErrorLength) {
+        return false;
+    }
+
+    auto location = read();
+    auto error = static_cast<Error>(readSigned());
+
+    mErrors.emplace_back(CommandError{location, error});
+
+    return true;
+}
+
+bool CommandReader::parseSetChangedCompositionTypes(uint16_t length)
+{
+    // (layer id, composition type) pairs
+    if (length % 3 != 0 || !mCurrentReturnData) {
+        return false;
+    }
+
+    uint32_t count = length / 3;
+    mCurrentReturnData->changedLayers.reserve(count);
+    mCurrentReturnData->compositionTypes.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto type = static_cast<IComposerClient::Composition>(readSigned());
+
+        mCurrentReturnData->changedLayers.push_back(layer);
+        mCurrentReturnData->compositionTypes.push_back(type);
+
+        count--;
+    }
+
+    return true;
+}
+
+bool CommandReader::parseSetDisplayRequests(uint16_t length)
+{
+    // display requests followed by (layer id, layer requests) pairs
+    if (length % 3 != 1 || !mCurrentReturnData) {
+        return false;
+    }
+
+    mCurrentReturnData->displayRequests = read();
+
+    uint32_t count = (length - 1) / 3;
+    mCurrentReturnData->requestedLayers.reserve(count);
+    mCurrentReturnData->requestMasks.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto layerRequestMask = read();
+
+        mCurrentReturnData->requestedLayers.push_back(layer);
+        mCurrentReturnData->requestMasks.push_back(layerRequestMask);
+
+        count--;
+    }
+
+    return true;
+}
+
+bool CommandReader::parseSetPresentFence(uint16_t length)
+{
+    if (length != CommandWriterBase::kSetPresentFenceLength ||
+            !mCurrentReturnData) {
+        return false;
+    }
+
+    if (mCurrentReturnData->presentFence >= 0) {
+        close(mCurrentReturnData->presentFence);
+    }
+    mCurrentReturnData->presentFence = readFence();
+
+    return true;
+}
+
+bool CommandReader::parseSetReleaseFences(uint16_t length)
+{
+    // (layer id, release fence index) pairs
+    if (length % 3 != 0 || !mCurrentReturnData) {
+        return false;
+    }
+
+    uint32_t count = length / 3;
+    mCurrentReturnData->releasedLayers.reserve(count);
+    mCurrentReturnData->releaseFences.reserve(count);
+    while (count > 0) {
+        auto layer = read64();
+        auto fence = readFence();
+
+        mCurrentReturnData->releasedLayers.push_back(layer);
+        mCurrentReturnData->releaseFences.push_back(fence);
+
+        count--;
+    }
+
+    return true;
+}
+
+void CommandReader::resetData()
+{
+    mErrors.clear();
+
+    for (auto& data : mReturnData) {
+        if (data.second.presentFence >= 0) {
+            close(data.second.presentFence);
+        }
+        for (auto fence : data.second.releaseFences) {
+            if (fence >= 0) {
+                close(fence);
+            }
+        }
+    }
+
+    mReturnData.clear();
+    mCurrentReturnData = nullptr;
+}
+
+std::vector<CommandReader::CommandError> CommandReader::takeErrors()
+{
+    return std::move(mErrors);
+}
+
+bool CommandReader::hasChanges(Display display,
+        uint32_t* outNumChangedCompositionTypes,
+        uint32_t* outNumLayerRequestMasks) const
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outNumChangedCompositionTypes = 0;
+        *outNumLayerRequestMasks = 0;
+        return false;
+    }
+
+    const ReturnData& data = found->second;
+
+    *outNumChangedCompositionTypes = data.compositionTypes.size();
+    *outNumLayerRequestMasks = data.requestMasks.size();
+
+    return !(data.compositionTypes.empty() && data.requestMasks.empty());
+}
+
+void CommandReader::takeChangedCompositionTypes(Display display,
+        std::vector<Layer>* outLayers,
+        std::vector<IComposerClient::Composition>* outTypes)
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        outLayers->clear();
+        outTypes->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outLayers = std::move(data.changedLayers);
+    *outTypes = std::move(data.compositionTypes);
+}
+
+void CommandReader::takeDisplayRequests(Display display,
+        uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
+        std::vector<uint32_t>* outLayerRequestMasks)
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outDisplayRequestMask = 0;
+        outLayers->clear();
+        outLayerRequestMasks->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outDisplayRequestMask = data.displayRequests;
+    *outLayers = std::move(data.requestedLayers);
+    *outLayerRequestMasks = std::move(data.requestMasks);
+}
+
+void CommandReader::takeReleaseFences(Display display,
+        std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        outLayers->clear();
+        outReleaseFences->clear();
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outLayers = std::move(data.releasedLayers);
+    *outReleaseFences = std::move(data.releaseFences);
+}
+
+void CommandReader::takePresentFence(Display display, int* outPresentFence)
+{
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *outPresentFence = -1;
+        return;
+    }
+
+    ReturnData& data = found->second;
+
+    *outPresentFence = data.presentFence;
+    data.presentFence = -1;
+}
+
+} // namespace Hwc2
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
new file mode 100644
index 0000000..18af9dd
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -0,0 +1,260 @@
+/*
+ * 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>
+
+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(bool useVrComposer);
+
+    std::vector<IComposer::Capability> getCapabilities();
+    std::string dumpDebugInfo();
+
+    void registerCallback(const sp<IComposerCallback>& callback);
+
+    uint32_t getMaxVirtualDisplayCount();
+    bool isUsingVrComposer() const { return mIsUsingVrComposer; }
+    Error createVirtualDisplay(uint32_t width, uint32_t height,
+            PixelFormat* format, Display* outDisplay);
+    Error destroyVirtualDisplay(Display display);
+
+    Error acceptDisplayChanges(Display display);
+
+    Error createLayer(Display display, Layer* outLayer);
+    Error destroyLayer(Display display, Layer layer);
+
+    Error getActiveConfig(Display display, Config* outConfig);
+    Error getChangedCompositionTypes(Display display,
+            std::vector<Layer>* outLayers,
+            std::vector<IComposerClient::Composition>* outTypes);
+    Error getColorModes(Display display, std::vector<ColorMode>* outModes);
+    Error getDisplayAttribute(Display display, Config config,
+            IComposerClient::Attribute attribute, int32_t* outValue);
+    Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+    Error getDisplayName(Display display, std::string* outName);
+
+    Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+            std::vector<Layer>* outLayers,
+            std::vector<uint32_t>* outLayerRequestMasks);
+
+    Error getDisplayType(Display display,
+            IComposerClient::DisplayType* outType);
+    Error getDozeSupport(Display display, bool* outSupport);
+    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+            float* outMaxLuminance, float* outMaxAverageLuminance,
+            float* outMinLuminance);
+
+    Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
+            std::vector<int>* outReleaseFences);
+
+    Error presentDisplay(Display display, int* outPresentFence);
+
+    Error setActiveConfig(Display display, Config config);
+
+    /*
+     * The composer caches client targets internally.  When target is nullptr,
+     * the composer uses slot to look up the client target from its cache.
+     * When target is not nullptr, the cache is updated with the new target.
+     */
+    Error setClientTarget(Display display, uint32_t slot,
+            const 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);
+    /* see setClientTarget for the purpose of slot */
+    Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
+            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;
+
+    // When true, the we attach to the vr_hwcomposer service instead of the
+    // hwcomposer. This allows us to redirect surfaces to 3d surfaces in vr.
+    const bool mIsUsingVrComposer;
+};
+
+} // namespace Hwc2
+
+} // namespace android
+
+#endif // ANDROID_SF_COMPOSER_HAL_H
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
index d801bb3..cb08f08 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/DisplaySurface.h
@@ -25,6 +25,7 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+class Fence;
 class IGraphicBufferProducer;
 class String8;
 
diff --git a/services/surfaceflinger/DisplayHardware/FloatRect.h b/services/surfaceflinger/DisplayHardware/FloatRect.h
deleted file mode 100644
index 151eaaa..0000000
--- a/services/surfaceflinger/DisplayHardware/FloatRect.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_FLOAT_RECT
-#define ANDROID_SF_FLOAT_RECT
-
-#include <ui/Rect.h>
-#include <utils/TypeHelpers.h>
-
-namespace android {
-
-class FloatRect
-{
-public:
-    float left;
-    float top;
-    float right;
-    float bottom;
-
-    inline FloatRect()
-        : left(0), top(0), right(0), bottom(0) { }
-    inline FloatRect(const Rect& other)  // NOLINT(implicit)
-        : left(other.left), top(other.top), right(other.right), bottom(other.bottom) { }
-
-    inline float getWidth() const { return right - left; }
-    inline float getHeight() const { return bottom - top; }
-};
-
-}; // namespace android
-
-#endif // ANDROID_SF_FLOAT_RECT
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index cdfe34a..ae6e0cc 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -27,15 +27,16 @@
 #include <utils/String8.h>
 #include <log/log.h>
 
-#include <ui/Rect.h>
-
 #include <EGL/egl.h>
 
 #include <hardware/hardware.h>
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 #include <gui/GraphicBufferAlloc.h>
 #include <gui/Surface.h>
+
 #include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
 
 #include "FramebufferSurface.h"
 #include "HWComposer.h"
@@ -100,16 +101,18 @@
 
 status_t FramebufferSurface::advanceFrame() {
 #ifdef USE_HWC2
+    uint32_t slot = 0;
     sp<GraphicBuffer> buf;
     sp<Fence> acquireFence(Fence::NO_FENCE);
     android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
-    status_t result = nextBuffer(buf, acquireFence, dataspace);
+    status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
     if (result != NO_ERROR) {
         ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
                 strerror(-result), result);
         return result;
     }
-    result = mHwc.setClientTarget(mDisplayType, acquireFence, buf, dataspace);
+    result = mHwc.setClientTarget(mDisplayType, slot,
+            acquireFence, buf, dataspace);
     if (result != NO_ERROR) {
         ALOGE("error posting framebuffer: %d", result);
     }
@@ -123,8 +126,9 @@
 }
 
 #ifdef USE_HWC2
-status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer,
-        sp<Fence>& outFence, android_dataspace_t& outDataspace) {
+status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
+        sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
+        android_dataspace_t& outDataspace) {
 #else
 status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
 #endif
@@ -133,7 +137,12 @@
     BufferItem item;
     status_t err = acquireBufferLocked(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+#ifdef USE_HWC2
+        mHwcBufferCache->getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
+                &outSlot, &outBuffer);
+#else
         outBuffer = mCurrentBuffer;
+#endif
         return NO_ERROR;
     } else if (err != NO_ERROR) {
         ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -169,9 +178,12 @@
     mCurrentFence = item.mFence;
 
     outFence = item.mFence;
-    outBuffer = mCurrentBuffer;
 #ifdef USE_HWC2
+    mHwcBufferCache->getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer,
+            &outSlot, &outBuffer);
     outDataspace = item.mDataSpace;
+#else
+    outBuffer = mCurrentBuffer;
 #endif
     return NO_ERROR;
 }
@@ -204,7 +216,7 @@
 void FramebufferSurface::onFrameCommitted() {
 #ifdef USE_HWC2
     if (mHasPendingRelease) {
-        sp<Fence> fence = mHwc.getRetireFence(mDisplayType);
+        sp<Fence> fence = mHwc.getPresentFence(mDisplayType);
         if (fence->isValid()) {
             status_t result = addReleaseFence(mPreviousBufferSlot,
                     mPreviousBuffer, fence);
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 439435a..5eea6b6 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -17,12 +17,14 @@
 #ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H
 #define ANDROID_SF_FRAMEBUFFER_SURFACE_H
 
+#include "DisplaySurface.h"
+
 #include <stdint.h>
 #include <sys/types.h>
 
 #include <gui/ConsumerBase.h>
 
-#include "DisplaySurface.h"
+#include <memory>
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -31,6 +33,7 @@
 class Rect;
 class String8;
 class HWComposer;
+class HWComposerBufferCache;
 
 // ---------------------------------------------------------------------------
 
@@ -68,8 +71,8 @@
     // BufferQueue and releases the previously latched buffer to the
     // BufferQueue.  The new buffer is returned in the 'buffer' argument.
 #ifdef USE_HWC2
-    status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
-            android_dataspace_t& outDataspace);
+    status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
+            sp<Fence>& outFence, android_dataspace_t& outDataspace);
 #else
     status_t nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence);
 #endif
@@ -93,6 +96,9 @@
     HWComposer& mHwc;
 
 #ifdef USE_HWC2
+    std::unique_ptr<HWComposerBufferCache> mHwcBufferCache =
+        std::make_unique<HWComposerBufferCache>();
+
     // Previous buffer to release after getting an updated retire fence
     bool mHasPendingRelease;
     int mPreviousBufferSlot;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 4fe3cfd..e49e734 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -21,10 +21,10 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "HWC2.h"
-
-#include "FloatRect.h"
+#include "ComposerHal.h"
 
 #include <ui/Fence.h>
+#include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Region.h>
 
@@ -79,11 +79,16 @@
 using android::Rect;
 using android::Region;
 using android::sp;
+using android::hardware::Return;
+using android::hardware::Void;
 
 namespace HWC2 {
 
+namespace Hwc2 = android::Hwc2;
+
 // Device methods
 
+#ifdef BYPASS_IHWC
 Device::Device(hwc2_device_t* device)
   : mHwcDevice(device),
     mCreateVirtualDisplay(nullptr),
@@ -128,6 +133,10 @@
     mSetLayerTransform(nullptr),
     mSetLayerVisibleRegion(nullptr),
     mSetLayerZOrder(nullptr),
+#else
+Device::Device(bool useVrComposer)
+  : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)),
+#endif // BYPASS_IHWC
     mCapabilities(),
     mDisplays(),
     mHotplug(),
@@ -144,9 +153,11 @@
 
 Device::~Device()
 {
+#ifdef BYPASS_IHWC
     if (mHwcDevice == nullptr) {
         return;
     }
+#endif
 
     for (auto element : mDisplays) {
         auto display = element.second.lock();
@@ -175,13 +186,16 @@
         }
     }
 
+#ifdef BYPASS_IHWC
     hwc2_close(mHwcDevice);
+#endif
 }
 
 // Required by HWC2 device
 
 std::string Device::dump() const
 {
+#ifdef BYPASS_IHWC
     uint32_t numBytes = 0;
     mDump(mHwcDevice, &numBytes, nullptr);
 
@@ -189,11 +203,18 @@
     mDump(mHwcDevice, &numBytes, buffer.data());
 
     return std::string(buffer.data(), buffer.size());
+#else
+    return mComposer->dumpDebugInfo();
+#endif
 }
 
 uint32_t Device::getMaxVirtualDisplayCount() const
 {
+#ifdef BYPASS_IHWC
     return mGetMaxVirtualDisplayCount(mHwcDevice);
+#else
+    return mComposer->getMaxVirtualDisplayCount();
+#endif
 }
 
 Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
@@ -202,9 +223,15 @@
     ALOGI("Creating virtual display");
 
     hwc2_display_t displayId = 0;
+#ifdef BYPASS_IHWC
     int32_t intFormat = static_cast<int32_t>(*format);
     int32_t intError = mCreateVirtualDisplay(mHwcDevice, width, height,
             &intFormat, &displayId);
+#else
+    auto intFormat = static_cast<Hwc2::PixelFormat>(*format);
+    auto intError = mComposer->createVirtualDisplay(width, height,
+            &intFormat, &displayId);
+#endif
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
@@ -258,6 +285,9 @@
 {
     if (connected == Connection::Connected) {
         if (!display->isConnected()) {
+#ifndef BYPASS_IHWC
+            mComposer->setClientTargetSlotCount(display->getId());
+#endif
             display->loadConfigs();
             display->setConnected(true);
         }
@@ -315,6 +345,7 @@
 {
     static_assert(sizeof(Capability) == sizeof(int32_t),
             "Capability size has changed");
+#ifdef BYPASS_IHWC
     uint32_t numCapabilities = 0;
     mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
     std::vector<Capability> capabilities(numCapabilities);
@@ -323,6 +354,12 @@
     for (auto capability : capabilities) {
         mCapabilities.emplace(capability);
     }
+#else
+    auto capabilities = mComposer->getCapabilities();
+    for (auto capability : capabilities) {
+        mCapabilities.emplace(static_cast<Capability>(capability));
+    }
+#endif
 }
 
 bool Device::hasCapability(HWC2::Capability capability) const
@@ -333,6 +370,7 @@
 
 void Device::loadFunctionPointers()
 {
+#ifdef BYPASS_IHWC
     // For all of these early returns, we log an error message inside
     // loadFunctionPointer specifying which function failed to load
 
@@ -426,13 +464,48 @@
             mSetLayerVisibleRegion)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder,
             mSetLayerZOrder)) return;
+#endif // BYPASS_IHWC
 }
 
+namespace {
+class ComposerCallback : public Hwc2::IComposerCallback {
+public:
+    ComposerCallback(Device* device) : mDevice(device) {}
+
+    Return<void> onHotplug(Hwc2::Display display,
+            Connection connected) override
+    {
+        hotplug_hook(mDevice, display, static_cast<int32_t>(connected));
+        return Void();
+    }
+
+    Return<void> onRefresh(Hwc2::Display display) override
+    {
+        refresh_hook(mDevice, display);
+        return Void();
+    }
+
+    Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
+    {
+        vsync_hook(mDevice, display, timestamp);
+        return Void();
+    }
+
+private:
+    Device* mDevice;
+};
+} // namespace anonymous
+
 void Device::registerCallbacks()
 {
+#ifdef BYPASS_IHWC
     registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);
     registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);
     registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
+#else
+    sp<ComposerCallback> callback = new ComposerCallback(this);
+    mComposer->registerCallback(callback);
+#endif
 }
 
 
@@ -441,7 +514,11 @@
 void Device::destroyVirtualDisplay(hwc2_display_t display)
 {
     ALOGI("Destroying virtual display");
+#ifdef BYPASS_IHWC
     int32_t intError = mDestroyVirtualDisplay(mHwcDevice, display);
+#else
+    auto intError = mComposer->destroyVirtualDisplay(display);
+#endif
     auto error = static_cast<Error>(intError);
     ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:"
             " %s (%d)", display, to_string(error).c_str(), intError);
@@ -498,14 +575,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 +607,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 +637,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 +650,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 +683,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 +696,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 +724,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 +743,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 +769,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 +802,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 +822,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 +845,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 +858,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 +882,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 +896,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 +926,21 @@
     return Error::None;
 }
 
-Error Display::present(sp<Fence>* outRetireFence)
+Error Display::present(sp<Fence>* outPresentFence)
 {
-    int32_t retireFenceFd = 0;
+    int32_t presentFenceFd = -1;
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
-            &retireFenceFd);
+            &presentFenceFd);
+#else
+    auto intError = mDevice.mComposer->presentDisplay(mId, &presentFenceFd);
+#endif
     auto error = static_cast<Error>(intError);
     if (error != Error::None) {
         return error;
     }
 
-    *outRetireFence = new Fence(retireFenceFd);
+    *outPresentFence = new Fence(presentFenceFd);
     return Error::None;
 }
 
@@ -793,32 +952,53 @@
                 config->getDisplayId(), mId);
         return Error::BadConfig;
     }
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetActiveConfig(mDevice.mHwcDevice, mId,
             config->getId());
+#else
+    auto intError = mDevice.mComposer->setActiveConfig(mId, config->getId());
+#endif
     return static_cast<Error>(intError);
 }
 
-Error Display::setClientTarget(buffer_handle_t target,
+Error Display::setClientTarget(uint32_t slot, buffer_handle_t target,
         const sp<Fence>& acquireFence, android_dataspace_t dataspace)
 {
     // TODO: Properly encode client target surface damage
     int32_t fenceFd = acquireFence->dup();
+#ifdef BYPASS_IHWC
+    (void) slot;
     int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, target,
             fenceFd, static_cast<int32_t>(dataspace), {0, nullptr});
+#else
+    auto intError = mDevice.mComposer->setClientTarget(mId, slot, target,
+            fenceFd, static_cast<Hwc2::Dataspace>(dataspace),
+            std::vector<Hwc2::IComposerClient::Rect>());
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Display::setColorMode(android_color_mode_t mode)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetColorMode(mDevice.mHwcDevice, mId, mode);
+#else
+    auto intError = mDevice.mComposer->setColorMode(mId,
+            static_cast<Hwc2::ColorMode>(mode));
+#endif
     return static_cast<Error>(intError);
 }
 
 Error Display::setColorTransform(const android::mat4& matrix,
         android_color_transform_t hint)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetColorTransform(mDevice.mHwcDevice, mId,
             matrix.asArray(), static_cast<int32_t>(hint));
+#else
+    auto intError = mDevice.mComposer->setColorTransform(mId,
+            matrix.asArray(), static_cast<Hwc2::ColorTransform>(hint));
+#endif
     return static_cast<Error>(intError);
 }
 
@@ -827,24 +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,17 +1185,28 @@
 
 Error Layer::setCursorPosition(int32_t x, int32_t y)
 {
+#ifdef BYPASS_IHWC
     int32_t intError = mDevice.mSetCursorPosition(mDevice.mHwcDevice,
             mDisplayId, mId, x, y);
+#else
+    auto intError = mDevice.mComposer->setCursorPosition(mDisplayId,
+            mId, x, y);
+#endif
     return static_cast<Error>(intError);
 }
 
-Error Layer::setBuffer(buffer_handle_t buffer,
+Error Layer::setBuffer(uint32_t slot, buffer_handle_t buffer,
         const sp<Fence>& acquireFence)
 {
     int32_t fenceFd = acquireFence->dup();
+#ifdef BYPASS_IHWC
+    (void) slot;
     int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId,
             mId, buffer, fenceFd);
+#else
+    auto intError = mDevice.mComposer->setLayerBuffer(mDisplayId,
+            mId, slot, buffer, fenceFd);
+#endif
     return static_cast<Error>(intError);
 }
 
@@ -988,26 +1214,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 +1259,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 +1346,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 +1390,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..4419dc1 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -24,7 +24,7 @@
 #undef HWC2_USE_CPP11
 
 #include <ui/HdrCapabilities.h>
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
 #include <utils/Log.h>
 #include <utils/StrongPointer.h>
@@ -42,6 +42,9 @@
     class GraphicBuffer;
     class Rect;
     class Region;
+    namespace Hwc2 {
+        class Composer;
+    }
 }
 
 namespace HWC2 {
@@ -54,10 +57,19 @@
 typedef std::function<void(std::shared_ptr<Display>)> RefreshCallback;
 typedef std::function<void(std::shared_ptr<Display>, nsecs_t)> VsyncCallback;
 
+// C++ Wrapper around hwc2_device_t. Load all functions pointers
+// and handle callback registration.
 class Device
 {
 public:
+#ifdef BYPASS_IHWC
     explicit Device(hwc2_device_t* device);
+#else
+    // useVrComposer is passed to the composer HAL. When true, the composer HAL
+    // will use the vr composer service, otherwise it uses the real hardware
+    // composer.
+    Device(bool useVrComposer);
+#endif
     ~Device();
 
     friend class HWC2::Display;
@@ -95,9 +107,16 @@
 
     bool hasCapability(HWC2::Capability capability) const;
 
+#ifdef BYPASS_IHWC
+    android::Hwc2::Composer* getComposer() { return nullptr; }
+#else
+    android::Hwc2::Composer* getComposer() { return mComposer.get(); }
+#endif
+
 private:
     // Initialization methods
 
+#ifdef BYPASS_IHWC
     template <typename PFN>
     [[clang::warn_unused_result]] bool loadFunctionPointer(
             FunctionDescriptor desc, PFN& outPFN) {
@@ -121,6 +140,7 @@
         auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
         mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
     }
+#endif
 
     void loadCapabilities();
     void loadFunctionPointers();
@@ -132,6 +152,7 @@
 
     // Member variables
 
+#ifdef BYPASS_IHWC
     hwc2_device_t* mHwcDevice;
 
     // Device function pointers
@@ -181,6 +202,9 @@
     HWC2_PFN_SET_LAYER_TRANSFORM mSetLayerTransform;
     HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
     HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
+#else
+    std::unique_ptr<android::Hwc2::Composer> mComposer;
+#endif // BYPASS_IHWC
 
     std::unordered_set<Capability> mCapabilities;
     std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays;
@@ -194,6 +218,7 @@
     std::vector<std::pair<std::shared_ptr<Display>, nsecs_t>> mPendingVsyncs;
 };
 
+// Convenience C++ class to access hwc2_device_t Display functions directly.
 class Display : public std::enable_shared_from_this<Display>
 {
 public:
@@ -300,11 +325,11 @@
             std::unordered_map<std::shared_ptr<Layer>,
                     android::sp<android::Fence>>* outFences) const;
     [[clang::warn_unused_result]] Error present(
-            android::sp<android::Fence>* outRetireFence);
+            android::sp<android::Fence>* outPresentFence);
     [[clang::warn_unused_result]] Error setActiveConfig(
             const std::shared_ptr<const Config>& config);
     [[clang::warn_unused_result]] Error setClientTarget(
-            buffer_handle_t target,
+            uint32_t slot, buffer_handle_t target,
             const android::sp<android::Fence>& acquireFence,
             android_dataspace_t dataspace);
     [[clang::warn_unused_result]] Error setColorMode(android_color_mode_t mode);
@@ -355,6 +380,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:
@@ -365,7 +391,8 @@
     hwc2_layer_t getId() const { return mId; }
 
     [[clang::warn_unused_result]] Error setCursorPosition(int32_t x, int32_t y);
-    [[clang::warn_unused_result]] Error setBuffer(buffer_handle_t buffer,
+    [[clang::warn_unused_result]] Error setBuffer(uint32_t slot,
+            buffer_handle_t buffer,
             const android::sp<android::Fence>& acquireFence);
     [[clang::warn_unused_result]] Error setSurfaceDamage(
             const android::Region& damage);
@@ -386,6 +413,7 @@
     [[clang::warn_unused_result]] Error setVisibleRegion(
             const android::Region& region);
     [[clang::warn_unused_result]] Error setZOrder(uint32_t z);
+    [[clang::warn_unused_result]] Error setInfo(uint32_t type, uint32_t appId);
 
 private:
     std::weak_ptr<Display> mDisplay;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index 617ea9f..13bf0b5 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -34,33 +34,6 @@
 
 using namespace std::chrono_literals;
 
-static bool operator==(const hwc_color_t& lhs, const hwc_color_t& rhs) {
-    return lhs.r == rhs.r &&
-            lhs.g == rhs.g &&
-            lhs.b == rhs.b &&
-            lhs.a == rhs.a;
-}
-
-static bool operator==(const hwc_rect_t& lhs, const hwc_rect_t& rhs) {
-    return lhs.left == rhs.left &&
-            lhs.top == rhs.top &&
-            lhs.right == rhs.right &&
-            lhs.bottom == rhs.bottom;
-}
-
-static bool operator==(const hwc_frect_t& lhs, const hwc_frect_t& rhs) {
-    return lhs.left == rhs.left &&
-            lhs.top == rhs.top &&
-            lhs.right == rhs.right &&
-            lhs.bottom == rhs.bottom;
-}
-
-template <typename T>
-static inline bool operator!=(const T& lhs, const T& rhs)
-{
-    return !(lhs == rhs);
-}
-
 static uint8_t getMinorVersion(struct hwc_composer_device_1* device)
 {
     auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
@@ -80,21 +53,9 @@
 
 namespace android {
 
-void HWC2On1Adapter::DisplayContentsDeleter::operator()(
-        hwc_display_contents_1_t* contents)
-{
-    if (contents != nullptr) {
-        for (size_t l = 0; l < contents->numHwLayers; ++l) {
-            auto& layer = contents->hwLayers[l];
-            std::free(const_cast<hwc_rect_t*>(layer.visibleRegionScreen.rects));
-        }
-    }
-    std::free(contents);
-}
-
 class HWC2On1Adapter::Callbacks : public hwc_procs_t {
     public:
-        Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
+        explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
             invalidate = &invalidateHook;
             vsync = &vsyncHook;
             hotplug = &hotplugHook;
@@ -133,6 +94,7 @@
     mHwc1Device(hwc1Device),
     mHwc1MinorVersion(getMinorVersion(hwc1Device)),
     mHwc1SupportsVirtualDisplays(false),
+    mHwc1SupportsBackgroundColor(false),
     mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
     mCapabilities(),
     mLayers(),
@@ -159,8 +121,7 @@
 }
 
 void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount,
-        int32_t* outCapabilities)
-{
+        int32_t* outCapabilities) {
     if (outCapabilities == nullptr) {
         *outCount = mCapabilities.size();
         return;
@@ -177,8 +138,7 @@
 }
 
 hwc2_function_pointer_t HWC2On1Adapter::doGetFunction(
-        FunctionDescriptor descriptor)
-{
+        FunctionDescriptor descriptor) {
     switch (descriptor) {
         // Device functions
         case FunctionDescriptor::CreateVirtualDisplay:
@@ -348,8 +308,7 @@
 // Device functions
 
 Error HWC2On1Adapter::createVirtualDisplay(uint32_t width,
-        uint32_t height, hwc2_display_t* outDisplay)
-{
+        uint32_t height, hwc2_display_t* outDisplay) {
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
 
     if (mHwc1VirtualDisplay) {
@@ -379,8 +338,7 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId)
-{
+Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId) {
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
 
     if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) {
@@ -394,8 +352,7 @@
     return Error::None;
 }
 
-void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer)
-{
+void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer) {
     if (outBuffer != nullptr) {
         auto copiedBytes = mDumpString.copy(outBuffer, *outSize);
         *outSize = static_cast<uint32_t>(copiedBytes);
@@ -448,8 +405,7 @@
     *outSize = static_cast<uint32_t>(mDumpString.size());
 }
 
-uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount()
-{
+uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount() {
     return mHwc1SupportsVirtualDisplays ? 1 : 0;
 }
 
@@ -463,8 +419,7 @@
 }
 
 Error HWC2On1Adapter::registerCallback(Callback descriptor,
-        hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer)
-{
+        hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) {
     if (!isValid(descriptor)) {
         return Error::BadParameter;
     }
@@ -551,16 +506,14 @@
 HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type)
   : mId(sNextId++),
     mDevice(device),
-    mDirtyCount(0),
     mStateMutex(),
-    mZIsDirty(false),
     mHwc1RequestedContents(nullptr),
-    mHwc1ReceivedContents(nullptr),
     mRetireFence(),
     mChanges(),
     mHwc1Id(-1),
     mConfigs(),
     mActiveConfig(nullptr),
+    mActiveColorMode(static_cast<android_color_mode_t>(-1)),
     mName(),
     mType(type),
     mPowerMode(PowerMode::Off),
@@ -569,10 +522,13 @@
     mOutputBuffer(),
     mHasColorTransform(false),
     mLayers(),
-    mHwc1LayerMap() {}
+    mHwc1LayerMap(),
+    mNumAvailableRects(0),
+    mNextAvailableRect(nullptr),
+    mGeometryChanged(false)
+    {}
 
-Error HWC2On1Adapter::Display::acceptChanges()
-{
+Error HWC2On1Adapter::Display::acceptChanges() {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (!mChanges) {
@@ -591,24 +547,21 @@
 
     mChanges->clearTypeChanges();
 
-    mHwc1RequestedContents = std::move(mHwc1ReceivedContents);
-
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId)
-{
+Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     auto layer = *mLayers.emplace(std::make_shared<Layer>(*this));
     mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer));
     *outLayerId = layer->getId();
     ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId);
+    markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId)
-{
+Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     const auto mapLayer = mDevice.mLayers.find(layerId);
@@ -627,11 +580,11 @@
         }
     }
     ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId);
+    markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig)
-{
+Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (!mActiveConfig) {
@@ -646,8 +599,7 @@
 }
 
 Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId,
-        Attribute attribute, int32_t* outValue)
-{
+        Attribute attribute, int32_t* outValue) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
@@ -662,8 +614,7 @@
 }
 
 Error HWC2On1Adapter::Display::getChangedCompositionTypes(
-        uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes)
-{
+        uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (!mChanges) {
@@ -696,8 +647,7 @@
 }
 
 Error HWC2On1Adapter::Display::getColorModes(uint32_t* outNumModes,
-        int32_t* outModes)
-{
+        int32_t* outModes) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (!outModes) {
@@ -712,8 +662,7 @@
 }
 
 Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs,
-        hwc2_config_t* outConfigs)
-{
+        hwc2_config_t* outConfigs) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (!outConfigs) {
@@ -732,8 +681,7 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport)
-{
+Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) {
@@ -746,15 +694,13 @@
 
 Error HWC2On1Adapter::Display::getHdrCapabilities(uint32_t* outNumTypes,
         int32_t* /*outTypes*/, float* /*outMaxLuminance*/,
-        float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/)
-{
+        float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
     // This isn't supported on HWC1, so per the HWC2 header, return numTypes = 0
     *outNumTypes = 0;
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName)
-{
+Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (!outName) {
@@ -767,8 +713,7 @@
 }
 
 Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements,
-        hwc2_layer_t* outLayers, int32_t* outFences)
-{
+        hwc2_layer_t* outLayers, int32_t* outFences) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     uint32_t numWritten = 0;
@@ -779,7 +724,7 @@
         }
 
         auto releaseFence = layer->getReleaseFence();
-        if (releaseFence != Fence::NO_FENCE) {
+        if (releaseFence != MiniFence::NO_FENCE) {
             if (outputsNonNull) {
                 outLayers[numWritten] = layer->getId();
                 outFences[numWritten] = releaseFence->dup();
@@ -794,8 +739,7 @@
 
 Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests,
         uint32_t* outNumElements, hwc2_layer_t* outLayers,
-        int32_t* outLayerRequests)
-{
+        int32_t* outLayerRequests) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (!mChanges) {
@@ -807,7 +751,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) {
@@ -821,16 +768,14 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::getType(int32_t* outType)
-{
+Error HWC2On1Adapter::Display::getType(int32_t* outType) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     *outType = static_cast<int32_t>(mType);
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::present(int32_t* outRetireFence)
-{
+Error HWC2On1Adapter::Display::present(int32_t* outRetireFence) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (mChanges) {
@@ -849,8 +794,7 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId)
-{
+Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     auto config = getConfig(configId);
@@ -882,8 +826,7 @@
 }
 
 Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target,
-        int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/)
-{
+        int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence);
@@ -893,8 +836,7 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode)
-{
+Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode) {
     std::unique_lock<std::recursive_mutex> lock (mStateMutex);
 
     ALOGV("[%" PRIu64 "] setColorMode(%d)", mId, mode);
@@ -925,8 +867,7 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint)
-{
+Error HWC2On1Adapter::Display::setColorTransform(android_color_transform_t hint) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     ALOGV("%" PRIu64 "] setColorTransform(%d)", mId,
@@ -936,8 +877,7 @@
 }
 
 Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer,
-        int32_t releaseFence)
-{
+        int32_t releaseFence) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence);
@@ -946,30 +886,25 @@
     return Error::None;
 }
 
-static bool isValid(PowerMode mode)
-{
+static bool isValid(PowerMode mode) {
     switch (mode) {
         case PowerMode::Off: // Fall-through
         case PowerMode::DozeSuspend: // Fall-through
         case PowerMode::Doze: // Fall-through
         case PowerMode::On: return true;
-        default: return false;
     }
 }
 
-static int getHwc1PowerMode(PowerMode mode)
-{
+static int getHwc1PowerMode(PowerMode mode) {
     switch (mode) {
         case PowerMode::Off: return HWC_POWER_MODE_OFF;
         case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND;
         case PowerMode::Doze: return HWC_POWER_MODE_DOZE;
         case PowerMode::On: return HWC_POWER_MODE_NORMAL;
-        default: return HWC_POWER_MODE_OFF;
     }
 }
 
-Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode)
-{
+Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode) {
     if (!isValid(mode)) {
         return Error::BadParameter;
     }
@@ -999,12 +934,11 @@
     switch (enable) {
         case Vsync::Enable: // Fall-through
         case Vsync::Disable: return true;
-        default: return false;
+        case Vsync::Invalid: return false;
     }
 }
 
-Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable)
-{
+Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable) {
     if (!isValid(enable)) {
         return Error::BadParameter;
     }
@@ -1024,16 +958,15 @@
 }
 
 Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes,
-        uint32_t* outNumRequests)
-{
+        uint32_t* outNumRequests) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
-    ALOGV("[%" PRIu64 "] Entering validate", mId);
-
     if (!mChanges) {
         if (!mDevice.prepareAllDisplays()) {
             return Error::BadDisplay;
         }
+    } else {
+        ALOGE("Validate was called more than once!");
     }
 
     *outNumTypes = mChanges->getNumTypes();
@@ -1047,10 +980,7 @@
     return *outNumTypes > 0 ? Error::HasChanges : Error::None;
 }
 
-// Display helpers
-
-Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z)
-{
+Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     const auto mapLayer = mDevice.mLayers.find(layerId);
@@ -1082,7 +1012,7 @@
 
     layer->setZ(z);
     mLayers.emplace(std::move(layer));
-    mZIsDirty = true;
+    markGeometryChanged();
 
     return Error::None;
 }
@@ -1151,8 +1081,7 @@
 static_assert(attributesMatch<HWC_DISPLAY_COLOR_TRANSFORM>(),
         "Tables out of sync");
 
-void HWC2On1Adapter::Display::populateConfigs()
-{
+void HWC2On1Adapter::Display::populateConfigs() {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     ALOGV("[%" PRIu64 "] populateConfigs", mId);
@@ -1230,8 +1159,7 @@
     populateColorModes();
 }
 
-void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height)
-{
+void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     mConfigs.emplace_back(std::make_shared<Config>(*this));
@@ -1244,8 +1172,7 @@
     mActiveConfig = config;
 }
 
-bool HWC2On1Adapter::Display::prepare()
-{
+bool HWC2On1Adapter::Display::prepare() {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     // Only prepare display contents for displays HWC1 knows about
@@ -1260,86 +1187,45 @@
         return false;
     }
 
-    ALOGV("[%" PRIu64 "] Entering prepare", mId);
-
-    auto currentCount = mHwc1RequestedContents ?
-            mHwc1RequestedContents->numHwLayers : 0;
-    auto requiredCount = mLayers.size() + 1;
-    ALOGV("[%" PRIu64 "]   Requires %zd layers, %zd allocated in %p", mId,
-            requiredCount, currentCount, mHwc1RequestedContents.get());
-
-    bool layerCountChanged = (currentCount != requiredCount);
-    if (layerCountChanged) {
-        reallocateHwc1Contents();
-    }
-
-    bool applyAllState = false;
-    if (layerCountChanged || mZIsDirty) {
-        assignHwc1LayerIds();
-        mZIsDirty = false;
-        applyAllState = true;
-    }
+    allocateRequestedContents();
+    assignHwc1LayerIds();
 
     mHwc1RequestedContents->retireFenceFd = -1;
     mHwc1RequestedContents->flags = 0;
-    if (isDirty() || applyAllState) {
+    if (mGeometryChanged) {
         mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED;
     }
-
-    for (auto& layer : mLayers) {
-        auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
-        hwc1Layer.releaseFenceFd = -1;
-        layer->applyState(hwc1Layer, applyAllState);
-    }
-
     mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer();
     mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence();
 
+    // +1 is for framebuffer target layer.
+    mHwc1RequestedContents->numHwLayers = mLayers.size() + 1;
+    for (auto& layer : mLayers) {
+        auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
+        hwc1Layer.releaseFenceFd = -1;
+        hwc1Layer.acquireFenceFd = -1;
+        ALOGV("Applying states for layer %" PRIu64 " ", layer->getId());
+        layer->applyState(hwc1Layer);
+    }
+
     prepareFramebufferTarget();
 
+    resetGeometryMarker();
+
     return true;
 }
 
-static void cloneHWCRegion(hwc_region_t& region)
-{
-    auto size = sizeof(hwc_rect_t) * region.numRects;
-    auto newRects = static_cast<hwc_rect_t*>(std::malloc(size));
-    std::copy_n(region.rects, region.numRects, newRects);
-    region.rects = newRects;
-}
-
-HWC2On1Adapter::Display::HWC1Contents
-        HWC2On1Adapter::Display::cloneRequestedContents() const
-{
+void HWC2On1Adapter::Display::generateChanges() {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
-    size_t size = sizeof(hwc_display_contents_1_t) +
-            sizeof(hwc_layer_1_t) * (mHwc1RequestedContents->numHwLayers);
-    auto contents = static_cast<hwc_display_contents_1_t*>(std::malloc(size));
-    std::memcpy(contents, mHwc1RequestedContents.get(), size);
-    for (size_t layerId = 0; layerId < contents->numHwLayers; ++layerId) {
-        auto& layer = contents->hwLayers[layerId];
-        // Deep copy the regions to avoid double-frees
-        cloneHWCRegion(layer.visibleRegionScreen);
-        cloneHWCRegion(layer.surfaceDamage);
-    }
-    return HWC1Contents(contents);
-}
-
-void HWC2On1Adapter::Display::setReceivedContents(HWC1Contents contents)
-{
-    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
-
-    mHwc1ReceivedContents = std::move(contents);
-
     mChanges.reset(new Changes);
 
-    size_t numLayers = mHwc1ReceivedContents->numHwLayers;
+    size_t numLayers = mHwc1RequestedContents->numHwLayers;
     for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
-        const auto& receivedLayer = mHwc1ReceivedContents->hwLayers[hwc1Id];
+        const auto& receivedLayer = mHwc1RequestedContents->hwLayers[hwc1Id];
         if (mHwc1LayerMap.count(hwc1Id) == 0) {
             ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET,
-                    "setReceivedContents: HWC1 layer %zd doesn't have a"
+                    "generateChanges: HWC1 layer %zd doesn't have a"
                     " matching HWC2 layer, and isn't the framebuffer target",
                     hwc1Id);
             continue;
@@ -1351,14 +1237,12 @@
     }
 }
 
-bool HWC2On1Adapter::Display::hasChanges() const
-{
+bool HWC2On1Adapter::Display::hasChanges() const {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
     return mChanges != nullptr;
 }
 
-Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents)
-{
+Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     if (!mChanges || (mChanges->getNumTypes() > 0)) {
@@ -1394,15 +1278,13 @@
     return Error::None;
 }
 
-void HWC2On1Adapter::Display::addRetireFence(int fenceFd)
-{
+void HWC2On1Adapter::Display::addRetireFence(int fenceFd) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
     mRetireFence.add(fenceFd);
 }
 
 void HWC2On1Adapter::Display::addReleaseFences(
-        const hwc_display_contents_1_t& hwcContents)
-{
+        const hwc_display_contents_1_t& hwcContents) {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     size_t numLayers = hwcContents.numHwLayers;
@@ -1429,14 +1311,12 @@
     }
 }
 
-bool HWC2On1Adapter::Display::hasColorTransform() const
-{
+bool HWC2On1Adapter::Display::hasColorTransform() const {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
     return mHasColorTransform;
 }
 
-static std::string hwc1CompositionString(int32_t type)
-{
+static std::string hwc1CompositionString(int32_t type) {
     switch (type) {
         case HWC_FRAMEBUFFER: return "Framebuffer";
         case HWC_OVERLAY: return "Overlay";
@@ -1449,8 +1329,7 @@
     }
 }
 
-static std::string hwc1TransformString(int32_t transform)
-{
+static std::string hwc1TransformString(int32_t transform) {
     switch (transform) {
         case 0: return "None";
         case HWC_TRANSFORM_FLIP_H: return "FlipH";
@@ -1465,8 +1344,7 @@
     }
 }
 
-static std::string hwc1BlendModeString(int32_t mode)
-{
+static std::string hwc1BlendModeString(int32_t mode) {
     switch (mode) {
         case HWC_BLENDING_NONE: return "None";
         case HWC_BLENDING_PREMULT: return "Premultiplied";
@@ -1476,16 +1354,14 @@
     }
 }
 
-static std::string rectString(hwc_rect_t rect)
-{
+static std::string rectString(hwc_rect_t rect) {
     std::stringstream output;
     output << "[" << rect.left << ", " << rect.top << ", ";
     output << rect.right << ", " << rect.bottom << "]";
     return output.str();
 }
 
-static std::string approximateFloatString(float f)
-{
+static std::string approximateFloatString(float f) {
     if (static_cast<int32_t>(f) == f) {
         return std::to_string(static_cast<int32_t>(f));
     }
@@ -1498,8 +1374,7 @@
     return std::string(buffer, bytesWritten);
 }
 
-static std::string frectString(hwc_frect_t frect)
-{
+static std::string frectString(hwc_frect_t frect) {
     std::stringstream output;
     output << "[" << approximateFloatString(frect.left) << ", ";
     output << approximateFloatString(frect.top) << ", ";
@@ -1508,8 +1383,7 @@
     return output.str();
 }
 
-static std::string colorString(hwc_color_t color)
-{
+static std::string colorString(hwc_color_t color) {
     std::stringstream output;
     output << "RGBA [";
     output << static_cast<int32_t>(color.r) << ", ";
@@ -1519,8 +1393,7 @@
     return output.str();
 }
 
-static std::string alphaString(float f)
-{
+static std::string alphaString(float f) {
     const size_t BUFFER_SIZE = 8;
     char buffer[BUFFER_SIZE] = {};
     auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f);
@@ -1528,8 +1401,7 @@
 }
 
 static std::string to_string(const hwc_layer_1_t& hwcLayer,
-        int32_t hwc1MinorVersion)
-{
+        int32_t hwc1MinorVersion) {
     const char* fill = "          ";
 
     std::stringstream output;
@@ -1589,8 +1461,7 @@
 }
 
 static std::string to_string(const hwc_display_contents_1_t& hwcContents,
-        int32_t hwc1MinorVersion)
-{
+        int32_t hwc1MinorVersion) {
     const char* fill = "      ";
 
     std::stringstream output;
@@ -1612,8 +1483,7 @@
     return output.str();
 }
 
-std::string HWC2On1Adapter::Display::dump() const
-{
+std::string HWC2On1Adapter::Display::dump() const {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
 
     std::stringstream output;
@@ -1653,10 +1523,7 @@
         output << "    Output buffer: " << mOutputBuffer.getBuffer() << '\n';
     }
 
-    if (mHwc1ReceivedContents) {
-        output << "    Last received HWC1 state\n";
-        output << to_string(*mHwc1ReceivedContents, mDevice.mHwc1MinorVersion);
-    } else if (mHwc1RequestedContents) {
+    if (mHwc1RequestedContents) {
         output << "    Last requested HWC1 state\n";
         output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion);
     }
@@ -1664,28 +1531,46 @@
     return output.str();
 }
 
+hwc_rect_t* HWC2On1Adapter::Display::GetRects(size_t numRects) {
+    if (numRects == 0) {
+        return nullptr;
+    }
+
+    if (numRects > mNumAvailableRects) {
+        // This should NEVER happen since we calculated how many rects the
+        // display would need.
+        ALOGE("Rect allocation failure! SF is likely to crash soon!");
+        return nullptr;
+
+    }
+    hwc_rect_t* rects = mNextAvailableRect;
+    mNextAvailableRect += numRects;
+    mNumAvailableRects -= numRects;
+    return rects;
+}
+
+hwc_display_contents_1* HWC2On1Adapter::Display::getDisplayContents() {
+    return mHwc1RequestedContents.get();
+}
+
 void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute,
-        int32_t value)
-{
+        int32_t value) {
     mAttributes[attribute] = value;
 }
 
-int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const
-{
+int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const {
     if (mAttributes.count(attribute) == 0) {
         return -1;
     }
     return mAttributes.at(attribute);
 }
 
-void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id)
-{
+void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id) {
     android_color_mode_t colorMode = static_cast<android_color_mode_t>(getAttribute(ColorMode));
     mHwc1Ids.emplace(colorMode, id);
 }
 
-bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const
-{
+bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const {
     for (const auto& idPair : mHwc1Ids) {
         if (id == idPair.second) {
             return true;
@@ -1695,8 +1580,7 @@
 }
 
 Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id(
-        uint32_t id, android_color_mode_t* outMode) const
-{
+        uint32_t id, android_color_mode_t* outMode) const {
     for (const auto& idPair : mHwc1Ids) {
         if (id == idPair.second) {
             *outMode = idPair.first;
@@ -1708,8 +1592,7 @@
 }
 
 Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode,
-        uint32_t* outId) const
-{
+        uint32_t* outId) const {
     for (const auto& idPair : mHwc1Ids) {
         if (mode == idPair.first) {
             *outId = idPair.second;
@@ -1720,8 +1603,7 @@
     return Error::BadParameter;
 }
 
-bool HWC2On1Adapter::Display::Config::merge(const Config& other)
-{
+bool HWC2On1Adapter::Display::Config::merge(const Config& other) {
     auto attributes = {HWC2::Attribute::Width, HWC2::Attribute::Height,
             HWC2::Attribute::VsyncPeriod, HWC2::Attribute::DpiX,
             HWC2::Attribute::DpiY};
@@ -1743,8 +1625,7 @@
     return true;
 }
 
-std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const
-{
+std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const {
     std::set<android_color_mode_t> colorModes;
     for (const auto& idPair : mHwc1Ids) {
         colorModes.emplace(idPair.first);
@@ -1752,8 +1633,7 @@
     return colorModes;
 }
 
-std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const
-{
+std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const {
     std::string output;
 
     const size_t BUFFER_SIZE = 100;
@@ -1809,16 +1689,14 @@
 }
 
 std::shared_ptr<const HWC2On1Adapter::Display::Config>
-        HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const
-{
+        HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const {
     if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
         return nullptr;
     }
     return mConfigs[configId];
 }
 
-void HWC2On1Adapter::Display::populateColorModes()
-{
+void HWC2On1Adapter::Display::populateColorModes() {
     mColorModes = mConfigs[0]->getColorModes();
     for (const auto& config : mConfigs) {
         std::set<android_color_mode_t> intersection;
@@ -1830,8 +1708,7 @@
     }
 }
 
-void HWC2On1Adapter::Display::initializeActiveConfig()
-{
+void HWC2On1Adapter::Display::initializeActiveConfig() {
     if (mDevice.mHwc1Device->getActiveConfig == nullptr) {
         ALOGV("getActiveConfig is null, choosing config 0");
         mActiveConfig = mConfigs[0];
@@ -1841,47 +1718,75 @@
 
     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()
-{
-    // Allocate an additional layer for the framebuffer target
+void HWC2On1Adapter::Display::allocateRequestedContents() {
+    // What needs to be allocated:
+    // 1 hwc_display_contents_1_t
+    // 1 hwc_layer_1_t for each layer
+    // 1 hwc_rect_t for each layer's surfaceDamage
+    // 1 hwc_rect_t for each layer's visibleRegion
+    // 1 hwc_layer_1_t for the framebuffer
+    // 1 hwc_rect_t for the framebuffer's visibleRegion
+
+    // Count # of surfaceDamage
+    size_t numSurfaceDamages = 0;
+    for (const auto& layer : mLayers) {
+        numSurfaceDamages += layer->getNumSurfaceDamages();
+    }
+
+    // Count # of visibleRegions (start at 1 for mandatory framebuffer target
+    // region)
+    size_t numVisibleRegion = 1;
+    for (const auto& layer : mLayers) {
+        numVisibleRegion += layer->getNumVisibleRegions();
+    }
+
+    size_t numRects = numVisibleRegion + numSurfaceDamages;
     auto numLayers = mLayers.size() + 1;
     size_t size = sizeof(hwc_display_contents_1_t) +
-            sizeof(hwc_layer_1_t) * numLayers;
-    ALOGV("[%" PRIu64 "] reallocateHwc1Contents creating %zd layer%s", mId,
-            numLayers, numLayers != 1 ? "s" : "");
-    auto contents =
-            static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1));
-    contents->numHwLayers = numLayers;
+            sizeof(hwc_layer_1_t) * numLayers +
+            sizeof(hwc_rect_t) * numRects;
+    auto contents = static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1));
     mHwc1RequestedContents.reset(contents);
+    mNextAvailableRect = reinterpret_cast<hwc_rect_t*>(&contents->hwLayers[numLayers]);
+    mNumAvailableRects = numRects;
 }
 
-void HWC2On1Adapter::Display::assignHwc1LayerIds()
-{
+void HWC2On1Adapter::Display::assignHwc1LayerIds() {
     mHwc1LayerMap.clear();
     size_t nextHwc1Id = 0;
     for (auto& layer : mLayers) {
@@ -1891,8 +1796,7 @@
 }
 
 void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer,
-        const Layer& layer)
-{
+        const Layer& layer) {
     auto layerId = layer.getId();
     switch (hwc1Layer.compositionType) {
         case HWC_FRAMEBUFFER:
@@ -1927,16 +1831,14 @@
 }
 
 void HWC2On1Adapter::Display::updateLayerRequests(
-        const hwc_layer_1_t& hwc1Layer, const Layer& layer)
-{
+        const hwc_layer_1_t& hwc1Layer, const Layer& layer) {
     if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) {
         mChanges->addLayerRequest(layer.getId(),
                 LayerRequest::ClearClientTarget);
     }
 }
 
-void HWC2On1Adapter::Display::prepareFramebufferTarget()
-{
+void HWC2On1Adapter::Display::prepareFramebufferTarget() {
     // We check that mActiveConfig is valid in Display::prepare
     int32_t width = mActiveConfig->getAttribute(Attribute::Width);
     int32_t height = mActiveConfig->getAttribute(Attribute::Height);
@@ -1956,8 +1858,9 @@
     }
     hwc1Target.displayFrame = {0, 0, width, height};
     hwc1Target.planeAlpha = 255;
+
     hwc1Target.visibleRegionScreen.numRects = 1;
-    auto rects = static_cast<hwc_rect_t*>(std::malloc(sizeof(hwc_rect_t)));
+    hwc_rect_t* rects = GetRects(1);
     rects[0].left = 0;
     rects[0].top = 0;
     rects[0].right = width;
@@ -1975,42 +1878,37 @@
 HWC2On1Adapter::Layer::Layer(Display& display)
   : mId(sNextId++),
     mDisplay(display),
-    mDirtyCount(0),
     mBuffer(),
     mSurfaceDamage(),
-    mBlendMode(*this, BlendMode::None),
-    mColor(*this, {0, 0, 0, 0}),
-    mCompositionType(*this, Composition::Invalid),
-    mDisplayFrame(*this, {0, 0, -1, -1}),
-    mPlaneAlpha(*this, 0.0f),
-    mSidebandStream(*this, nullptr),
-    mSourceCrop(*this, {0.0f, 0.0f, -1.0f, -1.0f}),
-    mTransform(*this, Transform::None),
-    mVisibleRegion(*this, std::vector<hwc_rect_t>()),
+    mBlendMode(BlendMode::None),
+    mColor({0, 0, 0, 0}),
+    mCompositionType(Composition::Invalid),
+    mDisplayFrame({0, 0, -1, -1}),
+    mPlaneAlpha(0.0f),
+    mSidebandStream(nullptr),
+    mSourceCrop({0.0f, 0.0f, -1.0f, -1.0f}),
+    mTransform(Transform::None),
+    mVisibleRegion(),
     mZ(0),
     mReleaseFence(),
     mHwc1Id(0),
-    mHasUnsupportedDataspace(false),
     mHasUnsupportedPlaneAlpha(false) {}
 
 bool HWC2On1Adapter::SortLayersByZ::operator()(
-        const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs)
-{
+        const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs) {
     return lhs->getZ() < rhs->getZ();
 }
 
 Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer,
-        int32_t acquireFence)
-{
+        int32_t acquireFence) {
     ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId);
     mBuffer.setBuffer(buffer);
     mBuffer.setFence(acquireFence);
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y)
-{
-    if (mCompositionType.getValue() != Composition::Cursor) {
+Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y) {
+    if (mCompositionType != Composition::Cursor) {
         return Error::BadLayer;
     }
 
@@ -2024,8 +1922,11 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage)
-{
+Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage) {
+    // HWC1 supports surface damage starting only with version 1.5.
+    if (mDisplay.getDevice().mHwc1MinorVersion < 5) {
+        return Error::None;
+    }
     mSurfaceDamage.resize(damage.numRects);
     std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin());
     return Error::None;
@@ -2033,105 +1934,91 @@
 
 // Layer state functions
 
-Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode)
-{
-    mBlendMode.setPending(mode);
+Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode) {
+    mBlendMode = mode;
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setColor(hwc_color_t color)
-{
-    mColor.setPending(color);
+Error HWC2On1Adapter::Layer::setColor(hwc_color_t color) {
+    mColor = color;
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setCompositionType(Composition type)
-{
-    mCompositionType.setPending(type);
+Error HWC2On1Adapter::Layer::setCompositionType(Composition type) {
+    mCompositionType = type;
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t dataspace)
-{
-    mHasUnsupportedDataspace = (dataspace != HAL_DATASPACE_UNKNOWN);
+Error HWC2On1Adapter::Layer::setDataspace(android_dataspace_t) {
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame)
-{
-    mDisplayFrame.setPending(frame);
+Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame) {
+    mDisplayFrame = frame;
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha)
-{
-    mPlaneAlpha.setPending(alpha);
+Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha) {
+    mPlaneAlpha = alpha;
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream)
-{
-    mSidebandStream.setPending(stream);
+Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream) {
+    mSidebandStream = stream;
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop)
-{
-    mSourceCrop.setPending(crop);
+Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop) {
+    mSourceCrop = crop;
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setTransform(Transform transform)
-{
-    mTransform.setPending(transform);
+Error HWC2On1Adapter::Layer::setTransform(Transform transform) {
+    mTransform = transform;
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t rawVisible)
-{
-    std::vector<hwc_rect_t> visible(rawVisible.rects,
-            rawVisible.rects + rawVisible.numRects);
-    mVisibleRegion.setPending(std::move(visible));
+Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t visible) {
+    mVisibleRegion.resize(visible.numRects);
+    std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin());
+    mDisplay.markGeometryChanged();
     return Error::None;
 }
 
-Error HWC2On1Adapter::Layer::setZ(uint32_t z)
-{
+Error HWC2On1Adapter::Layer::setZ(uint32_t z) {
     mZ = z;
     return Error::None;
 }
 
-void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd)
-{
+void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd) {
     ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId);
     mReleaseFence.add(fenceFd);
 }
 
-const sp<Fence>& HWC2On1Adapter::Layer::getReleaseFence() const
-{
+const sp<MiniFence>& HWC2On1Adapter::Layer::getReleaseFence() const {
     return mReleaseFence.get();
 }
 
-void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
-    applyCommonState(hwc1Layer, applyAllState);
-    auto compositionType = mCompositionType.getPendingValue();
-    if (compositionType == Composition::SolidColor) {
-        applySolidColorState(hwc1Layer, applyAllState);
-    } else if (compositionType == Composition::Sideband) {
-        applySidebandState(hwc1Layer, applyAllState);
-    } else {
-        applyBufferState(hwc1Layer);
+void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer) {
+    applyCommonState(hwc1Layer);
+    applyCompositionType(hwc1Layer);
+    switch (mCompositionType) {
+        case Composition::SolidColor : applySolidColorState(hwc1Layer); break;
+        case Composition::Sideband : applySidebandState(hwc1Layer); break;
+        default: applyBufferState(hwc1Layer); break;
     }
-    applyCompositionType(hwc1Layer, applyAllState);
 }
 
-// Layer dump helpers
-
 static std::string regionStrings(const std::vector<hwc_rect_t>& visibleRegion,
-        const std::vector<hwc_rect_t>& surfaceDamage)
-{
+        const std::vector<hwc_rect_t>& surfaceDamage) {
     std::string regions;
     regions += "        Visible Region";
     regions.resize(40, ' ');
@@ -2159,40 +2046,38 @@
     return regions;
 }
 
-std::string HWC2On1Adapter::Layer::dump() const
-{
+std::string HWC2On1Adapter::Layer::dump() const {
     std::stringstream output;
     const char* fill = "      ";
 
-    output << fill << to_string(mCompositionType.getPendingValue());
+    output << fill << to_string(mCompositionType);
     output << " Layer  HWC2/1: " << mId << "/" << mHwc1Id << "  ";
     output << "Z: " << mZ;
-    if (mCompositionType.getValue() == HWC2::Composition::SolidColor) {
-        output << "  " << colorString(mColor.getValue());
-    } else if (mCompositionType.getValue() == HWC2::Composition::Sideband) {
-        output << "  Handle: " << mSidebandStream.getValue() << '\n';
+    if (mCompositionType == HWC2::Composition::SolidColor) {
+        output << "  " << colorString(mColor);
+    } else if (mCompositionType == HWC2::Composition::Sideband) {
+        output << "  Handle: " << mSidebandStream << '\n';
     } else {
         output << "  Buffer: " << mBuffer.getBuffer() << "/" <<
                 mBuffer.getFence() << '\n';
         output << fill << "  Display frame [LTRB]: " <<
-                rectString(mDisplayFrame.getValue()) << '\n';
+                rectString(mDisplayFrame) << '\n';
         output << fill << "  Source crop: " <<
-                frectString(mSourceCrop.getValue()) << '\n';
-        output << fill << "  Transform: " << to_string(mTransform.getValue());
-        output << "  Blend mode: " << to_string(mBlendMode.getValue());
-        if (mPlaneAlpha.getValue() != 1.0f) {
+                frectString(mSourceCrop) << '\n';
+        output << fill << "  Transform: " << to_string(mTransform);
+        output << "  Blend mode: " << to_string(mBlendMode);
+        if (mPlaneAlpha != 1.0f) {
             output << "  Alpha: " <<
-                alphaString(mPlaneAlpha.getValue()) << '\n';
+                alphaString(mPlaneAlpha) << '\n';
         } else {
             output << '\n';
         }
-        output << regionStrings(mVisibleRegion.getValue(), mSurfaceDamage);
+        output << regionStrings(mVisibleRegion, mSurfaceDamage);
     }
     return output.str();
 }
 
-static int getHwc1Blending(HWC2::BlendMode blendMode)
-{
+static int getHwc1Blending(HWC2::BlendMode blendMode) {
     switch (blendMode) {
         case BlendMode::Coverage: return HWC_BLENDING_COVERAGE;
         case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT;
@@ -2200,153 +2085,124 @@
     }
 }
 
-void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
+void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer) {
     auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion();
-    if (applyAllState || mBlendMode.isDirty()) {
-        hwc1Layer.blending = getHwc1Blending(mBlendMode.getPendingValue());
-        mBlendMode.latch();
-    }
-    if (applyAllState || mDisplayFrame.isDirty()) {
-        hwc1Layer.displayFrame = mDisplayFrame.getPendingValue();
-        mDisplayFrame.latch();
-    }
-    if (applyAllState || mPlaneAlpha.isDirty()) {
-        auto pendingAlpha = mPlaneAlpha.getPendingValue();
-        if (minorVersion < 2) {
-            mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f;
-        } else {
-            hwc1Layer.planeAlpha =
-                    static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f);
-        }
-        mPlaneAlpha.latch();
-    }
-    if (applyAllState || mSourceCrop.isDirty()) {
-        if (minorVersion < 3) {
-            auto pending = mSourceCrop.getPendingValue();
-            hwc1Layer.sourceCropi.left =
-                    static_cast<int32_t>(std::ceil(pending.left));
-            hwc1Layer.sourceCropi.top =
-                    static_cast<int32_t>(std::ceil(pending.top));
-            hwc1Layer.sourceCropi.right =
-                    static_cast<int32_t>(std::floor(pending.right));
-            hwc1Layer.sourceCropi.bottom =
-                    static_cast<int32_t>(std::floor(pending.bottom));
-        } else {
-            hwc1Layer.sourceCropf = mSourceCrop.getPendingValue();
-        }
-        mSourceCrop.latch();
-    }
-    if (applyAllState || mTransform.isDirty()) {
-        hwc1Layer.transform =
-                static_cast<uint32_t>(mTransform.getPendingValue());
-        mTransform.latch();
-    }
-    if (applyAllState || mVisibleRegion.isDirty()) {
-        auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen;
+    hwc1Layer.blending = getHwc1Blending(mBlendMode);
+    hwc1Layer.displayFrame = mDisplayFrame;
 
-        std::free(const_cast<hwc_rect_t*>(hwc1VisibleRegion.rects));
+    auto pendingAlpha = mPlaneAlpha;
+    if (minorVersion < 2) {
+        mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f;
+    } else {
+        hwc1Layer.planeAlpha =
+                static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f);
+    }
 
-        auto pending = mVisibleRegion.getPendingValue();
-        hwc_rect_t* newRects = static_cast<hwc_rect_t*>(
-                std::malloc(sizeof(hwc_rect_t) * pending.size()));
-        std::copy(pending.begin(), pending.end(), newRects);
-        hwc1VisibleRegion.rects = const_cast<const hwc_rect_t*>(newRects);
-        hwc1VisibleRegion.numRects = pending.size();
-        mVisibleRegion.latch();
+    if (minorVersion < 3) {
+        auto pending = mSourceCrop;
+        hwc1Layer.sourceCropi.left =
+                static_cast<int32_t>(std::ceil(pending.left));
+        hwc1Layer.sourceCropi.top =
+                static_cast<int32_t>(std::ceil(pending.top));
+        hwc1Layer.sourceCropi.right =
+                static_cast<int32_t>(std::floor(pending.right));
+        hwc1Layer.sourceCropi.bottom =
+                static_cast<int32_t>(std::floor(pending.bottom));
+    } else {
+        hwc1Layer.sourceCropf = mSourceCrop;
+    }
+
+    hwc1Layer.transform = static_cast<uint32_t>(mTransform);
+
+    auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen;
+    hwc1VisibleRegion.numRects = mVisibleRegion.size();
+    hwc_rect_t* rects = mDisplay.GetRects(hwc1VisibleRegion.numRects);
+    hwc1VisibleRegion.rects = rects;
+    for (size_t i = 0; i < mVisibleRegion.size(); i++) {
+        rects[i] = mVisibleRegion[i];
     }
 }
 
-void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
-    if (applyAllState || mColor.isDirty()) {
-        hwc1Layer.backgroundColor = mColor.getPendingValue();
-        mColor.latch();
+void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer) {
+    // If the device does not support background color it is likely to make
+    // assumption regarding backgroundColor and handle (both fields occupy
+    // the same location in hwc_layer_1_t union).
+    // To not confuse these devices we don't set background color and we
+    // make sure handle is a null pointer.
+    if (hasUnsupportedBackgroundColor()) {
+        hwc1Layer.handle = nullptr;
+    } else {
+        hwc1Layer.backgroundColor = mColor;
     }
 }
 
-void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
-    if (applyAllState || mSidebandStream.isDirty()) {
-        hwc1Layer.sidebandStream = mSidebandStream.getPendingValue();
-        mSidebandStream.latch();
-    }
+void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer) {
+    hwc1Layer.sidebandStream = mSidebandStream;
 }
 
-void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer)
-{
+void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer) {
     hwc1Layer.handle = mBuffer.getBuffer();
     hwc1Layer.acquireFenceFd = mBuffer.getFence();
 }
 
-void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer,
-        bool applyAllState)
-{
+void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer) {
     // HWC1 never supports color transforms or dataspaces and only sometimes
     // supports plane alpha (depending on the version). These require us to drop
     // some or all layers to client composition.
-    if (mHasUnsupportedDataspace || mHasUnsupportedPlaneAlpha ||
-            mDisplay.hasColorTransform()) {
+    if (mHasUnsupportedPlaneAlpha || mDisplay.hasColorTransform() ||
+            hasUnsupportedBackgroundColor()) {
         hwc1Layer.compositionType = HWC_FRAMEBUFFER;
         hwc1Layer.flags = HWC_SKIP_LAYER;
         return;
     }
 
-    if (applyAllState || mCompositionType.isDirty()) {
-        hwc1Layer.flags = 0;
-        switch (mCompositionType.getPendingValue()) {
-            case Composition::Client:
+    hwc1Layer.flags = 0;
+    switch (mCompositionType) {
+        case Composition::Client:
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            hwc1Layer.flags |= HWC_SKIP_LAYER;
+            break;
+        case Composition::Device:
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            break;
+        case Composition::SolidColor:
+            // In theory the following line should work, but since the HWC1
+            // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1
+            // devices may not work correctly. To be on the safe side, we
+            // fall back to client composition.
+            //
+            // hwc1Layer.compositionType = HWC_BACKGROUND;
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            hwc1Layer.flags |= HWC_SKIP_LAYER;
+            break;
+        case Composition::Cursor:
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) {
+                hwc1Layer.hints |= HWC_IS_CURSOR_LAYER;
+            }
+            break;
+        case Composition::Sideband:
+            if (mDisplay.getDevice().getHwc1MinorVersion() < 4) {
+                hwc1Layer.compositionType = HWC_SIDEBAND;
+            } else {
                 hwc1Layer.compositionType = HWC_FRAMEBUFFER;
                 hwc1Layer.flags |= HWC_SKIP_LAYER;
-                break;
-            case Composition::Device:
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                break;
-            case Composition::SolidColor:
-                // In theory the following line should work, but since the HWC1
-                // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1
-                // devices may not work correctly. To be on the safe side, we
-                // fall back to client composition.
-                //
-                // hwc1Layer.compositionType = HWC_BACKGROUND;
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                hwc1Layer.flags |= HWC_SKIP_LAYER;
-                break;
-            case Composition::Cursor:
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) {
-                    hwc1Layer.hints |= HWC_IS_CURSOR_LAYER;
-                }
-                break;
-            case Composition::Sideband:
-                if (mDisplay.getDevice().getHwc1MinorVersion() < 4) {
-                    hwc1Layer.compositionType = HWC_SIDEBAND;
-                } else {
-                    hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                    hwc1Layer.flags |= HWC_SKIP_LAYER;
-                }
-                break;
-            default:
-                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
-                hwc1Layer.flags |= HWC_SKIP_LAYER;
-                break;
-        }
-        ALOGV("Layer %" PRIu64 " %s set to %d", mId,
-                to_string(mCompositionType.getPendingValue()).c_str(),
-                hwc1Layer.compositionType);
-        ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, "    and skipping");
-        mCompositionType.latch();
+            }
+            break;
+        default:
+            hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+            hwc1Layer.flags |= HWC_SKIP_LAYER;
+            break;
     }
+    ALOGV("Layer %" PRIu64 " %s set to %d", mId,
+            to_string(mCompositionType).c_str(),
+            hwc1Layer.compositionType);
+    ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, "    and skipping");
 }
 
 // Adapter helpers
 
-void HWC2On1Adapter::populateCapabilities()
-{
-    ALOGV("populateCapabilities");
+void HWC2On1Adapter::populateCapabilities() {
     if (mHwc1MinorVersion >= 3U) {
         int supportedTypes = 0;
         auto result = mHwc1Device->query(mHwc1Device,
@@ -2359,10 +2215,21 @@
     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)
-{
+HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id) {
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
 
     auto display = mDisplays.find(id);
@@ -2374,8 +2241,7 @@
 }
 
 std::tuple<HWC2On1Adapter::Layer*, Error> HWC2On1Adapter::getLayer(
-        hwc2_display_t displayId, hwc2_layer_t layerId)
-{
+        hwc2_display_t displayId, hwc2_layer_t layerId) {
     auto display = getDisplay(displayId);
     if (!display) {
         return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay);
@@ -2393,22 +2259,17 @@
     return std::make_tuple(layer.get(), Error::None);
 }
 
-void HWC2On1Adapter::populatePrimary()
-{
-    ALOGV("populatePrimary");
-
+void HWC2On1Adapter::populatePrimary() {
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
 
-    auto display =
-            std::make_shared<Display>(*this, HWC2::DisplayType::Physical);
+    auto display = std::make_shared<Display>(*this, HWC2::DisplayType::Physical);
     mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId();
     display->setHwc1Id(HWC_DISPLAY_PRIMARY);
     display->populateConfigs();
     mDisplays.emplace(display->getId(), std::move(display));
 }
 
-bool HWC2On1Adapter::prepareAllDisplays()
-{
+bool HWC2On1Adapter::prepareAllDisplays() {
     ATRACE_CALL();
 
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
@@ -2420,29 +2281,28 @@
         }
     }
 
-    if (mHwc1DisplayMap.count(0) == 0) {
+    if (mHwc1DisplayMap.count(HWC_DISPLAY_PRIMARY) == 0) {
         ALOGE("prepareAllDisplays: Unable to find primary HWC1 display");
         return false;
     }
 
+    // Build an array of hwc_display_contents_1 to call prepare() on HWC1.
+    mHwc1Contents.clear();
+
     // Always push the primary display
-    std::vector<HWC2On1Adapter::Display::HWC1Contents> requestedContents;
     auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY];
     auto& primaryDisplay = mDisplays[primaryDisplayId];
-    auto primaryDisplayContents = primaryDisplay->cloneRequestedContents();
-    requestedContents.push_back(std::move(primaryDisplayContents));
+    mHwc1Contents.push_back(primaryDisplay->getDisplayContents());
 
     // Push the external display, if present
     if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) {
         auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL];
         auto& externalDisplay = mDisplays[externalDisplayId];
-        auto externalDisplayContents =
-                externalDisplay->cloneRequestedContents();
-        requestedContents.push_back(std::move(externalDisplayContents));
+        mHwc1Contents.push_back(externalDisplay->getDisplayContents());
     } else {
         // Even if an external display isn't present, we still need to send
         // at least two displays down to HWC1
-        requestedContents.push_back(nullptr);
+        mHwc1Contents.push_back(nullptr);
     }
 
     // Push the hardware virtual display, if supported and present
@@ -2450,17 +2310,13 @@
         if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) {
             auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL];
             auto& virtualDisplay = mDisplays[virtualDisplayId];
-            auto virtualDisplayContents =
-                    virtualDisplay->cloneRequestedContents();
-            requestedContents.push_back(std::move(virtualDisplayContents));
+            mHwc1Contents.push_back(virtualDisplay->getDisplayContents());
         } else {
-            requestedContents.push_back(nullptr);
+            mHwc1Contents.push_back(nullptr);
         }
     }
 
-    mHwc1Contents.clear();
-    for (auto& displayContents : requestedContents) {
-        mHwc1Contents.push_back(displayContents.get());
+    for (auto& displayContents : mHwc1Contents) {
         if (!displayContents) {
             continue;
         }
@@ -2498,14 +2354,90 @@
 
         auto displayId = mHwc1DisplayMap[hwc1Id];
         auto& display = mDisplays[displayId];
-        display->setReceivedContents(std::move(requestedContents[hwc1Id]));
+        display->generateChanges();
     }
 
     return true;
 }
 
-Error HWC2On1Adapter::setAllDisplays()
-{
+void dumpHWC1Message(hwc_composer_device_1* device, size_t numDisplays,
+                     hwc_display_contents_1_t** displays) {
+    ALOGV("*****************************");
+    size_t displayId = 0;
+    while (displayId < numDisplays) {
+        hwc_display_contents_1_t* display = displays[displayId];
+
+        ALOGV("hwc_display_contents_1_t[%zu] @0x%p", displayId, display);
+        if (display == nullptr) {
+            displayId++;
+            continue;
+        }
+        ALOGV("  retirefd:0x%08x", display->retireFenceFd);
+        ALOGV("  outbuf  :0x%p", display->outbuf);
+        ALOGV("  outbuffd:0x%08x", display->outbufAcquireFenceFd);
+        ALOGV("  flags   :0x%08x", display->flags);
+        for(size_t layerId=0 ; layerId < display->numHwLayers ; layerId++) {
+            hwc_layer_1_t& layer = display->hwLayers[layerId];
+            ALOGV("    Layer[%zu]:", layerId);
+            ALOGV("      composition        : 0x%08x", layer.compositionType);
+            ALOGV("      hints              : 0x%08x", layer.hints);
+            ALOGV("      flags              : 0x%08x", layer.flags);
+            ALOGV("      handle             : 0x%p", layer.handle);
+            ALOGV("      transform          : 0x%08x", layer.transform);
+            ALOGV("      blending           : 0x%08x", layer.blending);
+            ALOGV("      sourceCropf        : %f, %f, %f, %f",
+                  layer.sourceCropf.left,
+                  layer.sourceCropf.top,
+                  layer.sourceCropf.right,
+                  layer.sourceCropf.bottom);
+            ALOGV("      displayFrame       : %d, %d, %d, %d",
+                  layer.displayFrame.left,
+                  layer.displayFrame.left,
+                  layer.displayFrame.left,
+                  layer.displayFrame.left);
+            hwc_region_t& visReg = layer.visibleRegionScreen;
+            ALOGV("      visibleRegionScreen: #0x%08zx[@0x%p]",
+                  visReg.numRects,
+                  visReg.rects);
+            for (size_t visRegId=0; visRegId < visReg.numRects ; visRegId++) {
+                if (layer.visibleRegionScreen.rects == nullptr) {
+                    ALOGV("        null");
+                } else {
+                    ALOGV("        visibleRegionScreen[%zu] %d, %d, %d, %d",
+                          visRegId,
+                          visReg.rects[visRegId].left,
+                          visReg.rects[visRegId].top,
+                          visReg.rects[visRegId].right,
+                          visReg.rects[visRegId].bottom);
+                }
+            }
+            ALOGV("      acquireFenceFd     : 0x%08x", layer.acquireFenceFd);
+            ALOGV("      releaseFenceFd     : 0x%08x", layer.releaseFenceFd);
+            ALOGV("      planeAlpha         : 0x%08x", layer.planeAlpha);
+            if (getMinorVersion(device) < 5)
+               continue;
+            ALOGV("      surfaceDamage      : #0x%08zx[@0x%p]",
+                  layer.surfaceDamage.numRects,
+                  layer.surfaceDamage.rects);
+            for (size_t sdId=0; sdId < layer.surfaceDamage.numRects ; sdId++) {
+                if (layer.surfaceDamage.rects == nullptr) {
+                    ALOGV("      null");
+                } else {
+                    ALOGV("      surfaceDamage[%zu] %d, %d, %d, %d",
+                          sdId,
+                          layer.surfaceDamage.rects[sdId].left,
+                          layer.surfaceDamage.rects[sdId].top,
+                          layer.surfaceDamage.rects[sdId].right,
+                          layer.surfaceDamage.rects[sdId].bottom);
+                }
+            }
+        }
+        displayId++;
+    }
+    ALOGV("-----------------------------");
+}
+
+Error HWC2On1Adapter::setAllDisplays() {
     ATRACE_CALL();
 
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
@@ -2529,6 +2461,7 @@
     ALOGV("Calling HWC1 set");
     {
         ATRACE_NAME("HWC1 set");
+        //dumpHWC1Message(mHwc1Device, mHwc1Contents.size(), mHwc1Contents.data());
         mHwc1Device->set(mHwc1Device, mHwc1Contents.size(),
                 mHwc1Contents.data());
     }
@@ -2551,14 +2484,13 @@
     return Error::None;
 }
 
-void HWC2On1Adapter::hwc1Invalidate()
-{
+void HWC2On1Adapter::hwc1Invalidate() {
     ALOGV("Received hwc1Invalidate");
 
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
 
     // If the HWC2-side callback hasn't been registered yet, buffer this until
-    // it is registered
+    // it is registered.
     if (mCallbacks.count(Callback::Refresh) == 0) {
         mHasPendingInvalidate = true;
         return;
@@ -2570,7 +2502,7 @@
         displays.emplace_back(displayPair.first);
     }
 
-    // Call back without the state lock held
+    // Call back without the state lock held.
     lock.unlock();
 
     auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(callbackInfo.pointer);
@@ -2579,14 +2511,13 @@
     }
 }
 
-void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp)
-{
+void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp) {
     ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp);
 
     std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);
 
     // If the HWC2-side callback hasn't been registered yet, buffer this until
-    // it is registered
+    // it is registered.
     if (mCallbacks.count(Callback::Vsync) == 0) {
         mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp);
         return;
@@ -2600,15 +2531,14 @@
     const auto& callbackInfo = mCallbacks[Callback::Vsync];
     auto displayId = mHwc1DisplayMap[hwc1DisplayId];
 
-    // Call back without the state lock held
+    // Call back without the state lock held.
     lock.unlock();
 
     auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer);
     vsync(callbackInfo.data, displayId, timestamp);
 }
 
-void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected)
-{
+void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) {
     ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected);
 
     if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) {
@@ -2663,5 +2593,4 @@
             HWC2::Connection::Disconnected : HWC2::Connection::Connected;
     hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected));
 }
-
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index 962361e..a1d2c88 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -23,7 +23,7 @@
 #undef HWC2_INCLUDE_STRINGIFICATION
 #undef HWC2_USE_CPP11
 
-#include <ui/Fence.h>
+#include "MiniFence.h"
 
 #include <atomic>
 #include <map>
@@ -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);
@@ -126,51 +134,60 @@
                     const std::shared_ptr<Layer>& rhs);
     };
 
-    class DisplayContentsDeleter {
-        public:
-            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({MiniFence::NO_FENCE, MiniFence::NO_FENCE}) {}
 
             void add(int32_t fenceFd) {
-                mFences.emplace(new Fence(fenceFd));
+                mFences.emplace(new MiniFence(fenceFd));
                 mFences.pop();
             }
 
-            const sp<Fence>& get() const {
+            const sp<MiniFence>& get() const {
                 return mFences.front();
             }
 
         private:
-            mutable std::mutex mMutex;
-            std::queue<sp<Fence>> mFences;
+            // There are always two fences in this queue.
+            std::queue<sp<MiniFence>> mFences;
     };
 
     class FencedBuffer {
         public:
-            FencedBuffer() : mBuffer(nullptr), mFence(Fence::NO_FENCE) {}
+            FencedBuffer() : mBuffer(nullptr), mFence(MiniFence::NO_FENCE) {}
 
             void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; }
-            void setFence(int fenceFd) { mFence = new Fence(fenceFd); }
+            void setFence(int fenceFd) { mFence = new MiniFence(fenceFd); }
 
             buffer_handle_t getBuffer() const { return mBuffer; }
             int getFence() const { return mFence->dup(); }
 
         private:
             buffer_handle_t mBuffer;
-            sp<Fence> mFence;
+            sp<MiniFence> mFence;
     };
 
     class Display {
         public:
-            typedef std::unique_ptr<hwc_display_contents_1,
-                    DisplayContentsDeleter> HWC1Contents;
-
             Display(HWC2On1Adapter& device, HWC2::DisplayType type);
 
             hwc2_display_t getId() const { return mId; }
@@ -181,10 +198,6 @@
             void setHwc1Id(int32_t id) { mHwc1Id = id; }
             int32_t getHwc1Id() const { return mHwc1Id; }
 
-            void incDirty() { ++mDirtyCount; }
-            void decDirty() { --mDirtyCount; }
-            bool isDirty() const { return mDirtyCount > 0 || mZIsDirty; }
-
             // HWC2 Display functions
             HWC2::Error acceptChanges();
             HWC2::Error createLayer(hwc2_layer_t* outLayerId);
@@ -208,7 +221,14 @@
                     uint32_t* outNumElements, hwc2_layer_t* outLayers,
                     int32_t* outLayerRequests);
             HWC2::Error getType(int32_t* outType);
+
+            // Since HWC1 "presents" (called "set" in HWC1) all Displays
+            // at once, the first call to any Display::present will trigger
+            // present() on all Displays in the Device. Subsequent calls without
+            // first calling validate() are noop (except for duping/returning
+            // the retire fence).
             HWC2::Error present(int32_t* outRetireFence);
+
             HWC2::Error setActiveConfig(hwc2_config_t configId);
             HWC2::Error setClientTarget(buffer_handle_t target,
                     int32_t acquireFence, int32_t dataspace,
@@ -219,6 +239,10 @@
                     int32_t releaseFence);
             HWC2::Error setPowerMode(HWC2::PowerMode mode);
             HWC2::Error setVsyncEnabled(HWC2::Vsync enabled);
+
+            // Since HWC1 "validates" (called "prepare" in HWC1) all Displays
+            // at once, the first call to any Display::validate() will trigger
+            // validate() on all other Displays in the Device.
             HWC2::Error validate(uint32_t* outNumTypes,
                     uint32_t* outNumRequests);
 
@@ -231,8 +255,10 @@
             void populateConfigs(uint32_t width, uint32_t height);
 
             bool prepare();
-            HWC1Contents cloneRequestedContents() const;
-            void setReceivedContents(HWC1Contents contents);
+
+            // Called after hwc.prepare() with responses from the device.
+            void generateChanges();
+
             bool hasChanges() const;
             HWC2::Error set(hwc_display_contents_1& hwcContents);
             void addRetireFence(int fenceFd);
@@ -242,11 +268,19 @@
 
             std::string dump() const;
 
+            // Return a rect from the pool allocated during validate()
+            hwc_rect_t* GetRects(size_t numRects);
+
+            hwc_display_contents_1* getDisplayContents();
+
+            void markGeometryChanged() { mGeometryChanged = true; }
+            void resetGeometryMarker() { mGeometryChanged = false;}
         private:
             class Config {
                 public:
                     Config(Display& display)
                       : mDisplay(display),
+                        mId(0),
                         mAttributes() {}
 
                     bool isOnDisplay(const Display& display) const {
@@ -285,6 +319,10 @@
                     std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids;
             };
 
+            // Stores changes requested from the device upon calling prepare().
+            // Handles change request to:
+            //   - Layer composition type.
+            //   - Layer hints.
             class Changes {
                 public:
                     uint32_t getNumTypes() const {
@@ -305,14 +343,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 +360,6 @@
                             mTypeChanges;
                     std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
                             mLayerRequests;
-                    std::unordered_set<HWC2::DisplayRequest> mDisplayRequests;
             };
 
             std::shared_ptr<const Config>
@@ -339,21 +368,31 @@
             void populateColorModes();
             void initializeActiveConfig();
 
-            void reallocateHwc1Contents();
+            // Creates a bi-directional mapping between index in HWC1
+            // prepare/set array and Layer object. Stores mapping in
+            // mHwc1LayerMap and also updates Layer's attribute mHwc1Id.
             void assignHwc1LayerIds();
 
+            // Called after a response to prepare() has been received:
+            // Ingest composition type changes requested by the device.
             void updateTypeChanges(const struct hwc_layer_1& hwc1Layer,
                     const Layer& layer);
+
+            // Called after a response to prepare() has been received:
+            // Ingest layer hint changes requested by the device.
             void updateLayerRequests(const struct hwc_layer_1& hwc1Layer,
                     const Layer& layer);
 
+            // Set all fields in HWC1 comm array for layer containing the
+            // HWC_FRAMEBUFFER_TARGET (always the last layer).
             void prepareFramebufferTarget();
 
+            // Display ID generator.
             static std::atomic<hwc2_display_t> sNextId;
             const hwc2_display_t mId;
-            HWC2On1Adapter& mDevice;
 
-            std::atomic<size_t> mDirtyCount;
+
+            HWC2On1Adapter& mDevice;
 
             // The state of this display should only be modified from
             // SurfaceFlinger's main loop, with the exception of when dump is
@@ -366,12 +405,18 @@
             // which require locking.
             mutable std::recursive_mutex mStateMutex;
 
-            bool mZIsDirty;
-            HWC1Contents mHwc1RequestedContents;
-            HWC1Contents mHwc1ReceivedContents;
+            // Allocate RAM able to store all layers and rects used for
+            // communication with HWC1. Place allocated RAM in variable
+            // mHwc1RequestedContents.
+            void allocateRequestedContents();
+
+            // Array of structs exchanged between client and hwc1 device.
+            // Sent to device upon calling prepare().
+            std::unique_ptr<hwc_display_contents_1> mHwc1RequestedContents;
+    private:
             DeferredFence mRetireFence;
 
-            // Will only be non-null after the layer has been validated but
+            // Will only be non-null after the Display has been validated and
             // before it has been presented
             std::unique_ptr<Changes> mChanges;
 
@@ -386,15 +431,34 @@
             HWC2::PowerMode mPowerMode;
             HWC2::Vsync mVsyncEnabled;
 
+            // Used to populate HWC1 HWC_FRAMEBUFFER_TARGET layer
             FencedBuffer mClientTarget;
+
+
             FencedBuffer mOutputBuffer;
 
             bool mHasColorTransform;
 
+            // All layers this Display is aware of.
             std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers;
+
+            // Mapping between layer index in array of hwc_display_contents_1*
+            // passed to HWC1 during validate/set and Layer object.
             std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap;
+
+            // All communication with HWC1 via prepare/set is done with one
+            // alloc. This pointer is pointing to a pool of hwc_rect_t.
+            size_t mNumAvailableRects;
+            hwc_rect_t* mNextAvailableRect;
+
+            // True if any of the Layers contained in this Display have been
+            // updated with anything other than a buffer since last call to
+            // Display::set()
+            bool mGeometryChanged;
     };
 
+    // Utility template calling a Display object method directly based on the
+    // hwc2_display_t displayId parameter.
     template <typename ...Args>
     static int32_t callDisplayFunction(hwc2_device_t* device,
             hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...),
@@ -436,7 +500,8 @@
     static int32_t setColorModeHook(hwc2_device_t* device,
             hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) {
         auto mode = static_cast<android_color_mode_t>(intMode);
-        return callDisplayFunction(device, display, &Display::setColorMode, mode);
+        return callDisplayFunction(device, display, &Display::setColorMode,
+                mode);
     }
 
     static int32_t setPowerModeHook(hwc2_device_t* device,
@@ -453,46 +518,6 @@
                 enabled);
     }
 
-    // Layer functions
-
-    template <typename T>
-    class LatchedState {
-        public:
-            LatchedState(Layer& parent, T initialValue)
-              : mParent(parent),
-                mPendingValue(initialValue),
-                mValue(initialValue) {}
-
-            void setPending(T value) {
-                if (value == mPendingValue) {
-                    return;
-                }
-                if (mPendingValue == mValue) {
-                    mParent.incDirty();
-                } else if (value == mValue) {
-                    mParent.decDirty();
-                }
-                mPendingValue = value;
-            }
-
-            T getValue() const { return mValue; }
-            T getPendingValue() const { return mPendingValue; }
-
-            bool isDirty() const { return mPendingValue != mValue; }
-
-            void latch() {
-                if (isDirty()) {
-                    mValue = mPendingValue;
-                    mParent.decDirty();
-                }
-            }
-
-        private:
-            Layer& mParent;
-            T mPendingValue;
-            T mValue;
-    };
-
     class Layer {
         public:
             explicit Layer(Display& display);
@@ -503,10 +528,6 @@
             hwc2_layer_t getId() const { return mId; }
             Display& getDisplay() const { return mDisplay; }
 
-            void incDirty() { if (mDirtyCount++ == 0) mDisplay.incDirty(); }
-            void decDirty() { if (--mDirtyCount == 0) mDisplay.decDirty(); }
-            bool isDirty() const { return mDirtyCount > 0; }
-
             // HWC2 Layer functions
             HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence);
             HWC2::Error setCursorPosition(int32_t x, int32_t y);
@@ -526,57 +547,67 @@
             HWC2::Error setZ(uint32_t z);
 
             HWC2::Composition getCompositionType() const {
-                return mCompositionType.getValue();
+                return mCompositionType;
             }
             uint32_t getZ() const { return mZ; }
 
             void addReleaseFence(int fenceFd);
-            const sp<Fence>& getReleaseFence() const;
+            const sp<MiniFence>& getReleaseFence() const;
 
             void setHwc1Id(size_t id) { mHwc1Id = id; }
             size_t getHwc1Id() const { return mHwc1Id; }
 
-            void applyState(struct hwc_layer_1& hwc1Layer, bool applyAllState);
+            // Write state to HWC1 communication struct.
+            void applyState(struct hwc_layer_1& hwc1Layer);
 
             std::string dump() const;
 
+            std::size_t getNumVisibleRegions() { return mVisibleRegion.size(); }
+
+            std::size_t getNumSurfaceDamages() { return mSurfaceDamage.size(); }
+
+            // True if a layer cannot be properly rendered by the device due
+            // to usage of SolidColor (a.k.a BackgroundColor in HWC1).
+            bool hasUnsupportedBackgroundColor() {
+                return (mCompositionType == HWC2::Composition::SolidColor &&
+                        !mDisplay.getDevice().supportsBackgroundColor());
+            }
         private:
-            void applyCommonState(struct hwc_layer_1& hwc1Layer,
-                    bool applyAllState);
-            void applySolidColorState(struct hwc_layer_1& hwc1Layer,
-                    bool applyAllState);
-            void applySidebandState(struct hwc_layer_1& hwc1Layer,
-                    bool applyAllState);
+            void applyCommonState(struct hwc_layer_1& hwc1Layer);
+            void applySolidColorState(struct hwc_layer_1& hwc1Layer);
+            void applySidebandState(struct hwc_layer_1& hwc1Layer);
             void applyBufferState(struct hwc_layer_1& hwc1Layer);
-            void applyCompositionType(struct hwc_layer_1& hwc1Layer,
-                    bool applyAllState);
+            void applyCompositionType(struct hwc_layer_1& hwc1Layer);
 
             static std::atomic<hwc2_layer_t> sNextId;
             const hwc2_layer_t mId;
             Display& mDisplay;
-            size_t mDirtyCount;
 
             FencedBuffer mBuffer;
             std::vector<hwc_rect_t> mSurfaceDamage;
 
-            LatchedState<HWC2::BlendMode> mBlendMode;
-            LatchedState<hwc_color_t> mColor;
-            LatchedState<HWC2::Composition> mCompositionType;
-            LatchedState<hwc_rect_t> mDisplayFrame;
-            LatchedState<float> mPlaneAlpha;
-            LatchedState<const native_handle_t*> mSidebandStream;
-            LatchedState<hwc_frect_t> mSourceCrop;
-            LatchedState<HWC2::Transform> mTransform;
-            LatchedState<std::vector<hwc_rect_t>> mVisibleRegion;
+            HWC2::BlendMode mBlendMode;
+            hwc_color_t mColor;
+            HWC2::Composition mCompositionType;
+            hwc_rect_t mDisplayFrame;
+            float mPlaneAlpha;
+            const native_handle_t* mSidebandStream;
+            hwc_frect_t mSourceCrop;
+            HWC2::Transform mTransform;
+            std::vector<hwc_rect_t> mVisibleRegion;
+
             uint32_t mZ;
 
             DeferredFence mReleaseFence;
 
             size_t mHwc1Id;
-            bool mHasUnsupportedDataspace;
             bool mHasUnsupportedPlaneAlpha;
     };
 
+    // Utility tempate calling a Layer object method based on ID parameters:
+    // hwc2_display_t displayId
+    // and
+    // hwc2_layer_t layerId
     template <typename ...Args>
     static int32_t callLayerFunction(hwc2_device_t* device,
             hwc2_display_t displayId, hwc2_layer_t layerId,
@@ -645,6 +676,7 @@
     std::vector<struct hwc_display_contents_1*> mHwc1Contents;
     HWC2::Error setAllDisplays();
 
+    // Callbacks
     void hwc1Invalidate();
     void hwc1Vsync(int hwc1DisplayId, int64_t timestamp);
     void hwc1Hotplug(int hwc1DisplayId, int connected);
@@ -655,6 +687,7 @@
     struct hwc_composer_device_1* const mHwc1Device;
     const uint8_t mHwc1MinorVersion;
     bool mHwc1SupportsVirtualDisplays;
+    bool mHwc1SupportsBackgroundColor;
 
     class Callbacks;
     const std::unique_ptr<Callbacks> mHwc1Callbacks;
@@ -665,6 +698,8 @@
     // callbacks or dump
 
     std::map<hwc2_layer_t, std::shared_ptr<Layer>> mLayers;
+
+    // A HWC1 supports only one virtual display.
     std::shared_ptr<Display> mHwc1VirtualDisplay;
 
     // These are potentially accessed from multiple threads, and are protected
@@ -679,10 +714,19 @@
     };
     std::unordered_map<HWC2::Callback, CallbackInfo> mCallbacks;
     bool mHasPendingInvalidate;
+
+    // There is a small gap between the time the HWC1 module is started and
+    // when the callbacks for vsync and hotplugs are registered by the
+    // HWC2on1Adapter. To prevent losing events they are stored in these arrays
+    // and fed to the callback as soon as possible.
     std::vector<std::pair<int, int64_t>> mPendingVsyncs;
     std::vector<std::pair<int, int>> mPendingHotplugs;
 
+    // Mapping between HWC1 display id and Display objects.
     std::map<hwc2_display_t, std::shared_ptr<Display>> mDisplays;
+
+    // Map HWC1 display type (HWC_DISPLAY_PRIMARY, HWC_DISPLAY_EXTERNAL,
+    // HWC_DISPLAY_VIRTUAL) to Display IDs generated by HWC2on1Adapter objects.
     std::unordered_map<int, hwc2_display_t> mHwc1DisplayMap;
 };
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index f0ded39..f03491f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -49,6 +49,7 @@
 #include "HWComposer.h"
 #include "HWC2On1Adapter.h"
 #include "HWC2.h"
+#include "ComposerHal.h"
 
 #include "../Layer.h"           // needed only for debugging
 #include "../SurfaceFlinger.h"
@@ -59,9 +60,8 @@
 
 // ---------------------------------------------------------------------------
 
-HWComposer::HWComposer(const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger),
-      mAdapter(),
+HWComposer::HWComposer(bool useVrComposer)
+    : mAdapter(),
       mHwcDevice(),
       mDisplayData(2),
       mFreeDisplaySlots(),
@@ -69,14 +69,15 @@
       mCBContext(),
       mEventHandler(nullptr),
       mVSyncCounts(),
-      mRemainingHwcVirtualDisplays(0)
+      mRemainingHwcVirtualDisplays(0),
+      mDumpMayLockUp(false)
 {
     for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
         mLastHwVSync[i] = 0;
         mVSyncCounts[i] = 0;
     }
 
-    loadHwcModule();
+    loadHwcModule(useVrComposer);
 }
 
 HWComposer::~HWComposer() {}
@@ -105,10 +106,13 @@
 }
 
 // Load and prepare the hardware composer module.  Sets mHwc.
-void HWComposer::loadHwcModule()
+void HWComposer::loadHwcModule(bool useVrComposer)
 {
     ALOGV("loadHwcModule");
 
+#ifdef BYPASS_IHWC
+    (void)useVrComposer; // Silence unused parameter warning.
+
     hw_module_t const* module;
 
     if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
@@ -140,6 +144,9 @@
         mHwcDevice = std::make_unique<HWC2::Device>(
                 static_cast<hwc2_device_t*>(mAdapter.get()));
     }
+#else
+    mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer);
+#endif
 
     mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
 }
@@ -204,7 +211,7 @@
 }
 
 void HWComposer::invalidate(const std::shared_ptr<HWC2::Display>& /*display*/) {
-    mFlinger->repaintEverything();
+    mEventHandler->onInvalidateReceived(this);
 }
 
 void HWComposer::vsync(const std::shared_ptr<HWC2::Display>& display,
@@ -250,7 +257,7 @@
     snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
     ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
 
-    mEventHandler->onVSyncReceived(disp, timestamp);
+    mEventHandler->onVSyncReceived(this, disp, timestamp);
 }
 
 status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -305,22 +312,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 +349,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 +411,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 +428,7 @@
     // that even if HWC blocks (which it shouldn't), it won't
     // affect other threads.
     Mutex::Autolock _l(mVsyncLock);
-    auto& displayData = mDisplayData[disp];
+    auto& displayData = mDisplayData[displayId];
     if (enabled != displayData.vsyncEnabled) {
         ATRACE_CALL();
         auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
@@ -428,18 +436,18 @@
             displayData.vsyncEnabled = enabled;
 
             char tag[16];
-            snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", disp);
+            snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId);
             ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0);
         } else {
             ALOGE("setVsyncEnabled: Failed to set vsync to %s on %d/%" PRIu64
-                    ": %s (%d)", to_string(enabled).c_str(), disp,
-                    mDisplayData[disp].hwcDisplay->getId(),
+                    ": %s (%d)", to_string(enabled).c_str(), displayId,
+                    mDisplayData[displayId].hwcDisplay->getId(),
                     to_string(error).c_str(), static_cast<int32_t>(error));
         }
     }
 }
 
-status_t HWComposer::setClientTarget(int32_t displayId,
+status_t HWComposer::setClientTarget(int32_t displayId, uint32_t slot,
         const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
         android_dataspace_t dataspace) {
     if (!isValidDisplay(displayId)) {
@@ -452,7 +460,8 @@
     if ((target != nullptr) && target->getNativeBuffer()) {
         handle = target->getNativeBuffer()->handle;
     }
-    auto error = hwcDisplay->setClientTarget(handle, acquireFence, dataspace);
+    auto error = hwcDisplay->setClientTarget(slot, handle,
+            acquireFence, dataspace);
     if (error != HWC2::Error::None) {
         ALOGE("Failed to set client target for display %d: %s (%d)", displayId,
                 to_string(error).c_str(), static_cast<int32_t>(error));
@@ -481,6 +490,8 @@
         return NO_ERROR;
     }
 
+    mDumpMayLockUp = true;
+
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
     auto error = hwcDisplay->validate(&numTypes, &numRequests);
@@ -589,12 +600,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 +626,7 @@
     return displayFences[layer];
 }
 
-status_t HWComposer::commit(int32_t displayId) {
+status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) {
     ATRACE_CALL();
 
     if (!isValidDisplay(displayId)) {
@@ -620,17 +635,21 @@
 
     auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
-    auto error = hwcDisplay->present(&displayData.lastRetireFence);
+    auto error = hwcDisplay->present(&displayData.lastPresentFence);
+
+    mDumpMayLockUp = false;
+
     if (error != HWC2::Error::None) {
-        ALOGE("commit: present failed for display %d: %s (%d)", displayId,
-                to_string(error).c_str(), static_cast<int32_t>(error));
+        ALOGE("presentAndGetReleaseFences: failed for display %d: %s (%d)",
+              displayId, to_string(error).c_str(), static_cast<int32_t>(error));
         return UNKNOWN_ERROR;
     }
 
     std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences;
     error = hwcDisplay->getReleaseFences(&releaseFences);
     if (error != HWC2::Error::None) {
-        ALOGE("commit: Failed to get release fences for display %d: %s (%d)",
+        ALOGE("presentAndGetReleaseFences: Failed to get release fences "
+              "for display %d: %s (%d)",
                 displayId, to_string(error).c_str(),
                 static_cast<int32_t>(error));
         return UNKNOWN_ERROR;
@@ -856,7 +875,20 @@
 }
 */
 
+bool HWComposer::isUsingVrComposer() const {
+#ifdef BYPASS_IHWC
+    return false;
+#else
+    return getComposer()->isUsingVrComposer();
+#endif
+}
+
 void HWComposer::dump(String8& result) const {
+    if (mDumpMayLockUp) {
+        result.append("HWComposer dump skipped because present in progress");
+        return;
+    }
+
     // TODO: In order to provide a dump equivalent to HWC1, we need to shadow
     // all the state going into the layers. This is probably better done in
     // Layer itself, but it's going to take a bit of work to get there.
@@ -869,7 +901,7 @@
   : hasClientComposition(false),
     hasDeviceComposition(false),
     hwcDisplay(),
-    lastRetireFence(Fence::NO_FENCE),
+    lastPresentFence(Fence::NO_FENCE),
     outbufHandle(nullptr),
     outbufAcquireFence(Fence::NO_FENCE),
     vsyncEnabled(HWC2::Vsync::Disable) {
@@ -884,5 +916,41 @@
     *this = DisplayData();
 }
 
+void HWComposerBufferCache::clear()
+{
+    mBuffers.clear();
+}
+
+void HWComposerBufferCache::getHwcBuffer(int slot,
+        const sp<GraphicBuffer>& buffer,
+        uint32_t* outSlot, sp<GraphicBuffer>* outBuffer)
+{
+#ifdef BYPASS_IHWC
+    *outSlot = slot;
+    *outBuffer = buffer;
+#else
+    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) {
+        // default to slot 0
+        slot = 0;
+    }
+
+    if (static_cast<size_t>(slot) >= mBuffers.size()) {
+        mBuffers.resize(slot + 1);
+    }
+
+    *outSlot = slot;
+
+    if (mBuffers[slot] == buffer) {
+        // already cached in HWC, skip sending the buffer
+        *outBuffer = nullptr;
+    } else {
+        *outBuffer = buffer;
+
+        // update cache
+        mBuffers[slot] = buffer;
+    }
+#endif
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 41671f6..20cca39 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -26,6 +26,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <gui/BufferQueue.h>
+
 #include <ui/Fence.h>
 
 #include <utils/BitSet.h>
@@ -62,20 +64,24 @@
 class NativeHandle;
 class Region;
 class String8;
-class SurfaceFlinger;
 
 class HWComposer
 {
 public:
     class EventHandler {
         friend class HWComposer;
-        virtual void onVSyncReceived(int32_t disp, nsecs_t timestamp) = 0;
+        virtual void onVSyncReceived(
+            HWComposer* composer, int32_t disp, nsecs_t timestamp) = 0;
         virtual void onHotplugReceived(int32_t disp, bool connected) = 0;
+        virtual void onInvalidateReceived(HWComposer* composer) = 0;
     protected:
         virtual ~EventHandler() {}
     };
 
-    HWComposer(const sp<SurfaceFlinger>& flinger);
+    // useVrComposer is passed to the composer HAL. When true, the composer HAL
+    // will use the vr composer service, otherwise it uses the real hardware
+    // composer.
+    HWComposer(bool useVrComposer);
 
     ~HWComposer();
 
@@ -94,11 +100,12 @@
     // Asks the HAL what it can do
     status_t prepare(DisplayDevice& displayDevice);
 
-    status_t setClientTarget(int32_t displayId, const sp<Fence>& acquireFence,
+    status_t setClientTarget(int32_t displayId, uint32_t slot,
+            const sp<Fence>& acquireFence,
             const sp<GraphicBuffer>& target, android_dataspace_t dataspace);
 
-    // Finalize the layers and present them
-    status_t commit(int32_t displayId);
+    // Present layers to the display and read releaseFences.
+    status_t presentAndGetReleaseFences(int32_t displayId);
 
     // set power mode
     status_t setPowerMode(int32_t displayId, int mode);
@@ -118,9 +125,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 +151,12 @@
 
     // Events handling ---------------------------------------------------------
 
-    void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled);
+    void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
 
     // Query display parameters.  Pass in a display index (e.g.
     // HWC_DISPLAY_PRIMARY).
-    nsecs_t getRefreshTimestamp(int32_t disp) const;
-    bool isConnected(int32_t disp) const;
+    nsecs_t getRefreshTimestamp(int32_t displayId) const;
+    bool isConnected(int32_t displayId) const;
 
     // Non-const because it can update configMap inside of mDisplayData
     std::vector<std::shared_ptr<const HWC2::Display::Config>>
@@ -158,13 +169,16 @@
 
     status_t setActiveColorMode(int32_t displayId, android_color_mode_t mode);
 
+    bool isUsingVrComposer() const;
+
     // for debugging ----------------------------------------------------------
     void dump(String8& out) const;
 
+    android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
 private:
     static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2;
 
-    void loadHwcModule();
+    void loadHwcModule(bool useVrComposer);
 
     bool isValidDisplay(int32_t displayId) const;
     static void validateChange(HWC2::Composition from, HWC2::Composition to);
@@ -186,7 +200,7 @@
         bool hasDeviceComposition;
         std::shared_ptr<HWC2::Display> hwcDisplay;
         HWC2::DisplayRequest displayRequests;
-        sp<Fence> lastRetireFence;  // signals when the last set op retires
+        sp<Fence> lastPresentFence;  // signals when the last set op retires
         std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>>
                 releaseFences;
         buffer_handle_t outbufHandle;
@@ -198,7 +212,6 @@
         HWC2::Vsync vsyncEnabled;
     };
 
-    sp<SurfaceFlinger>              mFlinger;
     std::unique_ptr<HWC2On1Adapter> mAdapter;
     std::unique_ptr<HWC2::Device>   mHwcDevice;
     std::vector<DisplayData>        mDisplayData;
@@ -218,6 +231,22 @@
 
     // thread-safe
     mutable Mutex mVsyncLock;
+
+    // XXX temporary workaround for b/35806047
+    mutable std::atomic<bool> mDumpMayLockUp;
+};
+
+class HWComposerBufferCache {
+public:
+    void clear();
+
+    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer,
+            uint32_t* outSlot, sp<GraphicBuffer>* outBuffer);
+
+private:
+    // a vector as we expect "slot" to be in the range of [0, 63] (that is,
+    // less than BufferQueue::NUM_BUFFER_SLOTS).
+    std::vector<sp<GraphicBuffer>> mBuffers;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index 52cbb34..5b5f1cf 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -277,7 +277,7 @@
 }
 
 void HWComposer::invalidate() {
-    mFlinger->repaintEverything();
+    mEventHandler.onInvalidateReceived(this);
 }
 
 void HWComposer::vsync(int disp, int64_t timestamp) {
@@ -302,7 +302,7 @@
         snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
         ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
 
-        mEventHandler.onVSyncReceived(disp, timestamp);
+        mEventHandler.onVSyncReceived(this, disp, timestamp);
     }
 }
 
@@ -933,7 +933,7 @@
 protected:
     HWCTYPE* const mLayerList;
     HWCTYPE* mCurrentLayer;
-    Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
+    explicit Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
             mIndex(0) { }
     inline HWCTYPE const * getLayer() const { return mCurrentLayer; }
     inline HWCTYPE* getLayer() { return mCurrentLayer; }
@@ -1159,6 +1159,8 @@
     switch (format) {
     case PIXEL_FORMAT_RGBA_8888:    return String8("RGBA_8888");
     case PIXEL_FORMAT_RGBX_8888:    return String8("RGBx_8888");
+    case PIXEL_FORMAT_RGBA_FP16:    return String8("RGBA_FP16");
+    case PIXEL_FORMAT_RGBA_1010102: return String8("RGBA_1010102");
     case PIXEL_FORMAT_RGB_888:      return String8("RGB_888");
     case PIXEL_FORMAT_RGB_565:      return String8("RGB_565");
     case PIXEL_FORMAT_BGRA_8888:    return String8("BGRA_8888");
@@ -1317,7 +1319,7 @@
     } while (err<0 && errno == EINTR);
 
     if (err == 0) {
-        mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
+        mHwc.mEventHandler.onVSyncReceived(&mHwc, 0, next_vsync);
     }
 
     return true;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
index 170e382..f64d69a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
@@ -60,8 +60,10 @@
 public:
     class EventHandler {
         friend class HWComposer;
-        virtual void onVSyncReceived(int disp, nsecs_t timestamp) = 0;
+        virtual void onVSyncReceived(
+            HWComposer* composer, int32_t disp, nsecs_t timestamp) = 0;
         virtual void onHotplugReceived(int disp, bool connected) = 0;
+        virtual void onInvalidateReceived(HWComposer* composer) = 0;
     protected:
         virtual ~EventHandler() {}
     };
diff --git a/services/surfaceflinger/DisplayHardware/MiniFence.cpp b/services/surfaceflinger/DisplayHardware/MiniFence.cpp
new file mode 100644
index 0000000..ecfb063
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/MiniFence.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MiniFence.h"
+
+#include <unistd.h>
+
+namespace android {
+
+const sp<MiniFence> MiniFence::NO_FENCE = sp<MiniFence>(new MiniFence);
+
+MiniFence::MiniFence() :
+    mFenceFd(-1) {
+}
+
+MiniFence::MiniFence(int fenceFd) :
+    mFenceFd(fenceFd) {
+}
+
+MiniFence::~MiniFence() {
+    if (mFenceFd != -1) {
+        close(mFenceFd);
+    }
+}
+
+int MiniFence::dup() const {
+    return ::dup(mFenceFd);
+}
+}
diff --git a/services/surfaceflinger/DisplayHardware/MiniFence.h b/services/surfaceflinger/DisplayHardware/MiniFence.h
new file mode 100644
index 0000000..75de764
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/MiniFence.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MINIFENCE_H
+#define MINIFENCE_H
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+/* MiniFence is a minimal re-implementation of Fence from libui. It exists to
+ * avoid linking the HWC2on1Adapter to libui and satisfy Treble requirements.
+ */
+class MiniFence : public LightRefBase<MiniFence> {
+public:
+    static const sp<MiniFence> NO_FENCE;
+
+    // Construct a new MiniFence object with an invalid file descriptor.
+    MiniFence();
+
+    // Construct a new MiniFence object to manage a given fence file descriptor.
+    // When the new MiniFence object is destructed the file descriptor will be
+    // closed.
+    explicit MiniFence(int fenceFd);
+
+    // Not copyable or movable.
+    MiniFence(const MiniFence& rhs) = delete;
+    MiniFence& operator=(const MiniFence& rhs) = delete;
+    MiniFence(MiniFence&& rhs) = delete;
+    MiniFence& operator=(MiniFence&& rhs) = delete;
+
+    // Return a duplicate of the fence file descriptor. The caller is
+    // responsible for closing the returned file descriptor. On error, -1 will
+    // be returned and errno will indicate the problem.
+    int dup() const;
+
+private:
+    // Only allow instantiation using ref counting.
+    friend class LightRefBase<MiniFence>;
+    ~MiniFence();
+
+    int mFenceFd;
+
+};
+}
+#endif //MINIFENCE_H
diff --git a/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..c5a4f99 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -19,6 +19,7 @@
 #include "HWComposer.h"
 
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 #include <gui/IProducerListener.h>
 
 // ---------------------------------------------------------------------------
@@ -221,9 +222,14 @@
     status_t result = NO_ERROR;
     if (fbBuffer != NULL) {
 #ifdef USE_HWC2
+        uint32_t hwcSlot = 0;
+        sp<GraphicBuffer> hwcBuffer;
+        mHwcBufferCache->getHwcBuffer(mFbProducerSlot, fbBuffer,
+                &hwcSlot, &hwcBuffer);
+
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(mDisplayId, mFbFence, fbBuffer,
-                HAL_DATASPACE_UNKNOWN);
+        result = mHwc.setClientTarget(mDisplayId, hwcSlot, mFbFence,
+                hwcBuffer, HAL_DATASPACE_UNKNOWN);
 #else
         result = mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
 #endif
@@ -241,7 +247,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 +287,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 +309,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 +346,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 +385,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 +522,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 +562,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 +623,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..fb5fcc8 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -17,16 +17,19 @@
 #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
 
+#include "DisplaySurface.h"
+
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
 
-#include "DisplaySurface.h"
+#include <memory>
 
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
 
 class HWComposer;
+class HWComposerBufferCache;
 class IProducerListener;
 
 /* This DisplaySurface implementation supports virtual displays, where GLES
@@ -104,7 +107,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 +139,7 @@
     static Source fbSourceForCompositionType(CompositionType type);
     status_t dequeueBuffer(Source source, PixelFormat format, uint32_t usage,
             int* sslot, sp<Fence>* fence);
-    void updateQueueBufferOutput(const QueueBufferOutput& qbo);
+    void updateQueueBufferOutput(QueueBufferOutput&& qbo);
     void resetPerFrameState();
     status_t refreshOutputBuffer();
 
@@ -175,11 +179,13 @@
     // slot. Both mProducerSlotSource and mProducerBuffers are indexed by a
     // "producer slot"; see the mapSlot*() functions.
     uint64_t mProducerSlotSource;
-    sp<GraphicBuffer> mProducerBuffers[BufferQueue::NUM_BUFFER_SLOTS];
+    sp<GraphicBuffer> mProducerBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // The QueueBufferOutput with the latest info from the sink, and with the
     // transform hint cleared. Since we defer queueBuffer from the GLES driver
     // to the sink, we have to return the previous version.
+    // Moves instead of copies are performed to avoid duplicate
+    // FrameEventHistoryDeltas.
     QueueBufferOutput mQueueBufferOutput;
 
     // Details of the current sink buffer. These become valid when a buffer is
@@ -247,6 +253,11 @@
     static const char* dbgSourceStr(Source s);
 
     bool mMustRecompose;
+
+#ifdef USE_HWC2
+    std::unique_ptr<HWComposerBufferCache> mHwcBufferCache =
+        std::make_unique<HWComposerBufferCache>();
+#endif
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index a104e8f..c953c68 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "Daltonizer.h"
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h
index d21b155..2fb60e9 100644
--- a/services/surfaceflinger/Effects/Daltonizer.h
+++ b/services/surfaceflinger/Effects/Daltonizer.h
@@ -17,7 +17,7 @@
 #ifndef SF_EFFECTS_DALTONIZER_H_
 #define SF_EFFECTS_DALTONIZER_H_
 
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/EventControlThread.h
index 9368db6..1b1ef75 100644
--- a/services/surfaceflinger/EventControlThread.h
+++ b/services/surfaceflinger/EventControlThread.h
@@ -45,4 +45,4 @@
 
 }
 
-#endif // ANDROID_DISPSYNC_H
+#endif // ANDROID_EVENTCONTROLTHREAD_H
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index dd88adb..486bce4 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -21,7 +21,7 @@
 
 #include <cutils/compiler.h>
 
-#include <gui/BitTube.h>
+#include <private/gui/BitTube.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/DisplayEventReceiver.h>
 
@@ -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..c3f8665 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -38,12 +38,14 @@
 #include <ui/PixelFormat.h>
 
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 #include <gui/Surface.h>
 
 #include "clz.h"
 #include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "Layer.h"
+#include "LayerRejecter.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 
@@ -75,11 +77,14 @@
         mPendingStates(),
         mQueuedFrames(0),
         mSidebandStreamChanged(false),
+        mActiveBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
         mCurrentTransform(0),
         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
         mOverrideScalingMode(-1),
         mCurrentOpacity(true),
+        mBufferLatched(false),
         mCurrentFrameNumber(0),
+        mPreviousFrameNumber(0),
         mRefreshPending(false),
         mFrameLatencyNeeded(false),
         mFiltering(false),
@@ -136,6 +141,9 @@
     mCurrentState.flags = layerFlags;
     mCurrentState.sequence = 0;
     mCurrentState.requested = mCurrentState.active;
+    mCurrentState.dataSpace = HAL_DATASPACE_UNKNOWN;
+    mCurrentState.appId = 0;
+    mCurrentState.type = 0;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -149,23 +157,26 @@
             flinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
 #endif
     mFrameTracker.setDisplayRefreshPeriod(displayPeriod);
+
+    CompositorTiming compositorTiming;
+    flinger->getCompositorTiming(&compositorTiming);
+    mFrameEventHistory.initializeCompositorTiming(compositorTiming);
 }
 
 void Layer::onFirstRef() {
     // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer);
-    mProducer = new MonitoredProducer(producer, mFlinger);
-    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName,
-            this);
+    BufferQueue::createBufferQueue(&producer, &consumer, 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 +223,8 @@
     // Add this buffer from our internal queue tracker
     { // Autolock scope
         Mutex::Autolock lock(mQueueItemLock);
-
+        mFlinger->mInterceptor.saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+                item.mGraphicBuffer->getHeight(), item.mFrameNumber);
         // Reset the frame number tracker when we receive the first buffer after
         // a frame number reset
         if (item.mFrameNumber == 1) {
@@ -264,6 +276,16 @@
     }
 }
 
+void Layer::onBuffersReleased() {
+#ifdef USE_HWC2
+    Mutex::Autolock lock(mHwcBufferCacheMutex);
+
+    for (auto info : mHwcBufferCaches) {
+        info.second.clear();
+    }
+#endif
+}
+
 void Layer::onSidebandStreamChanged() {
     if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) {
         // mSidebandStreamChanged was false
@@ -276,6 +298,9 @@
 // it's removed from the drawing state list)
 void Layer::onRemoved() {
     mSurfaceFlingerConsumer->abandon();
+    for (const auto& child : mCurrentChildren) {
+        child->onRemoved();
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -312,22 +337,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 +383,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 +429,27 @@
     if (!s.crop.isEmpty()) {
         win.intersect(s.crop, &win);
     }
+
+    Rect bounds = win;
+    const auto& p = getParent();
+    if (p != nullptr) {
+        // Look in computeScreenBounds recursive call for explanation of
+        // why we pass false here.
+        bounds = p->computeScreenBounds(false /* reduceTransparentRegion */);
+    }
+
+    Transform t = getTransform();
+    if (p != nullptr) {
+        win = t.transform(win);
+        win.intersect(bounds, &win);
+        win = t.inverse().transform(win);
+    }
+
     // subtract the transparent region and snap to the bounds
     return reduce(win, activeTransparentRegion);
 }
 
-FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
-    // the content crop is the area of the content that gets scaled to the
-    // layer's size.
-    FloatRect crop(getContentCrop());
-
+Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const {
     // the crop is the area of the window that gets cropped, but not
     // scaled in any ways.
     const State& s(getDrawingState());
@@ -411,7 +466,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 +476,27 @@
             activeCrop.clear();
         }
     }
-    activeCrop = s.active.transform.inverse().transform(activeCrop);
+    return activeCrop;
+}
+
+FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const {
+    // the content crop is the area of the content that gets scaled to the
+    // layer's size. This is in buffer space.
+    FloatRect crop = getContentCrop().toFloatRect();
+
+    // In addition there is a WM-specified crop we pull from our drawing state.
+    const State& s(getDrawingState());
+
+    // Screen space to make reduction to parent crop clearer.
+    Rect activeCrop = computeInitialCrop(hw);
+    const auto& p = getParent();
+    if (p != nullptr) {
+        auto parentCrop = p->computeInitialCrop(hw);
+        activeCrop.intersect(parentCrop, &activeCrop);
+    }
+    Transform t = getTransform();
+    // Back to layer space to work with the content crop.
+    activeCrop = t.inverse().transform(activeCrop);
 
     // This needs to be here as transform.transform(Rect) computes the
     // transformed rect and then takes the bounding box of the result before
@@ -494,7 +570,7 @@
 }
 
 #ifdef USE_HWC2
-void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice)
+void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z)
 #else
 void Layer::setGeometry(
     const sp<const DisplayDevice>& hw,
@@ -528,14 +604,15 @@
     // this gives us only the "orientation" component of the transform
     const State& s(getDrawingState());
 #ifdef USE_HWC2
+    auto blendMode = HWC2::BlendMode::None;
     if (!isOpaque(s) || s.alpha != 1.0f) {
-        auto blendMode = mPremultipliedAlpha ?
+        blendMode = mPremultipliedAlpha ?
                 HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
-        auto error = hwcLayer->setBlendMode(blendMode);
-        ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set blend mode %s:"
-                " %s (%d)", mName.string(), to_string(blendMode).c_str(),
-                to_string(error).c_str(), static_cast<int32_t>(error));
     }
+    auto error = hwcLayer->setBlendMode(blendMode);
+    ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set blend mode %s:"
+             " %s (%d)", mName.string(), to_string(blendMode).c_str(),
+             to_string(error).c_str(), static_cast<int32_t>(error));
 #else
     if (!isOpaque(s) || s.alpha != 0xFF) {
         layer.setBlending(mPremultipliedAlpha ?
@@ -547,9 +624,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 +635,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 +654,8 @@
         activeTransparentRegion.orSelf(Rect(activeCrop.right, activeCrop.top,
                 s.active.w, activeCrop.bottom));
     }
-    Rect frame(s.active.transform.transform(computeBounds(activeTransparentRegion)));
+
+    Rect frame(t.transform(computeBounds(activeTransparentRegion)));
     if (!s.finalCrop.isEmpty()) {
         if(!frame.intersect(s.finalCrop, &frame)) {
             frame.clear();
@@ -588,7 +667,7 @@
     }
     const Transform& tr(displayDevice->getTransform());
     Rect transformedFrame = tr.transform(frame);
-    auto error = hwcLayer->setDisplayFrame(transformedFrame);
+    error = hwcLayer->setDisplayFrame(transformedFrame);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
                 mName.string(), transformedFrame.left, transformedFrame.top,
@@ -614,10 +693,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 +720,7 @@
      */
 
     const Transform bufferOrientation(mCurrentTransform);
-    Transform transform(tr * s.active.transform * bufferOrientation);
+    Transform transform(tr * t * bufferOrientation);
 
     if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
         /*
@@ -764,8 +847,30 @@
         setCompositionType(hwcId, HWC2::Composition::Device);
     }
 
+    ALOGV("setPerFrameData: dataspace = %d", mCurrentState.dataSpace);
+    error = hwcLayer->setDataspace(mCurrentState.dataSpace);
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(),
+              mCurrentState.dataSpace, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    uint32_t hwcSlot = 0;
+    buffer_handle_t hwcHandle = nullptr;
+    {
+        Mutex::Autolock lock(mHwcBufferCacheMutex);
+
+        auto& hwcBufferCache = mHwcBufferCaches[hwcId];
+        sp<GraphicBuffer> hwcBuffer;
+        hwcBufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer,
+                &hwcSlot, &hwcBuffer);
+        if (hwcBuffer != nullptr) {
+            hwcHandle = hwcBuffer->handle;
+        }
+    }
+
     auto acquireFence = mSurfaceFlingerConsumer->getCurrentFence();
-    error = hwcLayer->setBuffer(mActiveBuffer->handle, acquireFence);
+    error = hwcLayer->setBuffer(hwcSlot, hwcHandle, acquireFence);
     if (error != HWC2::Error::None) {
         ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
                 mActiveBuffer->handle, to_string(error).c_str(),
@@ -815,7 +920,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 +969,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 +1012,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 +1089,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 +1105,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 +1131,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 +1307,8 @@
     switch (format) {
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_BGRA_8888:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBA_1010102:
             return false;
     }
     // in all other case, we have no blending (also for unknown formats)
@@ -1231,25 +1338,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 +1363,10 @@
     }
 
     Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
-    position[0] = tr.transform(lt);
-    position[1] = tr.transform(lb);
-    position[2] = tr.transform(rb);
-    position[3] = tr.transform(rt);
+    position[0] = hwTransform.transform(lt);
+    position[1] = hwTransform.transform(lb);
+    position[2] = hwTransform.transform(rb);
+    position[3] = hwTransform.transform(rt);
     for (size_t i=0 ; i<4 ; i++) {
         position[i].y = hw_h - position[i].y;
     }
@@ -1334,29 +1437,23 @@
 
     // If this transaction is waiting on the receipt of a frame, generate a sync
     // point and send it to the remote layer.
-    if (mCurrentState.handle != nullptr) {
-        sp<IBinder> strongBinder = mCurrentState.handle.promote();
-        sp<Handle> handle = nullptr;
-        sp<Layer> handleLayer = nullptr;
-        if (strongBinder != nullptr) {
-            handle = static_cast<Handle*>(strongBinder.get());
-            handleLayer = handle->owner.promote();
-        }
-        if (strongBinder == nullptr || handleLayer == nullptr) {
-            ALOGE("[%s] Unable to promote Layer handle", mName.string());
+    if (mCurrentState.barrierLayer != nullptr) {
+        sp<Layer> barrierLayer = mCurrentState.barrierLayer.promote();
+        if (barrierLayer == nullptr) {
+            ALOGE("[%s] Unable to promote barrier Layer.", mName.string());
             // If we can't promote the layer we are intended to wait on,
             // then it is expired or otherwise invalid. Allow this transaction
             // to be applied as per normal (no synchronization).
-            mCurrentState.handle = nullptr;
+            mCurrentState.barrierLayer = nullptr;
         } else {
             auto syncPoint = std::make_shared<SyncPoint>(
                     mCurrentState.frameNumber);
-            if (handleLayer->addSyncPoint(syncPoint)) {
+            if (barrierLayer->addSyncPoint(syncPoint)) {
                 mRemoteSyncPoints.push_back(std::move(syncPoint));
             } else {
                 // We already missed the frame we're supposed to synchronize
                 // on, so go ahead and apply the state update
-                mCurrentState.handle = nullptr;
+                mCurrentState.barrierLayer = nullptr;
             }
         }
 
@@ -1379,7 +1476,7 @@
 bool Layer::applyPendingStates(State* stateToCommit) {
     bool stateUpdateAvailable = false;
     while (!mPendingStates.empty()) {
-        if (mPendingStates[0].handle != nullptr) {
+        if (mPendingStates[0].barrierLayer != nullptr) {
             if (mRemoteSyncPoints.empty()) {
                 // If we don't have a sync point for this, apply it anyway. It
                 // will be visually wrong, but it should keep us from getting
@@ -1545,11 +1642,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 +1681,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 +1702,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;
@@ -1622,7 +1728,7 @@
 bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
     mCurrentState.sequence++;
     mCurrentState.requested.transform.set(
-            matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy);
+            matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1675,6 +1781,13 @@
     return true;
 }
 
+void Layer::setInfo(uint32_t type, uint32_t appId) {
+  mCurrentState.appId = appId;
+  mCurrentState.type = type;
+  mCurrentState.modified = true;
+  setTransactionFlags(eTransactionNeeded);
+}
+
 uint32_t Layer::getEffectiveScalingMode() const {
     if (mOverrideScalingMode >= 0) {
       return mOverrideScalingMode;
@@ -1692,17 +1805,42 @@
     return true;
 }
 
-void Layer::deferTransactionUntil(const sp<IBinder>& handle,
+bool Layer::setDataSpace(android_dataspace dataSpace) {
+    if (mCurrentState.dataSpace == dataSpace)
+        return false;
+    mCurrentState.sequence++;
+    mCurrentState.dataSpace = dataSpace;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+uint32_t Layer::getLayerStack() const {
+    auto p = getParent();
+    if (p == nullptr) {
+        return getDrawingState().layerStack;
+    }
+    return p->getLayerStack();
+}
+
+void Layer::deferTransactionUntil(const sp<Layer>& barrierLayer,
         uint64_t frameNumber) {
-    mCurrentState.handle = handle;
+    mCurrentState.barrierLayer = barrierLayer;
     mCurrentState.frameNumber = frameNumber;
     // We don't set eTransactionNeeded, because just receiving a deferral
     // request without any other state updates shouldn't actually induce a delay
     mCurrentState.modified = true;
     pushPendingState();
-    mCurrentState.handle = nullptr;
+    mCurrentState.barrierLayer = nullptr;
     mCurrentState.frameNumber = 0;
     mCurrentState.modified = false;
+    ALOGE("Deferred transaction");
+}
+
+void Layer::deferTransactionUntil(const sp<IBinder>& barrierHandle,
+        uint64_t frameNumber) {
+    sp<Handle> handle = static_cast<Handle*>(barrierHandle.get());
+    deferTransactionUntil(handle->owner.promote(), frameNumber);
 }
 
 void Layer::useSurfaceDamage() {
@@ -1744,65 +1882,136 @@
     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,
+        const CompositorTiming& compositorTiming) {
+    mAcquireTimeline.updateSignalTimes();
+    mReleaseTimeline.updateSignalTimes();
 
-        sp<Fence> frameReadyFence = mSurfaceFlingerConsumer->getCurrentFence();
-        if (frameReadyFence->isValid()) {
-            mFrameTracker.setFrameReadyFence(frameReadyFence);
-        } else {
-            // There was no fence for this frame, so assume that it was ready
-            // to be presented at the desired present time.
-            mFrameTracker.setFrameReadyTime(desiredPresentTime);
+    // mFrameLatencyNeeded is true when a new frame was latched for the
+    // composition.
+    if (!mFrameLatencyNeeded)
+        return false;
+
+    // Update mFrameEventHistory.
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addPostComposition(mCurrentFrameNumber,
+                glDoneFence, presentFence, compositorTiming);
+        if (mPreviousFrameNumber != 0) {
+            mFrameEventHistory.addRetire(mPreviousFrameNumber,
+                    retireFence);
         }
-
-        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;
     }
-    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() {
-    mSurfaceFlingerConsumer->releasePendingBuffer();
+void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+    if (!mSurfaceFlingerConsumer->releasePendingBuffer()) {
+        return;
+    }
+
+    auto releaseFenceTime = std::make_shared<FenceTime>(
+            mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+    mReleaseTimeline.push(releaseFenceTime);
+
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    if (mPreviousFrameNumber != 0) {
+        mFrameEventHistory.addRelease(mPreviousFrameNumber,
+                dequeueReadyTime, std::move(releaseFenceTime));
+    }
 }
 #endif
 
+bool Layer::isHiddenByPolicy() const {
+    const Layer::State& s(mDrawingState);
+    const auto& parent = getParent();
+    if (parent != nullptr && parent->isHiddenByPolicy()) {
+        return true;
+    }
+    return s.flags & layer_state_t::eLayerHidden;
+}
+
 bool Layer::isVisible() const {
     const Layer::State& s(mDrawingState);
 #ifdef USE_HWC2
-    return !(s.flags & layer_state_t::eLayerHidden) && s.alpha > 0.0f
+    return !(isHiddenByPolicy()) && 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 +2025,200 @@
         recomputeVisibleRegions = true;
 
         const State& s(getDrawingState());
-        return s.active.transform.transform(Region(Rect(s.active.w, s.active.h)));
+        return getTransform().transform(Region(Rect(s.active.w, s.active.h)));
     }
 
     Region outDirtyRegion;
-    if (mQueuedFrames > 0 || mAutoRefresh) {
-
-        // if we've already called updateTexImage() without going through
-        // a composition step, we have to skip this layer at this point
-        // because we cannot call updateTeximage() without a corresponding
-        // compositionComplete() call.
-        // we'll trigger an update in onPreComposition().
-        if (mRefreshPending) {
-            return outDirtyRegion;
-        }
-
-        // If the head buffer's acquire fence hasn't signaled yet, return and
-        // try again later
-        if (!headFenceHasSignaled()) {
-            mFlinger->signalLayerUpdate();
-            return outDirtyRegion;
-        }
-
-        // Capture the old state of the layer for comparisons later
-        const State& s(getDrawingState());
-        const bool oldOpacity = isOpaque(s);
-        sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
-
-        struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
-            Layer::State& front;
-            Layer::State& current;
-            bool& recomputeVisibleRegions;
-            bool stickyTransformSet;
-            const char* name;
-            int32_t overrideScalingMode;
-            bool& freezePositionUpdates;
-
-            Reject(Layer::State& front, Layer::State& current,
-                    bool& recomputeVisibleRegions, bool stickySet,
-                    const char* name,
-                    int32_t overrideScalingMode,
-                    bool& freezePositionUpdates)
-                : front(front), current(current),
-                  recomputeVisibleRegions(recomputeVisibleRegions),
-                  stickyTransformSet(stickySet),
-                  name(name),
-                  overrideScalingMode(overrideScalingMode),
-                  freezePositionUpdates(freezePositionUpdates) {
-            }
-
-            virtual bool reject(const sp<GraphicBuffer>& buf,
-                    const BufferItem& item) {
-                if (buf == NULL) {
-                    return false;
-                }
-
-                uint32_t bufWidth  = buf->getWidth();
-                uint32_t bufHeight = buf->getHeight();
-
-                // check that we received a buffer of the right size
-                // (Take the buffer's orientation into account)
-                if (item.mTransform & Transform::ROT_90) {
-                    swap(bufWidth, bufHeight);
-                }
-
-                int actualScalingMode = overrideScalingMode >= 0 ?
-                        overrideScalingMode : item.mScalingMode;
-                bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
-                if (front.active != front.requested) {
-
-                    if (isFixedSize ||
-                            (bufWidth == front.requested.w &&
-                             bufHeight == front.requested.h))
-                    {
-                        // Here we pretend the transaction happened by updating the
-                        // current and drawing states. Drawing state is only accessed
-                        // in this thread, no need to have it locked
-                        front.active = front.requested;
-
-                        // We also need to update the current state so that
-                        // we don't end-up overwriting the drawing state with
-                        // this stale current state during the next transaction
-                        //
-                        // NOTE: We don't need to hold the transaction lock here
-                        // because State::active is only accessed from this thread.
-                        current.active = front.active;
-                        current.modified = true;
-
-                        // recompute visible region
-                        recomputeVisibleRegions = true;
-                    }
-
-                    ALOGD_IF(DEBUG_RESIZE,
-                            "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
-                            "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
-                            "            requested={ wh={%4u,%4u} }}\n",
-                            name,
-                            bufWidth, bufHeight, item.mTransform, item.mScalingMode,
-                            front.active.w, front.active.h,
-                            front.crop.left,
-                            front.crop.top,
-                            front.crop.right,
-                            front.crop.bottom,
-                            front.crop.getWidth(),
-                            front.crop.getHeight(),
-                            front.requested.w, front.requested.h);
-                }
-
-                if (!isFixedSize && !stickyTransformSet) {
-                    if (front.active.w != bufWidth ||
-                        front.active.h != bufHeight) {
-                        // reject this buffer
-                        ALOGE("[%s] rejecting buffer: "
-                                "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
-                                name, bufWidth, bufHeight, front.active.w, front.active.h);
-                        return true;
-                    }
-                }
-
-                // if the transparent region has changed (this test is
-                // conservative, but that's fine, worst case we're doing
-                // a bit of extra work), we latch the new one and we
-                // trigger a visible-region recompute.
-                if (!front.activeTransparentRegion.isTriviallyEqual(
-                        front.requestedTransparentRegion)) {
-                    front.activeTransparentRegion = front.requestedTransparentRegion;
-
-                    // We also need to update the current state so that
-                    // we don't end-up overwriting the drawing state with
-                    // this stale current state during the next transaction
-                    //
-                    // NOTE: We don't need to hold the transaction lock here
-                    // because State::active is only accessed from this thread.
-                    current.activeTransparentRegion = front.activeTransparentRegion;
-
-                    // recompute visible region
-                    recomputeVisibleRegions = true;
-                }
-
-                if (front.crop != front.requestedCrop) {
-                    front.crop = front.requestedCrop;
-                    current.crop = front.requestedCrop;
-                    recomputeVisibleRegions = true;
-                }
-                freezePositionUpdates = false;
-
-                return false;
-            }
-        };
-
-        Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
-                getProducerStickyTransform() != 0, mName.string(),
-                mOverrideScalingMode, mFreezePositionUpdates);
-
-
-        // Check all of our local sync points to ensure that all transactions
-        // which need to have been applied prior to the frame which is about to
-        // be latched have signaled
-
-        auto headFrameNumber = getHeadFrameNumber();
-        bool matchingFramesFound = false;
-        bool allTransactionsApplied = true;
-        {
-            Mutex::Autolock lock(mLocalSyncPointMutex);
-            for (auto& point : mLocalSyncPoints) {
-                if (point->getFrameNumber() > headFrameNumber) {
-                    break;
-                }
-
-                matchingFramesFound = true;
-
-                if (!point->frameIsAvailable()) {
-                    // We haven't notified the remote layer that the frame for
-                    // this point is available yet. Notify it now, and then
-                    // abort this attempt to latch.
-                    point->setFrameAvailable();
-                    allTransactionsApplied = false;
-                    break;
-                }
-
-                allTransactionsApplied &= point->transactionIsApplied();
-            }
-        }
-
-        if (matchingFramesFound && !allTransactionsApplied) {
-            mFlinger->signalLayerUpdate();
-            return outDirtyRegion;
-        }
-
-        // This boolean is used to make sure that SurfaceFlinger's shadow copy
-        // of the buffer queue isn't modified when the buffer queue is returning
-        // BufferItem's that weren't actually queued. This can happen in shared
-        // buffer mode.
-        bool queuedBuffer = false;
-        status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
-                mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
-                mLastFrameNumberReceived);
-        if (updateResult == BufferQueue::PRESENT_LATER) {
-            // Producer doesn't want buffer to be displayed yet.  Signal a
-            // layer update so we check again at the next opportunity.
-            mFlinger->signalLayerUpdate();
-            return outDirtyRegion;
-        } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
-            // If the buffer has been rejected, remove it from the shadow queue
-            // and return early
-            if (queuedBuffer) {
-                Mutex::Autolock lock(mQueueItemLock);
-                mQueueItems.removeAt(0);
-                android_atomic_dec(&mQueuedFrames);
-            }
-            return outDirtyRegion;
-        } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
-            // This can occur if something goes wrong when trying to create the
-            // EGLImage for this buffer. If this happens, the buffer has already
-            // been released, so we need to clean up the queue and bug out
-            // early.
-            if (queuedBuffer) {
-                Mutex::Autolock lock(mQueueItemLock);
-                mQueueItems.clear();
-                android_atomic_and(0, &mQueuedFrames);
-            }
-
-            // Once we have hit this state, the shadow queue may no longer
-            // correctly reflect the incoming BufferQueue's contents, so even if
-            // updateTexImage starts working, the only safe course of action is
-            // to continue to ignore updates.
-            mUpdateTexImageFailed = true;
-
-            return outDirtyRegion;
-        }
-
-        if (queuedBuffer) {
-            // Autolock scope
-            auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
-            Mutex::Autolock lock(mQueueItemLock);
-
-            // Remove any stale buffers that have been dropped during
-            // updateTexImage
-            while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
-                mQueueItems.removeAt(0);
-                android_atomic_dec(&mQueuedFrames);
-            }
-
-            mQueueItems.removeAt(0);
-        }
-
-
-        // Decrement the queued-frames count.  Signal another event if we
-        // have more frames pending.
-        if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
-                || mAutoRefresh) {
-            mFlinger->signalLayerUpdate();
-        }
-
-        if (updateResult != NO_ERROR) {
-            // something happened!
-            recomputeVisibleRegions = true;
-            return outDirtyRegion;
-        }
-
-        // update the active buffer
-        mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
-        if (mActiveBuffer == NULL) {
-            // this can only happen if the very first buffer was rejected.
-            return outDirtyRegion;
-        }
-
-        mRefreshPending = true;
-        mFrameLatencyNeeded = true;
-        if (oldActiveBuffer == NULL) {
-             // the first time we receive a buffer, we need to trigger a
-             // geometry invalidation.
-            recomputeVisibleRegions = true;
-         }
-
-        Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
-        const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
-        const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
-        if ((crop != mCurrentCrop) ||
-            (transform != mCurrentTransform) ||
-            (scalingMode != mCurrentScalingMode))
-        {
-            mCurrentCrop = crop;
-            mCurrentTransform = transform;
-            mCurrentScalingMode = scalingMode;
-            recomputeVisibleRegions = true;
-        }
-
-        if (oldActiveBuffer != NULL) {
-            uint32_t bufWidth  = mActiveBuffer->getWidth();
-            uint32_t bufHeight = mActiveBuffer->getHeight();
-            if (bufWidth != uint32_t(oldActiveBuffer->width) ||
-                bufHeight != uint32_t(oldActiveBuffer->height)) {
-                recomputeVisibleRegions = true;
-            }
-        }
-
-        mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
-        if (oldOpacity != isOpaque(s)) {
-            recomputeVisibleRegions = true;
-        }
-
-        mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
-
-        // Remove any sync points corresponding to the buffer which was just
-        // latched
-        {
-            Mutex::Autolock lock(mLocalSyncPointMutex);
-            auto point = mLocalSyncPoints.begin();
-            while (point != mLocalSyncPoints.end()) {
-                if (!(*point)->frameIsAvailable() ||
-                        !(*point)->transactionIsApplied()) {
-                    // This sync point must have been added since we started
-                    // latching. Don't drop it yet.
-                    ++point;
-                    continue;
-                }
-
-                if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
-                    point = mLocalSyncPoints.erase(point);
-                } else {
-                    ++point;
-                }
-            }
-        }
-
-        // FIXME: postedRegion should be dirty & bounds
-        Region dirtyRegion(Rect(s.active.w, s.active.h));
-
-        // transform the dirty region to window-manager space
-        outDirtyRegion = (s.active.transform.transform(dirtyRegion));
+    if (mQueuedFrames <= 0 && !mAutoRefresh) {
+        return outDirtyRegion;
     }
+
+    // if we've already called updateTexImage() without going through
+    // a composition step, we have to skip this layer at this point
+    // because we cannot call updateTeximage() without a corresponding
+    // compositionComplete() call.
+    // we'll trigger an update in onPreComposition().
+    if (mRefreshPending) {
+        return outDirtyRegion;
+    }
+
+    // If the head buffer's acquire fence hasn't signaled yet, return and
+    // try again later
+    if (!headFenceHasSignaled()) {
+        mFlinger->signalLayerUpdate();
+        return outDirtyRegion;
+    }
+
+    // Capture the old state of the layer for comparisons later
+    const State& s(getDrawingState());
+    const bool oldOpacity = isOpaque(s);
+    sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
+
+    if (!allTransactionsSignaled()) {
+        mFlinger->signalLayerUpdate();
+        return outDirtyRegion;
+    }
+
+    // This boolean is used to make sure that SurfaceFlinger's shadow copy
+    // of the buffer queue isn't modified when the buffer queue is returning
+    // BufferItem's that weren't actually queued. This can happen in shared
+    // buffer mode.
+    bool queuedBuffer = false;
+    LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
+                    getProducerStickyTransform() != 0, mName.string(),
+                    mOverrideScalingMode, mFreezePositionUpdates);
+    status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
+            mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
+            mLastFrameNumberReceived);
+    if (updateResult == BufferQueue::PRESENT_LATER) {
+        // Producer doesn't want buffer to be displayed yet.  Signal a
+        // layer update so we check again at the next opportunity.
+        mFlinger->signalLayerUpdate();
+        return outDirtyRegion;
+    } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) {
+        // If the buffer has been rejected, remove it from the shadow queue
+        // and return early
+        if (queuedBuffer) {
+            Mutex::Autolock lock(mQueueItemLock);
+            mQueueItems.removeAt(0);
+            android_atomic_dec(&mQueuedFrames);
+        }
+        return outDirtyRegion;
+    } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
+        // This can occur if something goes wrong when trying to create the
+        // EGLImage for this buffer. If this happens, the buffer has already
+        // been released, so we need to clean up the queue and bug out
+        // early.
+        if (queuedBuffer) {
+            Mutex::Autolock lock(mQueueItemLock);
+            mQueueItems.clear();
+            android_atomic_and(0, &mQueuedFrames);
+        }
+
+        // Once we have hit this state, the shadow queue may no longer
+        // correctly reflect the incoming BufferQueue's contents, so even if
+        // updateTexImage starts working, the only safe course of action is
+        // to continue to ignore updates.
+        mUpdateTexImageFailed = true;
+
+        return outDirtyRegion;
+    }
+
+    if (queuedBuffer) {
+        // Autolock scope
+        auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+        Mutex::Autolock lock(mQueueItemLock);
+
+        // Remove any stale buffers that have been dropped during
+        // updateTexImage
+        while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
+            mQueueItems.removeAt(0);
+            android_atomic_dec(&mQueuedFrames);
+        }
+
+        mQueueItems.removeAt(0);
+    }
+
+
+    // Decrement the queued-frames count.  Signal another event if we
+    // have more frames pending.
+    if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
+            || mAutoRefresh) {
+        mFlinger->signalLayerUpdate();
+    }
+
+    // update the active buffer
+    mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer(
+            &mActiveBufferSlot);
+    if (mActiveBuffer == NULL) {
+        // this can only happen if the very first buffer was rejected.
+        return outDirtyRegion;
+    }
+
+    mBufferLatched = true;
+    mPreviousFrameNumber = mCurrentFrameNumber;
+    mCurrentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+#ifndef USE_HWC2
+        auto releaseFenceTime = std::make_shared<FenceTime>(
+                mSurfaceFlingerConsumer->getPrevFinalReleaseFence());
+        mReleaseTimeline.push(releaseFenceTime);
+        if (mPreviousFrameNumber != 0) {
+            mFrameEventHistory.addRelease(mPreviousFrameNumber,
+                    latchTime, std::move(releaseFenceTime));
+        }
+#endif
+    }
+
+    mRefreshPending = true;
+    mFrameLatencyNeeded = true;
+    if (oldActiveBuffer == NULL) {
+         // the first time we receive a buffer, we need to trigger a
+         // geometry invalidation.
+        recomputeVisibleRegions = true;
+     }
+
+    setDataSpace(mSurfaceFlingerConsumer->getCurrentDataSpace());
+
+    Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
+    const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
+    const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
+    if ((crop != mCurrentCrop) ||
+        (transform != mCurrentTransform) ||
+        (scalingMode != mCurrentScalingMode))
+    {
+        mCurrentCrop = crop;
+        mCurrentTransform = transform;
+        mCurrentScalingMode = scalingMode;
+        recomputeVisibleRegions = true;
+    }
+
+    if (oldActiveBuffer != NULL) {
+        uint32_t bufWidth  = mActiveBuffer->getWidth();
+        uint32_t bufHeight = mActiveBuffer->getHeight();
+        if (bufWidth != uint32_t(oldActiveBuffer->width) ||
+            bufHeight != uint32_t(oldActiveBuffer->height)) {
+            recomputeVisibleRegions = true;
+        }
+    }
+
+    mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
+    if (oldOpacity != isOpaque(s)) {
+        recomputeVisibleRegions = true;
+    }
+
+    // Remove any sync points corresponding to the buffer which was just
+    // latched
+    {
+        Mutex::Autolock lock(mLocalSyncPointMutex);
+        auto point = mLocalSyncPoints.begin();
+        while (point != mLocalSyncPoints.end()) {
+            if (!(*point)->frameIsAvailable() ||
+                    !(*point)->transactionIsApplied()) {
+                // This sync point must have been added since we started
+                // latching. Don't drop it yet.
+                ++point;
+                continue;
+            }
+
+            if ((*point)->getFrameNumber() <= mCurrentFrameNumber) {
+                point = mLocalSyncPoints.erase(point);
+            } else {
+                ++point;
+            }
+        }
+    }
+
+    // FIXME: postedRegion should be dirty & bounds
+    Region dirtyRegion(Rect(s.active.w, s.active.h));
+
+    // transform the dirty region to window-manager space
+    outDirtyRegion = (getTransform().transform(dirtyRegion));
+
     return outDirtyRegion;
 }
 
@@ -2209,7 +2280,9 @@
             "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
 #endif
             "      client=%p\n",
-            s.layerStack, s.z, s.active.transform.tx(), s.active.transform.ty(), s.active.w, s.active.h,
+            getLayerStack(), s.z,
+            s.active.transform.tx(), s.active.transform.ty(),
+            s.active.w, s.active.h,
             s.crop.left, s.crop.top,
             s.crop.right, s.crop.bottom,
             s.finalCrop.left, s.finalCrop.top,
@@ -2304,22 +2377,30 @@
     mFrameTracker.getStats(outStats);
 }
 
-void Layer::getFenceData(String8* outName, uint64_t* outFrameNumber,
-        bool* outIsGlesComposition, nsecs_t* outPostedTime,
-        sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const {
-    *outName = mName;
-    *outFrameNumber = mSurfaceFlingerConsumer->getFrameNumber();
+void Layer::dumpFrameEvents(String8& result) {
+    result.appendFormat("- Layer %s (%s, %p)\n",
+            getName().string(), getTypeId(), this);
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    mFrameEventHistory.checkFencesForCompletion();
+    mFrameEventHistory.dump(result);
+}
 
-#ifdef USE_HWC2
-    *outIsGlesComposition = mHwcLayers.count(HWC_DISPLAY_PRIMARY) ?
-            mHwcLayers.at(HWC_DISPLAY_PRIMARY).compositionType ==
-            HWC2::Composition::Client : true;
-#else
-    *outIsGlesComposition = mIsGlesComposition;
-#endif
-    *outPostedTime = mSurfaceFlingerConsumer->getTimestamp();
-    *outAcquireFence = mSurfaceFlingerConsumer->getCurrentFence();
-    *outPrevReleaseFence = mSurfaceFlingerConsumer->getPrevReleaseFence();
+void Layer::onDisconnect() {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    mFrameEventHistory.onDisconnect();
+}
+
+void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+        FrameEventHistoryDelta *outDelta) {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    if (newTimestamps) {
+        mAcquireTimeline.push(newTimestamps->acquireFence);
+        mFrameEventHistory.addQueue(*newTimestamps);
+    }
+
+    if (outDelta) {
+        mFrameEventHistory.getAndResetDelta(outDelta);
+    }
 }
 
 std::vector<OccupancyTracker::Segment> Layer::getOccupancyHistory(
@@ -2339,19 +2420,133 @@
     return mSurfaceFlingerConsumer->getTransformToDisplayInverse();
 }
 
-// ---------------------------------------------------------------------------
-
-Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
-        const sp<Layer>& layer)
-    : mFlinger(flinger), mLayer(layer) {
+void Layer::addChild(const sp<Layer>& layer) {
+    mCurrentChildren.add(layer);
+    layer->setParent(this);
 }
 
-Layer::LayerCleaner::~LayerCleaner() {
-    // destroy client resources
-    mFlinger->onLayerDestroyed(mLayer);
+ssize_t Layer::removeChild(const sp<Layer>& layer) {
+    layer->setParent(nullptr);
+    return mCurrentChildren.remove(layer);
+}
+
+bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
+    sp<Handle> handle = nullptr;
+    sp<Layer> newParent = nullptr;
+    if (newParentHandle == nullptr) {
+        return false;
+    }
+    handle = static_cast<Handle*>(newParentHandle.get());
+    newParent = handle->owner.promote();
+    if (newParent == nullptr) {
+        ALOGE("Unable to promote Layer handle");
+        return false;
+    }
+
+    for (const sp<Layer>& child : mCurrentChildren) {
+        newParent->addChild(child);
+
+        sp<Client> client(child->mClientRef.promote());
+        if (client != nullptr) {
+            client->setParentLayer(newParent);
+        }
+    }
+    mCurrentChildren.clear();
+
+    return true;
+}
+
+bool Layer::detachChildren() {
+    traverseInZOrder([this](Layer* child) {
+        if (child == this) {
+            return;
+        }
+
+        sp<Client> client(child->mClientRef.promote());
+        if (client != nullptr) {
+            client->detachLayer(child);
+        }
+    });
+
+    return true;
+}
+
+void Layer::setParent(const sp<Layer>& layer) {
+    mParent = layer;
+}
+
+void Layer::clearSyncPoints() {
+    for (const auto& child : mCurrentChildren) {
+        child->clearSyncPoints();
+    }
+
+    Mutex::Autolock lock(mLocalSyncPointMutex);
+    for (auto& point : mLocalSyncPoints) {
+        point->setFrameAvailable();
+    }
+    mLocalSyncPoints.clear();
+}
+
+int32_t Layer::getZ() const {
+    return mDrawingState.z;
+}
+
+/**
+ * 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..e2b2a3a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -40,13 +40,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,8 +108,15 @@
     struct State {
         Geometry active;
         Geometry requested;
-        uint32_t z;
+        int32_t z;
+
+        // The identifier of the layer stack this layer belongs to. A layer can
+        // only be associated to a single layer stack. A layer stack is a
+        // z-ordered group of layers which can be associated to one or more
+        // displays. Using the same layer stack on different displays is a way
+        // to achieve mirroring.
         uint32_t layerStack;
+
 #ifdef USE_HWC2
         float alpha;
 #else
@@ -121,14 +128,16 @@
         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
+        // If set, defers this state update until the identified Layer
         // receives a frame with the given frameNumber
-        wp<IBinder> handle;
+        wp<Layer> barrierLayer;
         uint64_t frameNumber;
 
         // the transparentRegion hint is a bit special, it's latched only
@@ -136,6 +145,10 @@
         // dependent.
         Region activeTransparentRegion;
         Region requestedTransparentRegion;
+        android_dataspace dataSpace;
+
+        uint32_t appId;
+        uint32_t type;
     };
 
     // -----------------------------------------------------------------------
@@ -150,7 +163,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 +176,14 @@
     bool setCrop(const Rect& crop, bool immediate);
     bool setFinalCrop(const Rect& crop);
     bool setLayerStack(uint32_t layerStack);
-    void deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
+    bool setDataSpace(android_dataspace dataSpace);
+    uint32_t getLayerStack() const;
+    void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber);
+    void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber);
     bool setOverrideScalingMode(int32_t overrideScalingMode);
+    void setInfo(uint32_t type, uint32_t appId);
+    bool reparentChildren(const sp<IBinder>& layer);
+    bool detachChildren();
 
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
@@ -180,11 +199,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 +233,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 +256,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 +297,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,
+            const CompositorTiming& compositorTiming);
 
 #ifdef USE_HWC2
     // If a buffer was replaced this frame, release the former buffer
-    void releasePendingBuffer();
+    void releasePendingBuffer(nsecs_t dequeueReadyTime);
 #endif
 
     /*
@@ -328,7 +353,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;}
 
@@ -358,13 +383,20 @@
 #ifdef USE_HWC2
     // -----------------------------------------------------------------------
 
+    void eraseHwcLayer(int32_t hwcId) {
+        mHwcLayers.erase(hwcId);
+
+        Mutex::Autolock lock(mHwcBufferCacheMutex);
+        mHwcBufferCaches.erase(hwcId);
+    }
+
     bool hasHwcLayer(int32_t hwcId) {
         if (mHwcLayers.count(hwcId) == 0) {
             return false;
         }
         if (mHwcLayers[hwcId].layer->isAbandoned()) {
             ALOGI("Erasing abandoned layer %s on %d", mName.string(), hwcId);
-            mHwcLayers.erase(hwcId);
+            eraseHwcLayer(hwcId);
             return false;
         }
         return true;
@@ -380,15 +412,22 @@
     void setHwcLayer(int32_t hwcId, std::shared_ptr<HWC2::Layer>&& layer) {
         if (layer) {
             mHwcLayers[hwcId].layer = layer;
+
+            Mutex::Autolock lock(mHwcBufferCacheMutex);
+            mHwcBufferCaches[hwcId] = HWComposerBufferCache();
         } else {
-            mHwcLayers.erase(hwcId);
+            eraseHwcLayer(hwcId);
         }
     }
 
+    void clearHwcLayers() {
+        mHwcLayers.clear();
+    }
+
 #endif
     // -----------------------------------------------------------------------
 
-    void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip) const;
+    void clearWithOpenGL(const sp<const DisplayDevice>& hw) const;
     void setFiltering(bool filtering);
     bool getFiltering() const;
 
@@ -407,29 +446,42 @@
     void miniDump(String8& result, int32_t hwcId) const;
 #endif
     void dumpFrameStats(String8& result) const;
+    void dumpFrameEvents(String8& result);
     void clearFrameStats();
     void logFrameStats();
     void getFrameStats(FrameStats* outStats) const;
 
-    void getFenceData(String8* outName, uint64_t* outFrameNumber,
-            bool* outIsGlesComposition, nsecs_t* outPostedTime,
-            sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const;
-
     std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush);
 
-    bool getFrameTimestamps(uint64_t frameNumber,
-            FrameTimestamps* outTimestamps) const {
-        return mFlinger->getFrameTimestamps(*this, frameNumber, outTimestamps);
-    }
+    void onDisconnect();
+    void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
+            FrameEventHistoryDelta* outDelta);
 
     bool getTransformToDisplayInverse() const;
 
+    Transform getTransform() const;
+
+    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,16 +490,28 @@
         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;
+    virtual void onBuffersReleased() override;
     virtual void onSidebandStreamChanged() override;
 
     void commitTransaction(const State& stateToCommit);
@@ -456,14 +520,19 @@
     bool needsFiltering(const sp<const DisplayDevice>& hw) const;
 
     uint32_t getEffectiveUsage(uint32_t usage) const;
+
     FloatRect computeCrop(const sp<const DisplayDevice>& hw) const;
+    // Compute the initial crop as specified by parent layers and the SurfaceControl
+    // for this layer. Does not include buffer crop from the IGraphicBufferProducer
+    // client, as that should not affect child clipping. Returns in screen space.
+    Rect computeInitialCrop(const sp<const DisplayDevice>& hw) const;
     bool isCropped() const;
     static bool getOpacityForFormat(uint32_t format);
 
     // drawing
-    void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip,
+    void clearWithOpenGL(const sp<const DisplayDevice>& hw,
             float r, float g, float b, float alpha) const;
-    void drawWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip,
+    void drawWithOpenGL(const sp<const DisplayDevice>& hw,
             bool useIdentityTransform) const;
 
     // Temporary - Used only for LEGACY camera mode.
@@ -472,6 +541,8 @@
     // Loads the corresponding system property once per process
     static bool latchUnsignaledBuffers();
 
+    void setParent(const sp<Layer>& layer);
+
     // -----------------------------------------------------------------------
 
     class SyncPoint
@@ -526,16 +597,42 @@
     void popPendingState(State* stateToCommit);
     bool applyPendingStates(State* stateToCommit);
 
+    void clearSyncPoints();
+
     // Returns mCurrentScaling mode (originating from the
     // Client) or mOverrideScalingMode mode (originating from
     // the Surface Controller) if set.
     uint32_t getEffectiveScalingMode() const;
 public:
+    /*
+     * The layer handle is just a BBinder object passed to the client
+     * (remote process) -- we don't keep any reference on our side such that
+     * the dtor is called when the remote side let go of its reference.
+     *
+     * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
+     * this layer when the handle is destroyed.
+     */
+    class Handle : public BBinder, public LayerCleaner {
+        public:
+            Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
+                : LayerCleaner(flinger, layer), owner(layer) {}
+
+            wp<Layer> owner;
+    };
+
+    sp<IBinder> getHandle();
+    sp<IGraphicBufferProducer> getProducer() const;
+    const String8& getName() const;
     void notifyAvailableFrames();
 private:
 
     // -----------------------------------------------------------------------
 
+    // Check all of the local sync points to ensure that all transactions
+    // which need to have been applied prior to the frame which is about to
+    // be latched have signaled
+    bool allTransactionsSignaled();
+
     // constants
     sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer;
     sp<IGraphicBufferProducer> mProducer;
@@ -556,9 +653,19 @@
     // thread-safe
     volatile int32_t mQueuedFrames;
     volatile int32_t mSidebandStreamChanged; // used like an atomic boolean
+
+    // Timestamp history for UIAutomation. Thread safe.
     FrameTracker mFrameTracker;
 
+    // Timestamp history for the consumer to query.
+    // Accessed by both consumer and producer on main and binder threads.
+    Mutex mFrameEventHistoryMutex;
+    ConsumerFrameEventHistory mFrameEventHistory;
+    FenceTimeline mAcquireTimeline;
+    FenceTimeline mReleaseTimeline;
+
     // main thread
+    int mActiveBufferSlot;
     sp<GraphicBuffer> mActiveBuffer;
     sp<NativeHandle> mSidebandStream;
     Rect mCurrentCrop;
@@ -567,7 +674,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
@@ -595,7 +704,18 @@
         Rect displayFrame;
         FloatRect sourceCrop;
     };
+
+    // A layer can be attached to multiple displays when operating in mirror mode
+    // (a.k.a: when several displays are attached with equal layerStack). In this
+    // case we need to keep track. In non-mirror mode, a layer will have only one.
+    // HWCInfo. This map key is a display layerStack.
     std::unordered_map<int32_t, HWCInfo> mHwcLayers;
+
+    // We need one HWComposerBufferCache for each HWC display.  We cannot have
+    // HWComposerBufferCache in HWCInfo because HWCInfo can only be accessed
+    // from the main thread.
+    Mutex mHwcBufferCacheMutex;
+    std::unordered_map<int32_t, HWComposerBufferCache> mHwcBufferCaches;
 #else
     bool mIsGlesComposition;
 #endif
@@ -617,10 +737,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..5ca7d39
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerRejecter.h"
+
+#include <gui/BufferItem.h>
+
+#include "clz.h"
+
+#define DEBUG_RESIZE 0
+
+namespace android {
+
+LayerRejecter::LayerRejecter(Layer::State& front,
+                             Layer::State& current,
+                             bool& recomputeVisibleRegions,
+                             bool stickySet,
+                             const char* name,
+                             int32_t overrideScalingMode,
+                             bool& freezePositionUpdates)
+  : mFront(front),
+    mCurrent(current),
+    mRecomputeVisibleRegions(recomputeVisibleRegions),
+    mStickyTransformSet(stickySet),
+    mName(name),
+    mOverrideScalingMode(overrideScalingMode),
+    mFreezePositionUpdates(freezePositionUpdates) {}
+
+bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
+    if (buf == NULL) {
+        return false;
+    }
+
+    uint32_t bufWidth = buf->getWidth();
+    uint32_t bufHeight = buf->getHeight();
+
+    // check that we received a buffer of the right size
+    // (Take the buffer's orientation into account)
+    if (item.mTransform & Transform::ROT_90) {
+        swap(bufWidth, bufHeight);
+    }
+
+    int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
+    bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+    if (mFront.active != mFront.requested) {
+        if (isFixedSize || (bufWidth == mFront.requested.w && bufHeight == mFront.requested.h)) {
+            // Here we pretend the transaction happened by updating the
+            // current and drawing states. Drawing state is only accessed
+            // in this thread, no need to have it locked
+            mFront.active = mFront.requested;
+
+            // We also need to update the current state so that
+            // we don't end-up overwriting the drawing state with
+            // this stale current state during the next transaction
+            //
+            // NOTE: We don't need to hold the transaction lock here
+            // because State::active is only accessed from this thread.
+            mCurrent.active = mFront.active;
+            mCurrent.modified = true;
+
+            // recompute visible region
+            mRecomputeVisibleRegions = true;
+        }
+
+        ALOGD_IF(DEBUG_RESIZE,
+                 "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
+                 "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) "
+                 "}\n"
+                 "            requested={ wh={%4u,%4u} }}\n",
+                 mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode, mFront.active.w,
+                 mFront.active.h, mFront.crop.left, mFront.crop.top, mFront.crop.right,
+                 mFront.crop.bottom, mFront.crop.getWidth(), mFront.crop.getHeight(),
+                 mFront.requested.w, mFront.requested.h);
+    }
+
+    if (!isFixedSize && !mStickyTransformSet) {
+        if (mFront.active.w != bufWidth || mFront.active.h != bufHeight) {
+            // reject this buffer
+            ALOGE("[%s] rejecting buffer: "
+                  "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
+                  mName, bufWidth, bufHeight, mFront.active.w, mFront.active.h);
+            return true;
+        }
+    }
+
+    // if the transparent region has changed (this test is
+    // conservative, but that's fine, worst case we're doing
+    // a bit of extra work), we latch the new one and we
+    // trigger a visible-region recompute.
+    if (!mFront.activeTransparentRegion.isTriviallyEqual(mFront.requestedTransparentRegion)) {
+        mFront.activeTransparentRegion = mFront.requestedTransparentRegion;
+
+        // We also need to update the current state so that
+        // we don't end-up overwriting the drawing state with
+        // this stale current state during the next transaction
+        //
+        // NOTE: We don't need to hold the transaction lock here
+        // because State::active is only accessed from this thread.
+        mCurrent.activeTransparentRegion = mFront.activeTransparentRegion;
+
+        // recompute visible region
+        mRecomputeVisibleRegions = true;
+    }
+
+    if (mFront.crop != mFront.requestedCrop) {
+        mFront.crop = mFront.requestedCrop;
+        mCurrent.crop = mFront.requestedCrop;
+        mRecomputeVisibleRegions = true;
+    }
+    mFreezePositionUpdates = false;
+
+    return false;
+}
+
+}  // namespace android
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
new file mode 100644
index 0000000..c2a9483
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_REJECTER_H
+#define ANDROID_LAYER_REJECTER_H
+
+#include "Layer.h"
+#include "SurfaceFlingerConsumer.h"
+
+namespace android {
+    class LayerRejecter : public SurfaceFlingerConsumer::BufferRejecter {
+    public:
+        LayerRejecter(Layer::State &front,
+                      Layer::State &current,
+                      bool &recomputeVisibleRegions,
+                      bool stickySet,
+                      const char *name,
+                      int32_t overrideScalingMode,
+                      bool &freezePositionUpdates);
+
+        virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item);
+
+    private:
+        Layer::State &mFront;
+        Layer::State &mCurrent;
+        bool &mRecomputeVisibleRegions;
+        bool mStickyTransformSet;
+        const char *mName;
+        int32_t mOverrideScalingMode;
+        bool &mFreezePositionUpdates;
+    };
+}  // namespace android
+
+#endif  // ANDROID_LAYER_REJECTER_H
\ No newline at end of file
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
new file mode 100644
index 0000000..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/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 974c7a3..debea58 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -25,7 +25,7 @@
 #include <utils/Log.h>
 
 #include <gui/IDisplayEventConnection.h>
-#include <gui/BitTube.h>
+#include <private/gui/BitTube.h>
 
 #include "MessageQueue.h"
 #include "EventThread.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/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 0259881..d19137b 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -23,7 +23,7 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-#include <ui/mat4.h>
+#include <math/mat4.h>
 #include <Transform.h>
 
 #define EGL_NO_CONFIG ((EGLConfig)0)
diff --git a/services/surfaceflinger/RenderEngine/Texture.h b/services/surfaceflinger/RenderEngine/Texture.h
index 8cf85fc..a07e0c3 100644
--- a/services/surfaceflinger/RenderEngine/Texture.h
+++ b/services/surfaceflinger/RenderEngine/Texture.h
@@ -15,7 +15,7 @@
  */
 
 #include <stdint.h>
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
 #ifndef SF_RENDER_ENGINE_TEXTURE_H
 #define SF_RENDER_ENGINE_TEXTURE_H
diff --git a/services/surfaceflinger/StartBootAnimThread.cpp b/services/surfaceflinger/StartBootAnimThread.cpp
new file mode 100644
index 0000000..c3f7296
--- /dev/null
+++ b/services/surfaceflinger/StartBootAnimThread.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/properties.h>
+#include "StartBootAnimThread.h"
+
+namespace android {
+
+StartBootAnimThread::StartBootAnimThread():
+        Thread(false) {
+}
+
+status_t StartBootAnimThread::Start() {
+    return run("SurfaceFlinger::StartBootAnimThread", PRIORITY_NORMAL);
+}
+
+bool StartBootAnimThread::threadLoop() {
+    property_set("service.bootanim.exit", "0");
+    property_set("ctl.start", "bootanim");
+    // Exit immediately
+    return false;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/StartBootAnimThread.h b/services/surfaceflinger/StartBootAnimThread.h
new file mode 100644
index 0000000..dba2bee
--- /dev/null
+++ b/services/surfaceflinger/StartBootAnimThread.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STARTBOOTANIMTHREAD_H
+#define ANDROID_STARTBOOTANIMTHREAD_H
+
+#include <stddef.h>
+
+#include <utils/Mutex.h>
+#include <utils/Thread.h>
+
+namespace android {
+
+class StartBootAnimThread : public Thread {
+// Boot animation is triggered via calls to "property_set()" which can block
+// if init's executing slow operation such as 'mount_all --late' (currently
+// happening 1/10th with fsck)  concurrently. Running in a separate thread
+// allows to pursue the SurfaceFlinger's init process without blocking.
+// see b/34499826.
+public:
+    StartBootAnimThread();
+    status_t Start();
+private:
+    virtual bool threadLoop();
+};
+
+}
+
+#endif // ANDROID_STARTBOOTANIMTHREAD_H
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index deeb456..46f5a1f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <errno.h>
 #include <math.h>
+#include <mutex>
 #include <dlfcn.h>
 #include <inttypes.h>
 #include <stdatomic.h>
@@ -32,13 +33,13 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
 #include <binder/PermissionCache.h>
 
+#include <dvr/vr_flinger.h>
+
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
 
-#include <gui/BitTube.h>
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
@@ -68,8 +69,11 @@
 #include "EventControlThread.h"
 #include "EventThread.h"
 #include "Layer.h"
+#include "LayerVector.h"
 #include "LayerDim.h"
+#include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
+#include "VrStateCallbacks.h"
 
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
@@ -80,6 +84,9 @@
 #include "RenderEngine/RenderEngine.h"
 #include <cutils/compiler.h>
 
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <configstore/Utils.h>
+
 #define DISPLAY_COUNT       1
 
 /*
@@ -92,6 +99,9 @@
 
 namespace android {
 
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
+
 // 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,10 +122,12 @@
 // 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 = getInt64<
+        ISurfaceFlingerConfigs,
+        &ISurfaceFlingerConfigs::vsyncEventPhaseOffsetNs>(1000000);
 
 // This is the phase offset at which SurfaceFlinger's composition runs.
-static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
+static constexpr int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS;
 
 // ---------------------------------------------------------------------------
 
@@ -132,13 +144,18 @@
         mTransactionPending(false),
         mAnimTransactionPending(false),
         mLayersRemoved(false),
+        mLayersAdded(false),
         mRepaintEverything(0),
-        mRenderEngine(NULL),
+        mHwc(nullptr),
+        mRealHwc(nullptr),
+        mVrHwc(nullptr),
+        mRenderEngine(nullptr),
         mBootTime(systemTime()),
         mBuiltinDisplays(),
         mVisibleRegionsDirty(false),
         mGeometryInvalid(false),
         mAnimCompositionPending(false),
+        mVrModeSupported(0),
         mDebugRegion(0),
         mDebugDDMS(0),
         mDebugDisableHWC(0),
@@ -149,6 +166,7 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
+        mInterceptor(this),
         mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
@@ -156,13 +174,21 @@
         mHasPoweredOff(false),
         mFrameBuckets(),
         mTotalTime(0),
-        mLastSwapTime(0)
+        mLastSwapTime(0),
+        mNumLayers(0)
+#ifdef USE_HWC2
+        ,mEnterVrMode(false)
+#endif
 {
     ALOGI("SurfaceFlinger is starting");
 
     // debugging stuff...
     char value[PROPERTY_VALUE_MAX];
 
+    // TODO (urbanus): remove once b/35319396 is fixed.
+    property_get("ro.boot.vr", value, "0");
+    mVrModeSupported = atoi(value);
+
     property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
     mGpuToCpuSupported = !atoi(value);
 
@@ -184,9 +210,13 @@
     mPropagateBackpressure = !atoi(value);
     ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
 
-    property_get("debug.sf.disable_hwc_vds", value, "0");
-    mUseHwcVirtualDisplays = !atoi(value);
-    ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+    property_get("debug.sf.enable_hwc_vds", value, "0");
+    mUseHwcVirtualDisplays = atoi(value);
+    ALOGI_IF(!mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
+
+    property_get("ro.sf.disable_triple_buffer", value, "1");
+    mLayerTripleBufferingDisabled = atoi(value);
+    ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -199,6 +229,14 @@
     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglTerminate(display);
+
+    if (mVrStateCallbacks.get()) {
+        sp<IVrManager> vrManagerService = interface_cast<IVrManager>(
+            defaultServiceManager()->checkService(String16("vrmanager")));
+        if (vrManagerService.get()) {
+            vrManagerService->unregisterListener(mVrStateCallbacks);
+        }
+    }
 }
 
 void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
@@ -212,15 +250,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 +287,7 @@
              flinger->setTransactionFlags(eDisplayTransactionNeeded);
          }
      public:
-        DisplayToken(const sp<SurfaceFlinger>& flinger)
+        explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
             : flinger(flinger) {
         }
     };
@@ -246,7 +298,7 @@
     DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
     info.displayName = displayName;
     mCurrentState.displays.add(token, info);
-
+    mInterceptor.saveDisplayCreation(info);
     return token;
 }
 
@@ -264,7 +316,7 @@
         ALOGE("destroyDisplay called for non-virtual display");
         return;
     }
-
+    mInterceptor.saveDisplayDeletion(info.displayId);
     mCurrentState.displays.removeItemsAt(idx);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -277,6 +329,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) {
@@ -295,6 +348,9 @@
 
 void SurfaceFlinger::bootFinished()
 {
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -315,6 +371,13 @@
     const int LOGTAG_SF_STOP_BOOTANIM = 60110;
     LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                    ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+
+    sp<IVrManager> vrManagerService = interface_cast<IVrManager>(
+        defaultServiceManager()->checkService(String16("vrmanager")));
+    if (vrManagerService.get()) {
+        mVrStateCallbacks = new VrStateCallbacks(*this);
+        vrManagerService->registerListener(mVrStateCallbacks);
+    }
 }
 
 void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
@@ -449,10 +512,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 +552,10 @@
         // start the EventThread
         sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
                 vsyncPhaseOffsetNs, true, "app");
-        mEventThread = new EventThread(vsyncSrc, *this);
+        mEventThread = new EventThread(vsyncSrc, *this, false);
         sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
                 sfVsyncPhaseOffsetNs, true, "sf");
-        mSFEventThread = new EventThread(sfVsyncSrc, *this);
+        mSFEventThread = new EventThread(sfVsyncSrc, *this, true);
         mEventQueue.setEventThread(mSFEventThread);
 
         // set SFEventThread to SCHED_FIFO to minimize jitter
@@ -484,7 +573,9 @@
     // Drop the state lock while we initialize the hardware composer. We drop
     // the lock because on creation, it will call back into SurfaceFlinger to
     // initialize the primary display.
-    mHwc = new HWComposer(this);
+    LOG_ALWAYS_FATAL_IF(mEnterVrMode, "Starting in vr mode is not currently supported.");
+    mRealHwc = new HWComposer(false);
+    mHwc = mRealHwc;
     mHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this));
 
     Mutex::Autolock _l(mStateLock);
@@ -510,16 +601,22 @@
 
     mRenderEngine->primeCache();
 
-    // start boot animation
-    startBootAnim();
+    mStartBootAnimThread = new StartBootAnimThread();
+    if (mStartBootAnimThread->Start() != NO_ERROR) {
+        ALOGE("Run StartBootAnimThread failed!");
+    }
 
     ALOGV("Done initializing");
 }
 
 void SurfaceFlinger::startBootAnim() {
-    // start boot animation
-    property_set("service.bootanim.exit", "0");
-    property_set("ctl.start", "bootanim");
+    // Start boot animation service by setting a property mailbox
+    // if property setting thread is already running, Start() will be just a NOP
+    mStartBootAnimThread->Start();
+    // Wait until property was set
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
 }
 
 size_t SurfaceFlinger::getMaxTextureSize() const {
@@ -535,10 +632,32 @@
 bool SurfaceFlinger::authenticateSurfaceTexture(
         const sp<IGraphicBufferProducer>& bufferProducer) const {
     Mutex::Autolock _l(mStateLock);
+    return authenticateSurfaceTextureLocked(bufferProducer);
+}
+
+bool SurfaceFlinger::authenticateSurfaceTextureLocked(
+        const sp<IGraphicBufferProducer>& bufferProducer) const {
     sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
     return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
 }
 
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+        std::vector<FrameEvent>* outSupported) const {
+    *outSupported = {
+        FrameEvent::REQUESTED_PRESENT,
+        FrameEvent::ACQUIRE,
+        FrameEvent::LATCH,
+        FrameEvent::FIRST_REFRESH_START,
+        FrameEvent::LAST_REFRESH_START,
+        FrameEvent::GPU_COMPOSITION_DONE,
+        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 +735,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 +969,40 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
+    if (enable == mInjectVSyncs) {
+        return NO_ERROR;
+    }
+
+    if (enable) {
+        mInjectVSyncs = enable;
+        ALOGV("VSync Injections enabled");
+        if (mVSyncInjector.get() == nullptr) {
+            mVSyncInjector = new InjectVSyncSource();
+            mInjectorEventThread = new EventThread(mVSyncInjector, *this, false);
+        }
+        mEventQueue.setEventThread(mInjectorEventThread);
+    } else {
+        mInjectVSyncs = enable;
+        ALOGV("VSync Injections disabled");
+        mEventQueue.setEventThread(mSFEventThread);
+        mVSyncInjector.clear();
+    }
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::injectVSync(nsecs_t when) {
+    if (!mInjectVSyncs) {
+        ALOGE("VSync Injections not enabled");
+        return BAD_VALUE;
+    }
+    if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
+        ALOGV("Injecting VSync inside SurfaceFlinger");
+        mVSyncInjector->onInjectSyncEvent(when);
+    }
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
@@ -949,7 +1102,14 @@
     }
 }
 
-void SurfaceFlinger::onVSyncReceived(int32_t type, nsecs_t timestamp) {
+void SurfaceFlinger::onVSyncReceived(HWComposer* composer, int32_t type,
+                                     nsecs_t timestamp) {
+    Mutex::Autolock lock(mStateLock);
+    // Ignore any vsyncs from the non-active hardware composer.
+    if (composer != mHwc) {
+        return;
+    }
+
     bool needsHwVsync = false;
 
     { // Scope for the lock
@@ -966,6 +1126,11 @@
     }
 }
 
+void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
+    std::lock_guard<std::mutex> lock(mCompositeTimingLock);
+    *compositorTiming = mCompositorTiming;
+}
+
 void SurfaceFlinger::onHotplugReceived(int32_t disp, bool connected) {
     ALOGV("onHotplugReceived(%d, %s)", disp, connected ? "true" : "false");
     if (disp == DisplayDevice::DISPLAY_PRIMARY) {
@@ -975,7 +1140,13 @@
         bool isSecure = true;
 
         int32_t type = DisplayDevice::DISPLAY_PRIMARY;
-        createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY);
+
+        // When we're using the vr composer, the assumption is that we've
+        // already created the IBinder object for the primary display.
+        if (!mHwc->isUsingVrComposer()) {
+            createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY);
+        }
+
         wp<IBinder> token = mBuiltinDisplays[type];
 
         sp<IGraphicBufferProducer> producer;
@@ -1004,21 +1175,110 @@
     }
 }
 
+void SurfaceFlinger::onInvalidateReceived(HWComposer* composer) {
+    Mutex::Autolock lock(mStateLock);
+    if (composer == mHwc) {
+        repaintEverything();
+    } else {
+        // This isn't from our current hardware composer. If it's a callback
+        // from the real composer, forward the refresh request to vr
+        // flinger. Otherwise ignore it.
+        if (!composer->isUsingVrComposer()) {
+            mVrFlinger->OnHardwareComposerRefresh();
+        }
+    }
+}
+
 void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) {
     ATRACE_CALL();
     getHwComposer().setVsyncEnabled(disp,
             enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
 }
 
+void SurfaceFlinger::clearHwcLayers(const LayerVector& layers) {
+    for (size_t i = 0; i < layers.size(); ++i) {
+        layers[i]->clearHwcLayers();
+    }
+}
+
+void SurfaceFlinger::resetHwc() {
+    disableHardwareVsync(true);
+    clearHwcLayers(mDrawingState.layersSortedByZ);
+    clearHwcLayers(mCurrentState.layersSortedByZ);
+    // Clear the drawing state so that the logic inside of
+    // handleTransactionLocked will fire. It will determine the delta between
+    // mCurrentState and mDrawingState and re-apply all changes when we make the
+    // transition.
+    mDrawingState.displays.clear();
+    mDisplays.clear();
+}
+
+void SurfaceFlinger::updateVrMode() {
+    bool enteringVrMode = mEnterVrMode;
+    if (enteringVrMode == mHwc->isUsingVrComposer()) {
+        return;
+    }
+    if (enteringVrMode && !mVrHwc) {
+        // Construct new HWComposer without holding any locks.
+        mVrHwc = new HWComposer(true);
+        ALOGV("Vr HWC created");
+    }
+    {
+        Mutex::Autolock _l(mStateLock);
+
+        if (enteringVrMode) {
+            // Start vrflinger thread, if it hasn't been started already.
+            if (!mVrFlinger) {
+                mVrFlinger = std::make_unique<dvr::VrFlinger>();
+                int err = mVrFlinger->Run(mHwc->getComposer());
+                if (err != NO_ERROR) {
+                    ALOGE("Failed to run vrflinger: %s (%d)", strerror(-err), err);
+                    mVrFlinger.reset();
+                    mEnterVrMode = false;
+                    return;
+                }
+            }
+
+            resetHwc();
+
+            mHwc = mVrHwc;
+            mVrFlinger->EnterVrMode();
+        } else {
+            mVrFlinger->ExitVrMode();
+
+            resetHwc();
+
+            mHwc = mRealHwc;
+            enableHardwareVsync();
+        }
+
+        mVisibleRegionsDirty = true;
+        invalidateHwcGeometry();
+        android_atomic_or(1, &mRepaintEverything);
+        setTransactionFlags(eDisplayTransactionNeeded);
+    }
+    if (mVrHwc) {
+        mVrHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this));
+    }
+}
+
 void SurfaceFlinger::onMessageReceived(int32_t what) {
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
+            // TODO(eieio): Tied to a conditional until SELinux issues
+            // are resolved.
+            if (mVrModeSupported) {
+                updateVrMode();
+            }
+
             bool frameMissed = !mHadClientComposition &&
                     mPreviousPresentFence != Fence::NO_FENCE &&
-                    mPreviousPresentFence->getSignalTime() == INT64_MAX;
+                    (mPreviousPresentFence->getSignalTime() ==
+                            Fence::SIGNAL_TIME_PENDING);
             ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
             if (mPropagateBackpressure && frameMissed) {
+                ALOGD("Backpressure trigger, skipping transaction & refresh!");
                 signalLayerUpdate();
                 break;
             }
@@ -1042,7 +1302,7 @@
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
-    uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+    uint32_t transactionFlags = peekTransactionFlags();
     if (transactionFlags) {
         handleTransaction(transactionFlags);
         return true;
@@ -1060,14 +1320,14 @@
 
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    preComposition();
+    preComposition(refreshStartTime);
     rebuildLayerStacks();
     setUpHWComposer();
     doDebugFlashRegions();
     doComposition();
     postComposition(refreshStartTime);
 
-    mPreviousPresentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+    mPreviousPresentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY);
 
     mHadClientComposition = false;
     for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
@@ -1076,10 +1336,6 @@
                 mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
     }
 
-    // Release any buffers which were replaced this frame
-    for (auto& layer : mLayersWithQueuedFrames) {
-        layer->releasePendingBuffer();
-    }
     mLayersWithQueuedFrames.clear();
 }
 
@@ -1127,64 +1383,151 @@
     }
 }
 
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
 {
     ATRACE_CALL();
     ALOGV("preComposition");
 
     bool needExtraInvalidate = false;
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        if (layers[i]->onPreComposition()) {
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        if (layer->onPreComposition(refreshStartTime)) {
             needExtraInvalidate = true;
         }
-    }
+    });
+
     if (needExtraInvalidate) {
         signalLayerUpdate();
     }
 }
 
+void SurfaceFlinger::updateCompositorTiming(
+        nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
+        std::shared_ptr<FenceTime>& presentFenceTime) {
+    // Update queue of past composite+present times and determine the
+    // most recently known composite to present latency.
+    mCompositePresentTimes.push({compositeTime, presentFenceTime});
+    nsecs_t compositeToPresentLatency = -1;
+    while (!mCompositePresentTimes.empty()) {
+        CompositePresentTime& cpt = mCompositePresentTimes.front();
+        // Cached values should have been updated before calling this method,
+        // which helps avoid duplicate syscalls.
+        nsecs_t displayTime = cpt.display->getCachedSignalTime();
+        if (displayTime == Fence::SIGNAL_TIME_PENDING) {
+            break;
+        }
+        compositeToPresentLatency = displayTime - cpt.composite;
+        mCompositePresentTimes.pop();
+    }
+
+    // Don't let mCompositePresentTimes grow unbounded, just in case.
+    while (mCompositePresentTimes.size() > 16) {
+        mCompositePresentTimes.pop();
+    }
+
+    // Integer division and modulo round toward 0 not -inf, so we need to
+    // treat negative and positive offsets differently.
+    nsecs_t idealLatency = (sfVsyncPhaseOffsetNs >= 0) ?
+            (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) :
+            ((-sfVsyncPhaseOffsetNs) % vsyncInterval);
+
+    // Snap the latency to a value that removes scheduling jitter from the
+    // composition and present times, which often have >1ms of jitter.
+    // Reducing jitter is important if an app attempts to extrapolate
+    // something (such as user input) to an accurate diasplay time.
+    // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs
+    // with (presentLatency % interval).
+    nsecs_t snappedCompositeToPresentLatency = -1;
+    if (compositeToPresentLatency >= 0) {
+        nsecs_t bias = vsyncInterval / 2;
+        int64_t extraVsyncs =
+                (compositeToPresentLatency - idealLatency + bias) /
+                vsyncInterval;
+        nsecs_t extraLatency = extraVsyncs * vsyncInterval;
+        snappedCompositeToPresentLatency = idealLatency + extraLatency;
+    }
+
+    std::lock_guard<std::mutex> lock(mCompositeTimingLock);
+    mCompositorTiming.deadline = vsyncPhase - idealLatency;
+    mCompositorTiming.interval = vsyncInterval;
+    if (snappedCompositeToPresentLatency >= 0) {
+        mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
+    }
+}
+
 void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
 {
     ATRACE_CALL();
     ALOGV("postComposition");
 
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        bool frameLatched = layers[i]->onPostComposition();
-        if (frameLatched) {
-            recordBufferingStats(layers[i]->getName().string(),
-                    layers[i]->getOccupancyHistory(false));
-        }
+    // Release any buffers which were replaced this frame
+    nsecs_t dequeueReadyTime = systemTime();
+    for (auto& layer : mLayersWithQueuedFrames) {
+        layer->releasePendingBuffer(dequeueReadyTime);
     }
 
-    sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+    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;
+    }
+
+    nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0);
+    nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod();
+
+    // We use the refreshStartTime which might be sampled a little later than
+    // when we started doing work for this frame, but that should be okay
+    // since updateCompositorTiming has snapping logic.
+    updateCompositorTiming(
+        vsyncPhase, vsyncInterval, refreshStartTime, displayFenceTime);
+
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime,
+                *presentFenceTime, *retireFenceTime, mCompositorTiming);
+        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.
@@ -1203,9 +1546,8 @@
     if (mHasPoweredOff) {
         mHasPoweredOff = false;
     } else {
-        nsecs_t period = mPrimaryDispSync.getPeriod();
         nsecs_t elapsedTime = currentTime - mLastSwapTime;
-        size_t numPeriods = static_cast<size_t>(elapsedTime / period);
+        size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncInterval);
         if (numPeriods < NUM_BUCKETS - 1) {
             mFrameBuckets[numPeriods] += elapsedTime;
         } else {
@@ -1226,7 +1568,6 @@
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        const LayerVector& layers(mDrawingState.layersSortedByZ);
         for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
             Region opaqueRegion;
             Region dirtyRegion;
@@ -1235,15 +1576,12 @@
             const Transform& tr(displayDevice->getTransform());
             const Rect bounds(displayDevice->getBounds());
             if (displayDevice->isDisplayOn()) {
-                SurfaceFlinger::computeVisibleRegions(layers,
+                computeVisibleRegions(
                         displayDevice->getLayerStack(), dirtyRegion,
                         opaqueRegion);
 
-                const size_t count = layers.size();
-                for (size_t i=0 ; i<count ; i++) {
-                    const sp<Layer>& layer(layers[i]);
-                    const Layer::State& s(layer->getDrawingState());
-                    if (s.layerStack == displayDevice->getLayerStack()) {
+                mDrawingState.traverseInZOrder([&](Layer* layer) {
+                    if (layer->getLayerStack() == displayDevice->getLayerStack()) {
                         Region drawRegion(tr.transform(
                                 layer->visibleNonTransparentRegion));
                         drawRegion.andSelf(bounds);
@@ -1255,8 +1593,14 @@
                             layer->setHwcLayer(displayDevice->getHwcDisplayId(),
                                     nullptr);
                         }
+                    } else {
+                        // WM changes displayDevice->layerStack upon sleep/awake.
+                        // Here we make sure we delete the HWC layers even if
+                        // WM changed their layer stack.
+                        layer->setHwcLayer(displayDevice->getHwcDisplayId(),
+                                nullptr);
                     }
-                }
+                });
             }
             displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
             displayDevice->undefinedRegion.set(bounds);
@@ -1309,20 +1653,19 @@
             if (hwcId >= 0) {
                 const Vector<sp<Layer>>& currentLayers(
                         displayDevice->getVisibleLayersSortedByZ());
-                bool foundLayerWithoutHwc = false;
-                for (auto& layer : currentLayers) {
+                for (size_t i = 0; i < currentLayers.size(); i++) {
+                    const auto& layer = currentLayers[i];
                     if (!layer->hasHwcLayer(hwcId)) {
                         auto hwcLayer = mHwc->createLayer(hwcId);
                         if (hwcLayer) {
                             layer->setHwcLayer(hwcId, std::move(hwcLayer));
                         } else {
                             layer->forceClientComposition(hwcId);
-                            foundLayerWithoutHwc = true;
                             continue;
                         }
                     }
 
-                    layer->setGeometry(displayDevice);
+                    layer->setGeometry(displayDevice, i);
                     if (mDebugDisableHWC || mDebugRegion) {
                         layer->forceClientComposition(hwcId);
                     }
@@ -1402,16 +1745,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 +1804,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 +1815,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;
-        }
+        });
     }
 
     /*
@@ -1628,16 +1961,10 @@
                                 "adding a supported display, but rendering "
                                 "surface is provided (%p), ignoring it",
                                 state.surface.get());
-                        if (state.type == DisplayDevice::DISPLAY_EXTERNAL) {
-                            hwcId = DisplayDevice::DISPLAY_EXTERNAL;
-                            dispSurface = new FramebufferSurface(*mHwc,
-                                    DisplayDevice::DISPLAY_EXTERNAL,
-                                    bqConsumer);
-                            producer = bqProducer;
-                        } else {
-                            ALOGE("Attempted to add non-external non-virtual"
-                                    " display");
-                        }
+
+                        hwcId = state.type;
+                        dispSurface = new FramebufferSurface(*mHwc, hwcId, bqConsumer);
+                        producer = bqProducer;
                     }
 
                     const wp<IBinder>& display(curr.keyAt(i));
@@ -1682,13 +2009,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 +2043,9 @@
                 disp = getDefaultDisplayDevice();
             }
             layer->updateTransformHint(disp);
-        }
+
+            first = false;
+        });
     }
 
 
@@ -1724,9 +2053,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 +2064,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 +2100,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 +2113,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 +2133,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 +2174,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 +2191,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 +2248,7 @@
         layer->setCoveredRegion(coveredRegion);
         layer->setVisibleNonTransparentRegion(
                 visibleRegion.subtract(transparentRegion));
-    }
+    });
 
     outOpaqueRegion = aboveOpaqueLayers;
 }
@@ -1942,10 +2267,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 +2281,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 +2319,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 +2338,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(
@@ -2151,7 +2475,7 @@
                                 && hasClientComposition) {
                             // never clear the very first layer since we're
                             // guaranteed the FB is already cleared
-                            layer->clearWithOpenGL(displayDevice, clip);
+                            layer->clearWithOpenGL(displayDevice);
                         }
                         break;
                     }
@@ -2187,8 +2511,8 @@
     return true;
 }
 
-void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& hw, const Region& region) const {
-    const int32_t height = hw->getHeight();
+void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const {
+    const int32_t height = displayDevice->getHeight();
     RenderEngine& engine(getRenderEngine());
     engine.fillRegionWithColor(region, height, 0, 0, 0, 0);
 }
@@ -2196,16 +2520,23 @@
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
         const sp<IBinder>& handle,
         const sp<IGraphicBufferProducer>& gbc,
-        const sp<Layer>& lbc)
+        const sp<Layer>& lbc,
+        const sp<Layer>& parent)
 {
     // add this layer to the current state list
     {
         Mutex::Autolock _l(mStateLock);
-        if (mCurrentState.layersSortedByZ.size() >= MAX_LAYERS) {
+        if (mNumLayers >= MAX_LAYERS) {
             return NO_MEMORY;
         }
-        mCurrentState.layersSortedByZ.add(lbc);
+        if (parent == nullptr) {
+            mCurrentState.layersSortedByZ.add(lbc);
+        } else {
+            parent->addChild(lbc);
+        }
         mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
+        mLayersAdded = true;
+        mNumLayers++;
     }
 
     // attach this layer to the client
@@ -2214,25 +2545,33 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) {
-    Mutex::Autolock _l(mStateLock);
-    sp<Layer> layer = weakLayer.promote();
-    if (layer == nullptr) {
-        // The layer has already been removed, carry on
+status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) {
+    const auto& p = layer->getParent();
+    const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
+        mCurrentState.layersSortedByZ.remove(layer);
+
+    // As a matter of normal operation, the LayerCleaner will produce a second
+    // attempt to remove the surface. The Layer will be kept alive in mDrawingState
+    // so we will succeed in promoting it, but it's already been removed
+    // from mCurrentState. As long as we can find it in mDrawingState we have no problem
+    // otherwise something has gone wrong and we are leaking the layer.
+    if (index < 0 && mDrawingState.layersSortedByZ.indexOf(layer) < 0) {
+        ALOGE("Failed to find layer (%s) in layer parent (%s).",
+                layer->getName().string(),
+                (p != nullptr) ? p->getName().string() : "no-parent");
+        return BAD_VALUE;
+    } else if (index < 0) {
         return NO_ERROR;
     }
 
-    ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
-    if (index >= 0) {
-        mLayersPendingRemoval.push(layer);
-        mLayersRemoved = true;
-        setTransactionFlags(eTransactionNeeded);
-        return NO_ERROR;
-    }
-    return status_t(index);
+    mLayersPendingRemoval.add(layer);
+    mLayersRemoved = true;
+    mNumLayers--;
+    setTransactionFlags(eTransactionNeeded);
+    return NO_ERROR;
 }
 
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
     return android_atomic_release_load(&mTransactionFlags);
 }
 
@@ -2308,6 +2647,10 @@
     }
 
     if (transactionFlags) {
+        if (mInterceptor.isEnabled()) {
+            mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+        }
+
         // this triggers the transaction
         setTransactionFlags(transactionFlags);
 
@@ -2399,13 +2742,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) {
@@ -2438,9 +2788,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)
@@ -2449,10 +2807,30 @@
             }
         }
         if (what & layer_state_t::eDeferTransaction) {
-            layer->deferTransactionUntil(s.handle, s.frameNumber);
+            if (s.barrierHandle != nullptr) {
+                layer->deferTransactionUntil(s.barrierHandle, s.frameNumber);
+            } else if (s.barrierGbp != nullptr) {
+                const sp<IGraphicBufferProducer>& gbp = s.barrierGbp;
+                if (authenticateSurfaceTextureLocked(gbp)) {
+                    const auto& otherLayer =
+                        (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
+                    layer->deferTransactionUntil(otherLayer, s.frameNumber);
+                } else {
+                    ALOGE("Attempt to defer transaction to to an"
+                            " unrecognized GraphicBufferProducer");
+                }
+            }
             // We don't trigger a traversal here because if no other state is
             // changed, we don't want this to cause any more work
         }
+        if (what & layer_state_t::eReparentChildren) {
+            if (layer->reparentChildren(s.reparentHandle)) {
+                flags |= eTransactionNeeded|eTraversalNeeded;
+            }
+        }
+        if (what & layer_state_t::eDetachChildren) {
+            layer->detachChildren();
+        }
         if (what & layer_state_t::eOverrideScalingModeChanged) {
             layer->setOverrideScalingMode(s.overrideScalingMode);
             // We don't trigger a traversal here because if no other state is
@@ -2466,9 +2844,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));
@@ -2499,10 +2877,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;
@@ -2546,10 +2927,11 @@
 
 status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle)
 {
-    // called by the window manager when it wants to remove a Layer
+    // called by a client when it wants to remove a Layer
     status_t err = NO_ERROR;
     sp<Layer> l(client->getLayerUser(handle));
     if (l != NULL) {
+        mInterceptor.saveSurfaceDeletion(l);
         err = removeLayer(l);
         ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
                 "error removing layer=%p (%s)", l.get(), strerror(-err));
@@ -2561,7 +2943,15 @@
 {
     // called by ~LayerCleaner() when all references to the IBinder (handle)
     // are gone
-    return removeLayer(layer);
+    sp<Layer> l = layer.promote();
+    if (l == nullptr) {
+        // The layer has already been removed, carry on
+        return NO_ERROR;
+    } if (l->getParent() != nullptr) {
+        // If we have a parent, then we can continue to live as long as it does.
+        return NO_ERROR;
+    }
+    return removeLayer(l);
 }
 
 // ---------------------------------------------------------------------------
@@ -2587,13 +2977,18 @@
     const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
     const nsecs_t period = activeConfig->getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(period);
+
+    {
+        std::lock_guard<std::mutex> lock(mCompositeTimingLock);
+        mCompositorTiming.interval = period;
+    }
 }
 
 void SurfaceFlinger::initializeDisplays() {
     class MessageScreenInitialized : public MessageBase {
         SurfaceFlinger* flinger;
     public:
-        MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
+        explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { }
         virtual bool handler() {
             flinger->onInitializeDisplays();
             return true;
@@ -2621,6 +3016,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);
@@ -2753,9 +3158,9 @@
             }
 
             if ((index < numArgs) &&
-                    (args[index] == String16("--fences"))) {
+                    (args[index] == String16("--frame-events"))) {
                 index++;
-                mFenceTracker.dump(&result);
+                dumpFrameEventsLocked(result);
                 dumpAll = false;
             }
         }
@@ -2775,12 +3180,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,
@@ -2799,14 +3201,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);
             }
-        }
+        });
     }
 }
 
@@ -2819,14 +3218,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();
 }
@@ -2834,31 +3230,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
@@ -2896,6 +3286,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> "
@@ -2989,15 +3389,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
@@ -3075,10 +3472,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");
     }
 
@@ -3136,13 +3532,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:
@@ -3155,12 +3548,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
@@ -3169,13 +3572,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) {
@@ -3306,6 +3718,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;
@@ -3407,7 +3831,7 @@
     }
 
 public:
-    GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+    explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
     :   impl(impl),
         looper(new Looper(true)),
         result(NO_ERROR),
@@ -3441,7 +3865,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))
@@ -3492,7 +3916,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)
@@ -3541,7 +3965,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();
@@ -3585,20 +4009,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();
@@ -3609,7 +4037,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)
 {
@@ -3633,16 +4061,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) {
@@ -3770,7 +4198,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;
@@ -3781,81 +4209,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..d63c0bb 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_SURFACE_FLINGER_H
 #define ANDROID_SURFACE_FLINGER_H
 
+#include <memory>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -35,11 +36,11 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
-#include <binder/IMemory.h>
-
+#include <ui/FenceTime.h>
 #include <ui/PixelFormat.h>
-#include <ui/mat4.h>
+#include <math/mat4.h>
 
+#include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/OccupancyTracker.h>
@@ -53,15 +54,20 @@
 #include "Barrier.h"
 #include "DisplayDevice.h"
 #include "DispSync.h"
-#include "FenceTracker.h"
 #include "FrameTracker.h"
+#include "LayerVector.h"
 #include "MessageQueue.h"
+#include "SurfaceInterceptor.h"
+#include "StartBootAnimThread.h"
 
 #include "DisplayHardware/HWComposer.h"
 #include "Effects/Daltonizer.h"
 
 #include <map>
+#include <mutex>
+#include <queue>
 #include <string>
+#include <utility>
 
 namespace android {
 
@@ -76,6 +82,13 @@
 class Surface;
 class RenderEngine;
 class EventControlThread;
+class VSyncSource;
+class InjectVSyncSource;
+class VrStateCallbacks;
+
+namespace dvr {
+class VrFlinger;
+} // namespace dvr
 
 // ---------------------------------------------------------------------------
 
@@ -145,11 +158,16 @@
         return *mRenderEngine;
     }
 
+    bool authenticateSurfaceTextureLocked(
+        const sp<IGraphicBufferProducer>& bufferProducer) const;
+
 private:
     friend class Client;
     friend class DisplayEventConnection;
+    friend class EventThread;
     friend class Layer;
     friend class MonitoredProducer;
+    friend class VrStateCallbacks;
 
     // This value is specified in number of frames.  Log frame stats at most
     // every half hour.
@@ -164,33 +182,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 +202,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 +212,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 +235,9 @@
     virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
     virtual status_t getHdrCapabilities(const sp<IBinder>& display,
             HdrCapabilities* outCapabilities) const;
+    virtual status_t enableVSyncInjections(bool enable);
+    virtual status_t injectVSync(nsecs_t when);
+
 
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
@@ -248,8 +252,9 @@
     /* ------------------------------------------------------------------------
      * HWComposer::EventHandler interface
      */
-    virtual void onVSyncReceived(int type, nsecs_t timestamp);
+    virtual void onVSyncReceived(HWComposer* composer, int type, nsecs_t timestamp);
     virtual void onHotplugReceived(int disp, bool connected);
+    virtual void onInvalidateReceived(HWComposer* composer);
 
     /* ------------------------------------------------------------------------
      * Message handling
@@ -292,7 +297,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 +308,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,
@@ -324,13 +330,14 @@
     status_t onLayerDestroyed(const wp<Layer>& layer);
 
     // remove a layer from SurfaceFlinger immediately
-    status_t removeLayer(const wp<Layer>& layer);
+    status_t removeLayer(const sp<Layer>& layer);
 
     // add a layer to SurfaceFlinger
     status_t addClientLayer(const sp<Client>& client,
             const sp<IBinder>& handle,
             const sp<IGraphicBufferProducer>& gbc,
-            const sp<Layer>& lbc);
+            const sp<Layer>& lbc,
+            const sp<Layer>& parent);
 
     /* ------------------------------------------------------------------------
      * Boot animation, on/off animations and screen capture
@@ -341,17 +348,19 @@
     void renderScreenImplLocked(
             const sp<const DisplayDevice>& hw,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool yswap, bool useIdentityTransform, Transform::orientation_flags rotation);
 
     status_t captureScreenImplLocked(
             const sp<const DisplayDevice>& hw,
             const sp<IGraphicBufferProducer>& producer,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-            uint32_t minLayerZ, uint32_t maxLayerZ,
+            int32_t minLayerZ, int32_t maxLayerZ,
             bool useIdentityTransform, Transform::orientation_flags rotation,
             bool isLocalScreenshot);
 
+    sp<StartBootAnimThread> mStartBootAnimThread = nullptr;
+
     /* ------------------------------------------------------------------------
      * EGL
      */
@@ -405,24 +414,26 @@
      * Compositing
      */
     void invalidateHwcGeometry();
-    static void computeVisibleRegions(
-            const LayerVector& currentLayers, uint32_t layerStack,
+    void computeVisibleRegions(uint32_t layerStack,
             Region& dirtyRegion, Region& opaqueRegion);
 
-    void preComposition();
+    void preComposition(nsecs_t refreshStartTime);
     void postComposition(nsecs_t refreshStartTime);
+    void updateCompositorTiming(
+            nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
+            std::shared_ptr<FenceTime>& presentFenceTime);
     void 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
@@ -431,11 +442,13 @@
     /* ------------------------------------------------------------------------
      * VSync
      */
-     void enableHardwareVsync();
-     void resyncToHardwareVsync(bool makeAvailable);
-     void disableHardwareVsync(bool makeUnavailable);
+    void enableHardwareVsync();
+    void resyncToHardwareVsync(bool makeAvailable);
+    void disableHardwareVsync(bool makeUnavailable);
+
 public:
-     void resyncWithRateLimit();
+    void resyncWithRateLimit();
+    void getCompositorTiming(CompositorTiming* compositorTiming);
 private:
 
     /* ------------------------------------------------------------------------
@@ -446,21 +459,36 @@
     void clearStatsLocked(const Vector<String16>& args, size_t& index, String8& result);
     void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const;
     bool startDdmConnection();
-    static void appendSfConfigString(String8& result);
+    void appendSfConfigString(String8& result) const;
     void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
             const sp<const DisplayDevice>& hw,
-            uint32_t minLayerZ, uint32_t maxLayerZ);
+            int32_t minLayerZ, int32_t maxLayerZ);
 
     void logFrameStats();
 
     void dumpStaticScreenStats(String8& result) const;
+    // Not const because each Layer needs to query Fences and cache timestamps.
+    void dumpFrameEventsLocked(String8& result);
 
     void recordBufferingStats(const char* layerName,
             std::vector<OccupancyTracker::Segment>&& history);
     void dumpBufferingStats(String8& result) const;
 
-    bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
-            FrameTimestamps* outTimestamps);
+    bool isLayerTripleBufferingDisabled() const {
+        return this->mLayerTripleBufferingDisabled;
+    }
+
+#ifdef USE_HWC2
+    /* ------------------------------------------------------------------------
+     * VrFlinger
+     */
+    void clearHwcLayers(const LayerVector& layers);
+    void resetHwc();
+
+    // Check to see if we should change to or from vr mode, and if so, perform
+    // the handoff.
+    void updateVrMode();
+#endif
 
     /* ------------------------------------------------------------------------
      * Attributes
@@ -473,27 +501,39 @@
     Condition mTransactionCV;
     bool mTransactionPending;
     bool mAnimTransactionPending;
-    Vector< sp<Layer> > mLayersPendingRemoval;
+    SortedVector< sp<Layer> > mLayersPendingRemoval;
     SortedVector< wp<IBinder> > mGraphicBufferProducerList;
 
     // protected by mStateLock (but we could use another lock)
     bool mLayersRemoved;
+    bool mLayersAdded;
 
     // access must be protected by mInvalidateLock
     volatile int32_t mRepaintEverything;
 
-    // constant members (no synchronization needed for access)
+    // current, real and vr hardware composers.
     HWComposer* mHwc;
+#ifdef USE_HWC2
+    HWComposer* mRealHwc;
+    HWComposer* mVrHwc;
+#endif
+    // constant members (no synchronization needed for access)
     RenderEngine* mRenderEngine;
     nsecs_t mBootTime;
     bool mGpuToCpuSupported;
     sp<EventThread> mEventThread;
     sp<EventThread> mSFEventThread;
+    sp<EventThread> mInjectorEventThread;
+    sp<InjectVSyncSource> mVSyncInjector;
     sp<EventControlThread> mEventControlThread;
     EGLContext mEGLContext;
     EGLDisplay mEGLDisplay;
     sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
 
+#ifdef USE_HWC2
+    std::unique_ptr<dvr::VrFlinger> mVrFlinger;
+#endif
+
     // Can only accessed from the main thread, these members
     // don't need synchronization
     State mDrawingState;
@@ -509,12 +549,15 @@
     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
     DefaultKeyedVector< wp<IBinder>, sp<DisplayDevice> > mDisplays;
 
     // don't use a lock for these, we don't care
+    int mVrModeSupported;
     int mDebugRegion;
     int mDebugDDMS;
     int mDebugDisableHWC;
@@ -525,11 +568,14 @@
     nsecs_t mLastTransactionTime;
     bool mBootFinished;
     bool mForceFullDamage;
-    FenceTracker mFenceTracker;
 #ifdef USE_HWC2
     bool mPropagateBackpressure = true;
 #endif
-    bool mUseHwcVirtualDisplays = true;
+    SurfaceInterceptor mInterceptor;
+    bool mUseHwcVirtualDisplays = false;
+
+    // Restrict layers to use two buffers in their bufferqueues.
+    bool mLayerTripleBufferingDisabled = false;
 
     // these are thread safe
     mutable MessageQueue mEventQueue;
@@ -545,10 +591,23 @@
     bool mPrimaryHWVsyncEnabled;
     bool mHWVsyncAvailable;
 
+    // protected by mCompositorTimingLock;
+    mutable std::mutex mCompositeTimingLock;
+    CompositorTiming mCompositorTiming;
+
+    // Only accessed from the main thread.
+    struct CompositePresentTime {
+        nsecs_t composite { -1 };
+        std::shared_ptr<FenceTime> display { FenceTime::NO_FENCE };
+    };
+    std::queue<CompositePresentTime> mCompositePresentTimes;
+
     /* ------------------------------------------------------------------------
      * Feature prototyping
      */
 
+    bool mInjectVSyncs;
+
     Daltonizer mDaltonizer;
 #ifndef USE_HWC2
     bool mDaltonize;
@@ -565,6 +624,8 @@
     nsecs_t mTotalTime;
     std::atomic<nsecs_t> mLastSwapTime;
 
+    size_t mNumLayers;
+
     // Double- vs. triple-buffering stats
     struct BufferingStats {
         BufferingStats()
@@ -587,7 +648,17 @@
     };
     mutable Mutex mBufferingStatsMutex;
     std::unordered_map<std::string, BufferingStats> mBufferingStats;
-};
+
+    // Verify that transaction is being called by an approved process:
+    // either AID_GRAPHICS or AID_SYSTEM.
+    status_t CheckTransactCodeCredentials(uint32_t code);
+
+#ifdef USE_HWC2
+    sp<VrStateCallbacks> mVrStateCallbacks;
+
+    std::atomic<bool> mEnterVrMode;
+#endif
+    };
 
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 6f2520b..2fcbdba 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -23,6 +23,7 @@
 #include <private/gui/SyncFeatures.h>
 
 #include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
 
 #include <utils/Errors.h>
 #include <utils/NativeHandle.h>
@@ -189,10 +190,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;
@@ -207,32 +212,24 @@
     }
 }
 
-void SurfaceFlingerConsumer::releasePendingBuffer()
+bool SurfaceFlingerConsumer::releasePendingBuffer()
 {
     if (!mPendingRelease.isPending) {
         ALOGV("Pending buffer already released");
-        return;
+        return false;
     }
     ALOGV("Releasing pending buffer");
     Mutex::Autolock lock(mMutex);
     status_t result = releaseBufferLocked(mPendingRelease.currentTexture,
             mPendingRelease.graphicBuffer, mPendingRelease.display,
             mPendingRelease.fence);
-    ALOGE_IF(result != NO_ERROR, "releasePendingBuffer failed: %s (%d)",
+    ALOGE_IF(result < NO_ERROR, "releasePendingBuffer failed: %s (%d)",
             strerror(-result), result);
     mPendingRelease = PendingRelease();
-}
-#else
-void SurfaceFlingerConsumer::setReleaseFence(const sp<Fence>& fence) {
-    mPrevReleaseFence = fence;
-    GLConsumer::setReleaseFence(fence);
+    return true;
 }
 #endif
 
-sp<Fence> SurfaceFlingerConsumer::getPrevReleaseFence() const {
-    return mPrevReleaseFence;
-}
-
 void SurfaceFlingerConsumer::setContentsChangedListener(
         const wp<ContentsChangedListener>& listener) {
     setFrameAvailableListener(listener);
@@ -240,6 +237,19 @@
     mContentsChangedListener = listener;
 }
 
+void SurfaceFlingerConsumer::onBuffersReleased() {
+    sp<ContentsChangedListener> listener;
+    {   // scope for the lock
+        Mutex::Autolock lock(mMutex);
+        ALOG_ASSERT(mFrameAvailableListener.unsafe_get() == mContentsChangedListener.unsafe_get());
+        listener = mContentsChangedListener.promote();
+    }
+
+    if (listener != NULL) {
+        listener->onBuffersReleased();
+    }
+}
+
 void SurfaceFlingerConsumer::onSidebandStreamChanged() {
     sp<ContentsChangedListener> listener;
     {   // scope for the lock
@@ -253,10 +263,20 @@
     }
 }
 
-bool SurfaceFlingerConsumer::getFrameTimestamps(uint64_t frameNumber,
-        FrameTimestamps* outTimestamps) const {
-    sp<const Layer> l = mLayer.promote();
-    return l.get() ? l->getFrameTimestamps(frameNumber, outTimestamps) : false;
+void SurfaceFlingerConsumer::onDisconnect() {
+    sp<Layer> l = mLayer.promote();
+    if (l.get()) {
+        l->onDisconnect();
+    }
+}
+
+void SurfaceFlingerConsumer::addAndGetFrameTimestamps(
+        const NewFrameEventsEntry* newTimestamps,
+        FrameEventHistoryDelta *outDelta) {
+    sp<Layer> l = mLayer.promote();
+    if (l.get()) {
+        l->addAndGetFrameTimestamps(newTimestamps, outDelta);
+    }
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 4271039..cfa70ed 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -18,6 +18,8 @@
 #define ANDROID_SURFACEFLINGERCONSUMER_H
 
 #include "DispSync.h"
+
+#include <ui/Region.h>
 #include <gui/GLConsumer.h>
 
 namespace android {
@@ -33,14 +35,14 @@
     static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
 
     struct ContentsChangedListener: public FrameAvailableListener {
+        virtual void onBuffersReleased() = 0;
         virtual void onSidebandStreamChanged() = 0;
     };
 
     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 +63,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,16 +81,19 @@
 
     nsecs_t computeExpectedPresent(const DispSync& dispSync);
 
-    virtual void setReleaseFence(const sp<Fence>& fence) override;
-    sp<Fence> getPrevReleaseFence() const;
+    sp<Fence> getPrevFinalReleaseFence() const;
 #ifdef USE_HWC2
-    void releasePendingBuffer();
+    virtual void setReleaseFence(const sp<Fence>& fence) override;
+    bool releasePendingBuffer();
 #endif
 
-    virtual bool getFrameTimestamps(uint64_t frameNumber,
-            FrameTimestamps* outTimestamps) const override;
+    void onDisconnect() override;
+    void addAndGetFrameTimestamps(
+            const NewFrameEventsEntry* newTimestamps,
+            FrameEventHistoryDelta* outDelta) override;
 
 private:
+    virtual void onBuffersReleased();
     virtual void onSidebandStreamChanged();
 
     wp<ContentsChangedListener> mContentsChangedListener;
@@ -107,11 +112,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..6cd7152 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -24,6 +24,8 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <mutex>
+
 #include <EGL/egl.h>
 
 #include <cutils/properties.h>
@@ -31,13 +33,11 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
-#include <binder/MemoryHeapBase.h>
 #include <binder/PermissionCache.h>
 
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
 
-#include <gui/BitTube.h>
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
@@ -70,7 +70,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 +136,7 @@
         mTransactionPending(false),
         mAnimTransactionPending(false),
         mLayersRemoved(false),
+        mLayersAdded(false),
         mRepaintEverything(0),
         mRenderEngine(NULL),
         mBootTime(systemTime()),
@@ -150,6 +153,7 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
+        mInterceptor(this),
         mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
@@ -158,11 +162,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");
@@ -182,9 +186,13 @@
     ALOGI_IF(mDebugRegion, "showupdates enabled");
     ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
 
-    property_get("debug.sf.disable_hwc_vds", value, "0");
-    mUseHwcVirtualDisplays = !atoi(value);
-    ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
+    property_get("debug.sf.enable_hwc_vds", value, "0");
+    mUseHwcVirtualDisplays = atoi(value);
+    ALOGI_IF(!mUseHwcVirtualDisplays, "Enabling HWC virtual displays");
+
+    property_get("ro.sf.disable_triple_buffer", value, "1");
+    mLayerTripleBufferingDisabled = atoi(value);
+    ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -210,15 +218,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 +255,7 @@
              flinger->setTransactionFlags(eDisplayTransactionNeeded);
          }
      public:
-        DisplayToken(const sp<SurfaceFlinger>& flinger)
+        explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
             : flinger(flinger) {
         }
     };
@@ -244,7 +266,7 @@
     DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
     info.displayName = displayName;
     mCurrentState.displays.add(token, info);
-
+    mInterceptor.saveDisplayCreation(info);
     return token;
 }
 
@@ -262,7 +284,7 @@
         ALOGE("destroyDisplay called for non-virtual display");
         return;
     }
-
+    mInterceptor.saveDisplayDeletion(info.displayId);
     mCurrentState.displays.removeItemsAt(idx);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -274,6 +296,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) {
@@ -292,6 +315,9 @@
 
 void SurfaceFlinger::bootFinished()
 {
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -446,6 +472,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 +509,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
@@ -540,8 +590,12 @@
 
     mRenderEngine->primeCache();
 
-    // start boot animation
-    startBootAnim();
+    mStartBootAnimThread = new StartBootAnimThread();
+    if (mStartBootAnimThread->Start() != NO_ERROR) {
+        ALOGE("Run StartBootAnimThread failed!");
+    }
+
+    ALOGV("Done initializing");
 }
 
 int32_t SurfaceFlinger::allocateHwcDisplayId(DisplayDevice::DisplayType type) {
@@ -550,9 +604,13 @@
 }
 
 void SurfaceFlinger::startBootAnim() {
-    // start boot animation
-    property_set("service.bootanim.exit", "0");
-    property_set("ctl.start", "bootanim");
+    // Start boot animation service by setting a property mailbox
+    // if property setting thread is already running, Start() will be just a NOP
+    mStartBootAnimThread->Start();
+    // Wait until property was set
+    if (mStartBootAnimThread->join() != NO_ERROR) {
+        ALOGE("Join StartBootAnimThread failed!");
+    }
 }
 
 size_t SurfaceFlinger::getMaxTextureSize() const {
@@ -568,10 +626,31 @@
 bool SurfaceFlinger::authenticateSurfaceTexture(
         const sp<IGraphicBufferProducer>& bufferProducer) const {
     Mutex::Autolock _l(mStateLock);
+    return authenticateSurfaceTextureLocked(bufferProducer);
+}
+
+bool SurfaceFlinger::authenticateSurfaceTextureLocked(
+        const sp<IGraphicBufferProducer>& bufferProducer) const {
     sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
     return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
 }
 
+status_t SurfaceFlinger::getSupportedFrameTimestamps(
+        std::vector<FrameEvent>* outSupported) const {
+    *outSupported = {
+        FrameEvent::REQUESTED_PRESENT,
+        FrameEvent::ACQUIRE,
+        FrameEvent::LATCH,
+        FrameEvent::FIRST_REFRESH_START,
+        FrameEvent::LAST_REFRESH_START,
+        FrameEvent::GPU_COMPOSITION_DONE,
+        FrameEvent::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 +888,40 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
+    if (enable == mInjectVSyncs) {
+        return NO_ERROR;
+    }
+
+    if (enable) {
+        mInjectVSyncs = enable;
+        ALOGV("VSync Injections enabled");
+        if (mVSyncInjector.get() == nullptr) {
+            mVSyncInjector = new InjectVSyncSource();
+            mInjectorEventThread = new EventThread(mVSyncInjector, *this, false);
+        }
+        mEventQueue.setEventThread(mInjectorEventThread);
+    } else {
+        mInjectVSyncs = enable;
+        ALOGV("VSync Injections disabled");
+        mEventQueue.setEventThread(mSFEventThread);
+        mVSyncInjector.clear();
+    }
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::injectVSync(nsecs_t when) {
+    if (!mInjectVSyncs) {
+        ALOGE("VSync Injections not enabled");
+        return BAD_VALUE;
+    }
+    if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
+        ALOGV("Injecting VSync inside SurfaceFlinger");
+        mVSyncInjector->onInjectSyncEvent(when);
+    }
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
@@ -908,7 +1021,8 @@
     }
 }
 
-void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
+void SurfaceFlinger::onVSyncReceived(HWComposer* /*composer*/, int type,
+                                     nsecs_t timestamp) {
     bool needsHwVsync = false;
 
     { // Scope for the lock
@@ -925,6 +1039,11 @@
     }
 }
 
+void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
+    std::lock_guard<std::mutex> lock(mCompositeTimingLock);
+    *compositorTiming = mCompositorTiming;
+}
+
 void SurfaceFlinger::onHotplugReceived(int type, bool connected) {
     if (mEventThread == NULL) {
         // This is a temporary workaround for b/7145521.  A non-null pointer
@@ -948,6 +1067,10 @@
     }
 }
 
+void SurfaceFlinger::onInvalidateReceived(HWComposer* /*composer*/) {
+    repaintEverything();
+}
+
 void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
     ATRACE_CALL();
     getHwComposer().eventControl(disp, event, enabled);
@@ -976,7 +1099,7 @@
 }
 
 bool SurfaceFlinger::handleMessageTransaction() {
-    uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
+    uint32_t transactionFlags = peekTransactionFlags();
     if (transactionFlags) {
         handleTransaction(transactionFlags);
         return true;
@@ -994,7 +1117,7 @@
 
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
-    preComposition();
+    preComposition(refreshStartTime);
     rebuildLayerStacks();
     setUpHWComposer();
     doDebugFlashRegions();
@@ -1042,59 +1165,132 @@
     }
 }
 
-void SurfaceFlinger::preComposition()
+void SurfaceFlinger::preComposition(nsecs_t refreshStartTime)
 {
     bool needExtraInvalidate = false;
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        if (layers[i]->onPreComposition()) {
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        if (layer->onPreComposition(refreshStartTime)) {
             needExtraInvalidate = true;
         }
-    }
+    });
+
     if (needExtraInvalidate) {
         signalLayerUpdate();
     }
 }
 
-void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-{
-    const LayerVector& layers(mDrawingState.layersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        bool frameLatched = layers[i]->onPostComposition();
-        if (frameLatched) {
-            recordBufferingStats(layers[i]->getName().string(),
-                    layers[i]->getOccupancyHistory(false));
+void SurfaceFlinger::updateCompositorTiming(
+        nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime,
+        std::shared_ptr<FenceTime>& presentFenceTime) {
+    // Update queue of past composite+present times and determine the
+    // most recently known composite to present latency.
+    mCompositePresentTimes.push({compositeTime, presentFenceTime});
+    nsecs_t compositeToPresentLatency = -1;
+    while (!mCompositePresentTimes.empty()) {
+        CompositePresentTime& cpt = mCompositePresentTimes.front();
+        // Cached values should have been updated before calling this method,
+        // which helps avoid duplicate syscalls.
+        nsecs_t displayTime = cpt.display->getCachedSignalTime();
+        if (displayTime == Fence::SIGNAL_TIME_PENDING) {
+            break;
         }
+        compositeToPresentLatency = displayTime - cpt.composite;
+        mCompositePresentTimes.pop();
     }
 
-    const HWComposer& hwc = getHwComposer();
-    sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+    // Don't let mCompositePresentTimes grow unbounded, just in case.
+    while (mCompositePresentTimes.size() > 16) {
+        mCompositePresentTimes.pop();
+    }
 
-    if (presentFence->isValid()) {
-        if (mPrimaryDispSync.addPresentFence(presentFence)) {
+    // Integer division and modulo round toward 0 not -inf, so we need to
+    // treat negative and positive offsets differently.
+    nsecs_t idealLatency = (sfVsyncPhaseOffsetNs >= 0) ?
+            (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) :
+            ((-sfVsyncPhaseOffsetNs) % vsyncInterval);
+
+    // Snap the latency to a value that removes scheduling jitter from the
+    // composition and present times, which often have >1ms of jitter.
+    // Reducing jitter is important if an app attempts to extrapolate
+    // something (such as user input) to an accurate diasplay time.
+    // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs
+    // with (presentLatency % interval).
+    nsecs_t snappedCompositeToPresentLatency = -1;
+    if (compositeToPresentLatency >= 0) {
+        nsecs_t bias = vsyncInterval / 2;
+        int64_t extraVsyncs =
+                (compositeToPresentLatency - idealLatency + bias) /
+                vsyncInterval;
+        nsecs_t extraLatency = extraVsyncs * vsyncInterval;
+        snappedCompositeToPresentLatency = idealLatency + extraLatency;
+    }
+
+    std::lock_guard<std::mutex> lock(mCompositeTimingLock);
+    mCompositorTiming.deadline = vsyncPhase - idealLatency;
+    mCompositorTiming.interval = vsyncInterval;
+    if (snappedCompositeToPresentLatency >= 0) {
+        mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
+    }
+}
+
+void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+{
+    const HWComposer& hwc = getHwComposer();
+    const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+
+    std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
+    if (getHwComposer().hasGlesComposition(hw->getHwcDisplayId())) {
+        glCompositionDoneFenceTime =
+                std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
+        mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
+    } else {
+        glCompositionDoneFenceTime = FenceTime::NO_FENCE;
+    }
+    mGlCompositionDoneTimeline.updateSignalTimes();
+
+    sp<Fence> 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();
+
+    nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0);
+    nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod();
+
+    // We use the refreshStartTime which might be sampled a little later than
+    // when we started doing work for this frame, but that should be okay
+    // since updateCompositorTiming has snapping logic.
+    updateCompositorTiming(
+        vsyncPhase, vsyncInterval, refreshStartTime, retireFenceTime);
+
+    mDrawingState.traverseInZOrder([&](Layer* layer) {
+        bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime,
+                presentFenceTime, retireFenceTime, mCompositorTiming);
+        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 +1328,6 @@
         mVisibleRegionsDirty = false;
         invalidateHwcGeometry();
 
-        const LayerVector& layers(mDrawingState.layersSortedByZ);
         for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
             Region opaqueRegion;
             Region dirtyRegion;
@@ -1141,14 +1336,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 +1348,7 @@
                             layersSortedByZ.add(layer);
                         }
                     }
-                }
+                });
             }
             hw->setVisibleLayersSortedByZ(layersSortedByZ);
             hw->undefinedRegion.set(bounds);
@@ -1379,13 +1571,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 +1582,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 +1776,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 +1810,9 @@
                 disp = getDefaultDisplayDevice();
             }
             layer->updateTransformHint(disp);
-        }
+
+            first = false;
+        });
     }
 
 
@@ -1630,9 +1820,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 +1831,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 +1879,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 +1892,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 +1911,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 +1952,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 +1969,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 +2026,7 @@
         layer->setCoveredRegion(coveredRegion);
         layer->setVisibleNonTransparentRegion(
                 visibleRegion.subtract(transparentRegion));
-    }
+    });
 
     outOpaqueRegion = aboveOpaqueLayers;
 }
@@ -1857,10 +2043,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 +2059,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 +2247,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 +2291,23 @@
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
         const sp<IBinder>& handle,
         const sp<IGraphicBufferProducer>& gbc,
-        const sp<Layer>& lbc)
+        const sp<Layer>& lbc,
+        const sp<Layer>& parent)
 {
     // add this layer to the current state list
     {
         Mutex::Autolock _l(mStateLock);
-        if (mCurrentState.layersSortedByZ.size() >= MAX_LAYERS) {
+        if (mNumLayers >= MAX_LAYERS) {
             return NO_MEMORY;
         }
-        mCurrentState.layersSortedByZ.add(lbc);
+        if (parent == nullptr) {
+            mCurrentState.layersSortedByZ.add(lbc);
+        } else {
+            parent->addChild(lbc);
+        }
         mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
+        mLayersAdded = true;
+        mNumLayers++;
     }
 
     // attach this layer to the client
@@ -2125,25 +2316,33 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::removeLayer(const wp<Layer>& weakLayer) {
-    Mutex::Autolock _l(mStateLock);
-    sp<Layer> layer = weakLayer.promote();
-    if (layer == nullptr) {
-        // The layer has already been removed, carry on
+status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) {
+    const auto& p = layer->getParent();
+    const ssize_t index = (p != nullptr) ? p->removeChild(layer) :
+             mCurrentState.layersSortedByZ.remove(layer);
+
+    // As a matter of normal operation, the LayerCleaner will produce a second
+    // attempt to remove the surface. The Layer will be kept alive in mDrawingState
+    // so we will succeed in promoting it, but it's already been removed
+    // from mCurrentState. As long as we can find it in mDrawingState we have no problem
+    // otherwise something has gone wrong and we are leaking the layer.
+    if (index < 0 && mDrawingState.layersSortedByZ.indexOf(layer) < 0) {
+        ALOGE("Failed to find layer (%s) in layer parent (%s).",
+                layer->getName().string(),
+                (p != nullptr) ? p->getName().string() : "no-parent");
+        return BAD_VALUE;
+    } else if (index < 0) {
         return NO_ERROR;
     }
 
-    ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
-    if (index >= 0) {
-        mLayersPendingRemoval.push(layer);
-        mLayersRemoved = true;
-        setTransactionFlags(eTransactionNeeded);
-        return NO_ERROR;
-    }
-    return status_t(index);
+    mLayersPendingRemoval.add(layer);
+    mLayersRemoved = true;
+    mNumLayers--;
+    setTransactionFlags(eTransactionNeeded);
+    return NO_ERROR;
 }
 
-uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
+uint32_t SurfaceFlinger::peekTransactionFlags() {
     return android_atomic_release_load(&mTransactionFlags);
 }
 
@@ -2219,6 +2418,10 @@
     }
 
     if (transactionFlags) {
+        if (mInterceptor.isEnabled()) {
+            mInterceptor.saveTransaction(state, mCurrentState.displays, displays, flags);
+        }
+
         // this triggers the transaction
         setTransactionFlags(transactionFlags);
 
@@ -2310,13 +2513,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 +2559,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)
@@ -2360,10 +2578,30 @@
             }
         }
         if (what & layer_state_t::eDeferTransaction) {
-            layer->deferTransactionUntil(s.handle, s.frameNumber);
+            if (s.barrierHandle != nullptr) {
+                layer->deferTransactionUntil(s.barrierHandle, s.frameNumber);
+            } else if (s.barrierGbp != nullptr) {
+                const sp<IGraphicBufferProducer>& gbp = s.barrierGbp;
+                if (authenticateSurfaceTextureLocked(gbp)) {
+                    const auto& otherLayer =
+                        (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
+                    layer->deferTransactionUntil(otherLayer, s.frameNumber);
+                } else {
+                    ALOGE("Attempt to defer transaction to to an"
+                            " unrecognized GraphicBufferProducer");
+                }
+            }
             // We don't trigger a traversal here because if no other state is
             // changed, we don't want this to cause any more work
         }
+        if (what & layer_state_t::eReparentChildren) {
+            if (layer->reparentChildren(s.reparentHandle)) {
+                flags |= eTransactionNeeded|eTraversalNeeded;
+            }
+        }
+        if (what & layer_state_t::eDetachChildren) {
+            layer->detachChildren();
+        }
         if (what & layer_state_t::eOverrideScalingModeChanged) {
             layer->setOverrideScalingMode(s.overrideScalingMode);
             // We don't trigger a traversal here because if no other state is
@@ -2377,9 +2615,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 +2648,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;
@@ -2457,10 +2698,11 @@
 
 status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle)
 {
-    // called by the window manager when it wants to remove a Layer
+    // called by a client when it wants to remove a Layer
     status_t err = NO_ERROR;
     sp<Layer> l(client->getLayerUser(handle));
     if (l != NULL) {
+        mInterceptor.saveSurfaceDeletion(l);
         err = removeLayer(l);
         ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
                 "error removing layer=%p (%s)", l.get(), strerror(-err));
@@ -2472,7 +2714,15 @@
 {
     // called by ~LayerCleaner() when all references to the IBinder (handle)
     // are gone
-    return removeLayer(layer);
+    sp<Layer> l = layer.promote();
+    if (l == nullptr) {
+        // The layer has already been removed, carry on
+        return NO_ERROR;
+    } if (l->getParent() != nullptr) {
+        // If we have a parent, then we can continue to live as long as it does.
+        return NO_ERROR;
+    }
+    return removeLayer(l);
 }
 
 // ---------------------------------------------------------------------------
@@ -2504,7 +2754,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 +2782,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 +2924,9 @@
             }
 
             if ((index < numArgs) &&
-                    (args[index] == String16("--fences"))) {
+                    (args[index] == String16("--frame-events"))) {
                 index++;
-                mFenceTracker.dump(&result);
+                dumpFrameEventsLocked(result);
                 dumpAll = false;
             }
         }
@@ -2686,12 +2946,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 +2967,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 +2984,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 +2996,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 +3034,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 +3152,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 +3283,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 +3301,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 +3456,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 +3569,7 @@
     }
 
 public:
-    GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
+    explicit GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl)
     :   impl(impl),
         looper(new Looper(true)),
         result(NO_ERROR),
@@ -3329,7 +3603,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 +3644,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 +3654,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 +3703,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 +3747,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 +3777,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 +3801,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 +3929,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 +3941,33 @@
         ALOGE("*** we just took a black screenshot ***\n"
                 "requested minz=%d, maxz=%d, layerStack=%d",
                 minLayerZ, maxLayerZ, hw->getLayerStack());
-        const LayerVector& layers( mDrawingState.layersSortedByZ );
-        const size_t count = layers.size();
-        for (size_t i=0 ; i<count ; ++i) {
-            const sp<Layer>& layer(layers[i]);
+        size_t i = 0;
+        for (const auto& layer : mDrawingState.layersSortedByZ) {
             const Layer::State& state(layer->getDrawingState());
-            const bool visible = (state.layerStack == hw->getLayerStack())
-                                && (state.z >= minLayerZ && state.z <= maxLayerZ)
-                                && (layer->isVisible());
-            ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x",
-                    visible ? '+' : '-',
-                            i, layer->getName().string(), state.layerStack, state.z,
+            if (layer->getLayerStack() == hw->getLayerStack() && state.z >= minLayerZ &&
+                    state.z <= maxLayerZ) {
+                layer->traverseInZOrder([&](Layer* layer) {
+                    ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%x",
+                            layer->isVisible() ? '+' : '-',
+                            i, layer->getName().string(), layer->getLayerStack(), state.z,
                             layer->isVisible(), state.flags, state.alpha);
+                    i++;
+                });
+            }
         }
     }
 }
 
 // ---------------------------------------------------------------------------
 
-SurfaceFlinger::LayerVector::LayerVector() {
+void SurfaceFlinger::State::traverseInZOrder(const std::function<void(Layer*)>& consume) const {
+    layersSortedByZ.traverseInZOrder(consume);
 }
 
-SurfaceFlinger::LayerVector::LayerVector(const LayerVector& rhs)
-    : SortedVector<sp<Layer> >(rhs) {
+void SurfaceFlinger::State::traverseInReverseZOrder(const std::function<void(Layer*)>& consume) const {
+    layersSortedByZ.traverseInReverseZOrder(consume);
 }
 
-int SurfaceFlinger::LayerVector::do_compare(const void* lhs,
-    const void* rhs) const
-{
-    // sort layers per layer-stack, then by z-order and finally by sequence
-    const sp<Layer>& l(*reinterpret_cast<const sp<Layer>*>(lhs));
-    const sp<Layer>& r(*reinterpret_cast<const sp<Layer>*>(rhs));
-
-    uint32_t ls = l->getCurrentState().layerStack;
-    uint32_t rs = r->getCurrentState().layerStack;
-    if (ls != rs)
-        return ls - rs;
-
-    uint32_t lz = l->getCurrentState().z;
-    uint32_t rz = r->getCurrentState().z;
-    if (lz != rz)
-        return lz - rz;
-
-    return l->sequence - r->sequence;
-}
-
-// ---------------------------------------------------------------------------
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState()
-    : type(DisplayDevice::DISPLAY_ID_INVALID),
-      layerStack(DisplayDevice::NO_LAYER_STACK),
-      orientation(0),
-      width(0),
-      height(0),
-      isSecure(false) {
-}
-
-SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(
-    DisplayDevice::DisplayType type, bool isSecure)
-    : type(type),
-      layerStack(DisplayDevice::NO_LAYER_STACK),
-      orientation(0),
-      width(0),
-      height(0),
-      isSecure(isSecure) {
-    viewport.makeInvalid();
-    frame.makeInvalid();
-}
-
-// ---------------------------------------------------------------------------
-
 }; // namespace android
 
 
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
new file mode 100644
index 0000000..026fe80
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -0,0 +1,597 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#undef LOG_TAG
+#define LOG_TAG "SurfaceInterceptor"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+#include "SurfaceInterceptor.h"
+
+#include <fstream>
+
+#include <android-base/file.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+SurfaceInterceptor::SurfaceInterceptor(SurfaceFlinger* flinger)
+    :   mFlinger(flinger)
+{
+}
+
+void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
+        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
+{
+    if (mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    mEnabled = true;
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    saveExistingDisplaysLocked(displays);
+    saveExistingSurfacesLocked(layers);
+}
+
+void SurfaceInterceptor::disable() {
+    if (!mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    mEnabled = false;
+    status_t err(writeProtoFileLocked());
+    ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
+    ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
+    mTrace.Clear();
+}
+
+bool SurfaceInterceptor::isEnabled() {
+    return mEnabled;
+}
+
+void SurfaceInterceptor::saveExistingDisplaysLocked(
+        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
+{
+    // Caveat: The initial snapshot does not capture the power mode of the existing displays
+    ATRACE_CALL();
+    for (size_t i = 0 ; i < displays.size() ; i++) {
+        addDisplayCreationLocked(createTraceIncrementLocked(), displays[i]);
+        addInitialDisplayStateLocked(createTraceIncrementLocked(), displays[i]);
+    }
+}
+
+void SurfaceInterceptor::saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers) {
+    ATRACE_CALL();
+    for (const auto& l : layers) {
+        l->traverseInZOrder([this](Layer* layer) {
+            addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
+            addInitialSurfaceStateLocked(createTraceIncrementLocked(), layer);
+        });
+    }
+}
+
+void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment,
+        const sp<const Layer>& layer)
+{
+    Transaction* transaction(increment->mutable_transaction());
+    transaction->set_synchronous(layer->mTransactionFlags & BnSurfaceComposer::eSynchronous);
+    transaction->set_animation(layer->mTransactionFlags & BnSurfaceComposer::eAnimation);
+
+    const int32_t layerId(getLayerId(layer));
+    addPositionLocked(transaction, layerId, layer->mCurrentState.active.transform.tx(),
+            layer->mCurrentState.active.transform.ty());
+    addDepthLocked(transaction, layerId, layer->mCurrentState.z);
+    addAlphaLocked(transaction, layerId, layer->mCurrentState.alpha);
+    addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion);
+    addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
+    addCropLocked(transaction, layerId, layer->mCurrentState.crop);
+    if (layer->mCurrentState.barrierLayer != nullptr) {
+        addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer.promote(),
+                layer->mCurrentState.frameNumber);
+    }
+    addFinalCropLocked(transaction, layerId, layer->mCurrentState.finalCrop);
+    addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
+    addFlagsLocked(transaction, layerId, layer->mCurrentState.flags);
+}
+
+void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
+        const DisplayDeviceState& display)
+{
+    Transaction* transaction(increment->mutable_transaction());
+    transaction->set_synchronous(false);
+    transaction->set_animation(false);
+
+    addDisplaySurfaceLocked(transaction, display.displayId, display.surface);
+    addDisplayLayerStackLocked(transaction, display.displayId, display.layerStack);
+    addDisplaySizeLocked(transaction, display.displayId, display.width, display.height);
+    addDisplayProjectionLocked(transaction, display.displayId, display.orientation,
+            display.viewport, display.frame);
+}
+
+status_t SurfaceInterceptor::writeProtoFileLocked() {
+    ATRACE_CALL();
+    std::string output;
+
+    if (!mTrace.IsInitialized()) {
+        return NOT_ENOUGH_DATA;
+    }
+    if (!mTrace.SerializeToString(&output)) {
+        return PERMISSION_DENIED;
+    }
+    if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
+        return PERMISSION_DENIED;
+    }
+
+    return NO_ERROR;
+}
+
+const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) {
+    const sp<const IBinder>& handle(weakHandle.promote());
+    const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
+    const sp<const Layer> layer(layerHandle->owner.promote());
+    // layer could be a nullptr at this point
+    return layer;
+}
+
+const std::string SurfaceInterceptor::getLayerName(const sp<const Layer>& layer) {
+    return layer->getName().string();
+}
+
+int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) {
+    return layer->sequence;
+}
+
+Increment* SurfaceInterceptor::createTraceIncrementLocked() {
+    Increment* increment(mTrace.add_increment());
+    increment->set_time_stamp(systemTime());
+    return increment;
+}
+
+SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transaction,
+        int32_t layerId)
+{
+    SurfaceChange* change(transaction->add_surface_change());
+    change->set_id(layerId);
+    return change;
+}
+
+DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
+        int32_t displayId)
+{
+    DisplayChange* dispChange(transaction->add_display_change());
+    dispChange->set_id(displayId);
+    return dispChange;
+}
+
+void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) {
+    protoRect->set_left(rect.left);
+    protoRect->set_top(rect.top);
+    protoRect->set_right(rect.right);
+    protoRect->set_bottom(rect.bottom);
+}
+
+void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
+        float x, float y)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    PositionChange* posChange(change->mutable_position());
+    posChange->set_x(x);
+    posChange->set_y(y);
+}
+
+void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId,
+        uint32_t z)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    LayerChange* depthChange(change->mutable_layer());
+    depthChange->set_layer(z);
+}
+
+void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w,
+        uint32_t h)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    SizeChange* sizeChange(change->mutable_size());
+    sizeChange->set_w(w);
+    sizeChange->set_h(h);
+}
+
+void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId,
+        float alpha)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    AlphaChange* alphaChange(change->mutable_alpha());
+    alphaChange->set_alpha(alpha);
+}
+
+void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId,
+        const layer_state_t::matrix22_t& matrix)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    MatrixChange* matrixChange(change->mutable_matrix());
+    matrixChange->set_dsdx(matrix.dsdx);
+    matrixChange->set_dtdx(matrix.dtdx);
+    matrixChange->set_dsdy(matrix.dsdy);
+    matrixChange->set_dtdy(matrix.dtdy);
+}
+
+void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction,
+        int32_t layerId, const Region& transRegion)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint());
+
+    for (const auto& rect : transRegion) {
+        Rectangle* protoRect(transparentChange->add_region());
+        setProtoRectLocked(protoRect, rect);
+    }
+}
+
+void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId,
+        uint8_t flags)
+{
+    // There can be multiple flags changed
+    if (flags & layer_state_t::eLayerHidden) {
+        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+        HiddenFlagChange* flagChange(change->mutable_hidden_flag());
+        flagChange->set_hidden_flag(true);
+    }
+    if (flags & layer_state_t::eLayerOpaque) {
+        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+        OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
+        flagChange->set_opaque_flag(true);
+    }
+    if (flags & layer_state_t::eLayerSecure) {
+        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+        SecureFlagChange* flagChange(change->mutable_secure_flag());
+        flagChange->set_secure_flag(true);
+    }
+}
+
+void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
+        uint32_t layerStack)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    LayerStackChange* layerStackChange(change->mutable_layer_stack());
+    layerStackChange->set_layer_stack(layerStack);
+}
+
+void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
+        const Rect& rect)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    CropChange* cropChange(change->mutable_crop());
+    Rectangle* protoRect(cropChange->mutable_rectangle());
+    setProtoRectLocked(protoRect, rect);
+}
+
+void SurfaceInterceptor::addFinalCropLocked(Transaction* transaction, int32_t layerId,
+        const Rect& rect)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    FinalCropChange* finalCropChange(change->mutable_final_crop());
+    Rectangle* protoRect(finalCropChange->mutable_rectangle());
+    setProtoRectLocked(protoRect, rect);
+}
+
+void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+        const sp<const Layer>& layer, uint64_t frameNumber)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    if (layer == nullptr) {
+        ALOGE("An existing layer could not be retrieved with the handle"
+                " for the deferred transaction");
+        return;
+    }
+    DeferredTransactionChange* deferTransaction(change->mutable_deferred_transaction());
+    deferTransaction->set_layer_id(getLayerId(layer));
+    deferTransaction->set_frame_number(frameNumber);
+}
+
+void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction,
+        int32_t layerId, int32_t overrideScalingMode)
+{
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    OverrideScalingModeChange* overrideChange(change->mutable_override_scaling_mode());
+    overrideChange->set_override_scaling_mode(overrideScalingMode);
+}
+
+void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
+        const layer_state_t& state)
+{
+    const sp<const Layer> layer(getLayer(state.surface));
+    if (layer == nullptr) {
+        ALOGE("An existing layer could not be retrieved with the surface "
+                "from the layer_state_t surface in the update transaction");
+        return;
+    }
+
+    const int32_t layerId(getLayerId(layer));
+
+    if (state.what & layer_state_t::ePositionChanged) {
+        addPositionLocked(transaction, layerId, state.x, state.y);
+    }
+    if (state.what & layer_state_t::eLayerChanged) {
+        addDepthLocked(transaction, layerId, state.z);
+    }
+    if (state.what & layer_state_t::eSizeChanged) {
+        addSizeLocked(transaction, layerId, state.w, state.h);
+    }
+    if (state.what & layer_state_t::eAlphaChanged) {
+        addAlphaLocked(transaction, layerId, state.alpha);
+    }
+    if (state.what & layer_state_t::eMatrixChanged) {
+        addMatrixLocked(transaction, layerId, state.matrix);
+    }
+    if (state.what & layer_state_t::eTransparentRegionChanged) {
+        addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
+    }
+    if (state.what & layer_state_t::eFlagsChanged) {
+        addFlagsLocked(transaction, layerId, state.flags);
+    }
+    if (state.what & layer_state_t::eLayerStackChanged) {
+        addLayerStackLocked(transaction, layerId, state.layerStack);
+    }
+    if (state.what & layer_state_t::eCropChanged) {
+        addCropLocked(transaction, layerId, state.crop);
+    }
+    if (state.what & layer_state_t::eDeferTransaction) {
+        sp<Layer> otherLayer = nullptr;
+        if (state.barrierHandle != nullptr) {
+            otherLayer = static_cast<Layer::Handle*>(state.barrierHandle.get())->owner.promote();
+        } else if (state.barrierGbp != nullptr) {
+            auto const& gbp = state.barrierGbp;
+            if (mFlinger->authenticateSurfaceTextureLocked(gbp)) {
+                otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
+            } else {
+                ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer");
+            }
+        }
+        addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber);
+    }
+    if (state.what & layer_state_t::eFinalCropChanged) {
+        addFinalCropLocked(transaction, layerId, state.finalCrop);
+    }
+    if (state.what & layer_state_t::eOverrideScalingModeChanged) {
+        addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
+    }
+}
+
+void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
+        const DisplayState& state, int32_t displayId)
+{
+    if (state.what & DisplayState::eSurfaceChanged) {
+        addDisplaySurfaceLocked(transaction, displayId, state.surface);
+    }
+    if (state.what & DisplayState::eLayerStackChanged) {
+        addDisplayLayerStackLocked(transaction, displayId, state.layerStack);
+    }
+    if (state.what & DisplayState::eDisplaySizeChanged) {
+        addDisplaySizeLocked(transaction, displayId, state.width, state.height);
+    }
+    if (state.what & DisplayState::eDisplayProjectionChanged) {
+        addDisplayProjectionLocked(transaction, displayId, state.orientation, state.viewport,
+                state.frame);
+    }
+}
+
+void SurfaceInterceptor::addTransactionLocked(Increment* increment,
+        const Vector<ComposerState>& stateUpdates,
+        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+        const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags)
+{
+    Transaction* transaction(increment->mutable_transaction());
+    transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
+    transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
+    for (const auto& compState: stateUpdates) {
+        addSurfaceChangesLocked(transaction, compState.state);
+    }
+    for (const auto& disp: changedDisplays) {
+        ssize_t dpyIdx = displays.indexOfKey(disp.token);
+        if (dpyIdx >= 0) {
+            const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
+            addDisplayChangesLocked(transaction, disp, dispState.displayId);
+        }
+    }
+}
+
+void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment,
+        const sp<const Layer>& layer)
+{
+    SurfaceCreation* creation(increment->mutable_surface_creation());
+    creation->set_id(getLayerId(layer));
+    creation->set_name(getLayerName(layer));
+    creation->set_w(layer->mCurrentState.active.w);
+    creation->set_h(layer->mCurrentState.active.h);
+}
+
+void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
+        const sp<const Layer>& layer)
+{
+    SurfaceDeletion* deletion(increment->mutable_surface_deletion());
+    deletion->set_id(getLayerId(layer));
+}
+
+void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer,
+        uint32_t width, uint32_t height, uint64_t frameNumber)
+{
+    BufferUpdate* update(increment->mutable_buffer_update());
+    update->set_id(getLayerId(layer));
+    update->set_w(width);
+    update->set_h(height);
+    update->set_frame_number(frameNumber);
+}
+
+void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp) {
+    VSyncEvent* event(increment->mutable_vsync_event());
+    event->set_when(timestamp);
+}
+
+void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+        const sp<const IGraphicBufferProducer>& surface)
+{
+    if (surface == nullptr) {
+        return;
+    }
+    uint64_t bufferQueueId = 0;
+    status_t err(surface->getUniqueId(&bufferQueueId));
+    if (err == NO_ERROR) {
+        DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+        DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
+        surfaceChange->set_buffer_queue_id(bufferQueueId);
+        surfaceChange->set_buffer_queue_name(surface->getConsumerName().string());
+    }
+    else {
+        ALOGE("invalid graphic buffer producer received while tracing a display change (%s)",
+                strerror(-err));
+    }
+}
+
+void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction,
+        int32_t displayId, uint32_t layerStack)
+{
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
+    layerStackChange->set_layer_stack(layerStack);
+}
+
+void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t displayId,
+        uint32_t w, uint32_t h)
+{
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    SizeChange* sizeChange(dispChange->mutable_size());
+    sizeChange->set_w(w);
+    sizeChange->set_h(h);
+}
+
+void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
+        int32_t displayId, int32_t orientation, const Rect& viewport, const Rect& frame)
+{
+    DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId));
+    ProjectionChange* projectionChange(dispChange->mutable_projection());
+    projectionChange->set_orientation(orientation);
+    Rectangle* viewportRect(projectionChange->mutable_viewport());
+    setProtoRectLocked(viewportRect, viewport);
+    Rectangle* frameRect(projectionChange->mutable_frame());
+    setProtoRectLocked(frameRect, frame);
+}
+
+void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment,
+        const DisplayDeviceState& info)
+{
+    DisplayCreation* creation(increment->mutable_display_creation());
+    creation->set_id(info.displayId);
+    creation->set_name(info.displayName);
+    creation->set_type(info.type);
+    creation->set_is_secure(info.isSecure);
+}
+
+void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t displayId) {
+    DisplayDeletion* deletion(increment->mutable_display_deletion());
+    deletion->set_id(displayId);
+}
+
+void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t displayId,
+        int32_t mode)
+{
+    PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
+    powerModeUpdate->set_id(displayId);
+    powerModeUpdate->set_mode(mode);
+}
+
+void SurfaceInterceptor::saveTransaction(const Vector<ComposerState>& stateUpdates,
+        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+        const Vector<DisplayState>& changedDisplays, uint32_t flags)
+{
+    if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
+            flags);
+}
+
+void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
+    if (!mEnabled || layer == nullptr) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveSurfaceDeletion(const sp<const Layer>& layer) {
+    if (!mEnabled || layer == nullptr) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveBufferUpdate(const sp<const Layer>& layer, uint32_t width,
+        uint32_t height, uint64_t frameNumber)
+{
+    if (!mEnabled || layer == nullptr) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addBufferUpdateLocked(createTraceIncrementLocked(), layer, width, height, frameNumber);
+}
+
+void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
+    if (!mEnabled) {
+        return;
+    }
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addVSyncUpdateLocked(createTraceIncrementLocked(), timestamp);
+}
+
+void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) {
+    if (!mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addDisplayCreationLocked(createTraceIncrementLocked(), info);
+}
+
+void SurfaceInterceptor::saveDisplayDeletion(int32_t displayId) {
+    if (!mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addDisplayDeletionLocked(createTraceIncrementLocked(), displayId);
+}
+
+void SurfaceInterceptor::savePowerModeUpdate(int32_t displayId, int32_t mode) {
+    if (!mEnabled) {
+        return;
+    }
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+    addPowerModeUpdateLocked(createTraceIncrementLocked(), displayId, mode);
+}
+
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
new file mode 100644
index 0000000..30ebcc6
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACEINTERCEPTOR_H
+#define ANDROID_SURFACEINTERCEPTOR_H
+
+#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
+
+#include <mutex>
+
+#include <utils/SortedVector.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class BufferItem;
+class Layer;
+class SurfaceFlinger;
+struct DisplayState;
+struct layer_state_t;
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+/*
+ * SurfaceInterceptor intercepts and stores incoming streams of window
+ * properties on SurfaceFlinger.
+ */
+class SurfaceInterceptor {
+public:
+    SurfaceInterceptor(SurfaceFlinger* const flinger);
+    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
+    void enable(const SortedVector<sp<Layer>>& layers,
+            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
+    void disable();
+    bool isEnabled();
+
+    // Intercept display and surface transactions
+    void saveTransaction(const Vector<ComposerState>& stateUpdates,
+            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+            const Vector<DisplayState>& changedDisplays, uint32_t flags);
+
+    // Intercept surface data
+    void saveSurfaceCreation(const sp<const Layer>& layer);
+    void saveSurfaceDeletion(const sp<const Layer>& layer);
+    void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+            uint64_t frameNumber);
+
+    // Intercept display data
+    void saveDisplayCreation(const DisplayDeviceState& info);
+    void saveDisplayDeletion(int32_t displayId);
+    void savePowerModeUpdate(int32_t displayId, int32_t mode);
+    void saveVSyncEvent(nsecs_t timestamp);
+
+private:
+    // The creation increments of Surfaces and Displays do not contain enough information to capture
+    // the initial state of each object, so a transaction with all of the missing properties is
+    // performed at the initial snapshot for each display and surface.
+    void saveExistingDisplaysLocked(
+            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
+    void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers);
+    void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer);
+    void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
+
+    status_t writeProtoFileLocked();
+    const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle);
+    const std::string getLayerName(const sp<const Layer>& layer);
+    int32_t getLayerId(const sp<const Layer>& layer);
+
+    Increment* createTraceIncrementLocked();
+    void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
+    void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
+    void addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer, uint32_t width,
+            uint32_t height, uint64_t frameNumber);
+    void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
+    void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
+    void addDisplayDeletionLocked(Increment* increment, int32_t displayId);
+    void addPowerModeUpdateLocked(Increment* increment, int32_t displayId, int32_t mode);
+
+    // Add surface transactions to the trace
+    SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
+    void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
+    void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
+    void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
+    void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
+    void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
+    void addMatrixLocked(Transaction* transaction, int32_t layerId,
+            const layer_state_t::matrix22_t& matrix);
+    void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
+            const Region& transRegion);
+    void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
+    void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
+    void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+    void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+            const sp<const Layer>& layer, uint64_t frameNumber);
+    void addFinalCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+    void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
+            int32_t overrideScalingMode);
+    void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
+    void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
+            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
+            const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+
+    // Add display transactions to the trace
+    DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t displayId);
+    void addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId,
+            const sp<const IGraphicBufferProducer>& surface);
+    void addDisplayLayerStackLocked(Transaction* transaction, int32_t displayId,
+            uint32_t layerStack);
+    void addDisplaySizeLocked(Transaction* transaction, int32_t displayId, uint32_t w,
+            uint32_t h);
+    void addDisplayProjectionLocked(Transaction* transaction, int32_t displayId,
+            int32_t orientation, const Rect& viewport, const Rect& frame);
+    void addDisplayChangesLocked(Transaction* transaction,
+            const DisplayState& state, int32_t displayId);
+
+
+    bool mEnabled {false};
+    std::string mOutputFileName {DEFAULT_FILENAME};
+    std::mutex mTraceMutex {};
+    Trace mTrace {};
+    SurfaceFlinger* const mFlinger;
+};
+
+}
+
+#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index 66463a0..6640a13 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -22,8 +22,8 @@
 
 #include <ui/Point.h>
 #include <ui/Rect.h>
-#include <ui/vec2.h>
-#include <ui/vec3.h>
+#include <math/vec2.h>
+#include <math/vec3.h>
 
 #include <hardware/hardware.h>
 
diff --git a/services/surfaceflinger/VrStateCallbacks.cpp b/services/surfaceflinger/VrStateCallbacks.cpp
new file mode 100644
index 0000000..a924def
--- /dev/null
+++ b/services/surfaceflinger/VrStateCallbacks.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VrStateCallbacks.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+VrStateCallbacks::VrStateCallbacks(SurfaceFlinger& flinger)
+    : mFlinger(flinger) {}
+
+void VrStateCallbacks::onVrStateChanged(bool enabled) {
+    mFlinger.mEnterVrMode = enabled;
+    mFlinger.signalTransaction();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/VrStateCallbacks.h b/services/surfaceflinger/VrStateCallbacks.h
new file mode 100644
index 0000000..4e655d3
--- /dev/null
+++ b/services/surfaceflinger/VrStateCallbacks.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VR_STATE_CALLBACKS_H
+#define ANDROID_VR_STATE_CALLBACKS_H
+
+#include <vr/vr_manager/vr_manager.h>
+
+namespace android {
+
+class SurfaceFlinger;
+
+class VrStateCallbacks : public BnVrStateCallbacks {
+public:
+    VrStateCallbacks(SurfaceFlinger& flinger);
+    void onVrStateChanged(bool enabled) override;
+
+private:
+    SurfaceFlinger& mFlinger;
+};
+
+} // namespace android
+
+#endif // ANDROID_VR_STATE_CALLBACKS_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..aeb557a 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -18,8 +18,6 @@
 
 #include <android/native_window.h>
 
-#include <binder/IMemory.h>
-
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -61,7 +59,6 @@
         sp<IGraphicBufferProducer> producer;
         sp<IGraphicBufferConsumer> consumer;
         BufferQueue::createBufferQueue(&producer, &consumer);
-        IGraphicBufferProducer::QueueBufferOutput bufferOutput;
         sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         sp<IBinder> display(sf->getBuiltInDisplay(
@@ -85,6 +82,18 @@
         }
     }
 
+    void expectFGColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 195, 63, 63);
+    }
+
+    void expectBGColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 63, 63, 195);
+    }
+
+    void expectChildColor(uint32_t x, uint32_t y) {
+        checkPixel(x, y, 200, 200, 200);
+    }
+
 private:
     ScreenCapture(const sp<CpuConsumer>& cc) :
         mCC(cc) {
@@ -141,14 +150,14 @@
 
         mComposerClient->setDisplayLayerStack(display, 0);
 
-        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-2));
+        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX-2));
         ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
 
-        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX-1));
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX-1));
         ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
         ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
 
-        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT_MAX-1));
+        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT32_MAX-1));
         ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setPosition(displayWidth-2,
                 displayHeight-2));
         ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->show());
@@ -188,9 +197,9 @@
     {
         SCOPED_TRACE("before move");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(  0,  12,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(0, 12);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -200,9 +209,9 @@
         // This should reflect the new position, but not the new color.
         SCOPED_TRACE("after move, before redraw");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145, 195,  63,  63);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectFGColor(145, 145);
     }
 
     fillSurfaceRGBA8(mFGSurfaceControl, 63, 195, 63);
@@ -211,9 +220,9 @@
         // This should reflect the new position and the new color.
         SCOPED_TRACE("after redraw");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63, 195,  63);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->checkPixel(145, 145, 63, 195, 63);
     }
 }
 
@@ -222,9 +231,9 @@
     {
         SCOPED_TRACE("before resize");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(  0,  12,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(0, 12);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     ALOGD("resizing");
@@ -237,9 +246,9 @@
         // has not yet received a buffer of the correct size.
         SCOPED_TRACE("after resize, before redraw");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(  0,  12,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(0, 12);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     ALOGD("drawing");
@@ -250,9 +259,9 @@
         // This should reflect the new size and the new color.
         SCOPED_TRACE("after redraw");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63, 195,  63);
-        sc->checkPixel(145, 145,  63, 195,  63);
+        sc->expectBGColor(24, 24);
+        sc->checkPixel(75, 75, 63, 195, 63);
+        sc->checkPixel(145, 145, 63, 195, 63);
     }
 }
 
@@ -261,9 +270,9 @@
     {
         SCOPED_TRACE("before crop");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -274,11 +283,11 @@
         // This should crop the foreground surface.
         SCOPED_TRACE("after crop");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel( 95,  80, 195,  63,  63);
-        sc->checkPixel( 80,  95, 195,  63,  63);
-        sc->checkPixel( 96,  96,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectFGColor(95, 80);
+        sc->expectFGColor(80, 95);
+        sc->expectBGColor(96, 96);
     }
 }
 
@@ -287,9 +296,9 @@
     {
         SCOPED_TRACE("before crop");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
     SurfaceComposerClient::openGlobalTransaction();
     Rect cropRect(16, 16, 32, 32);
@@ -299,11 +308,11 @@
         // This should crop the foreground surface.
         SCOPED_TRACE("after crop");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel( 95,  80,  63,  63, 195);
-        sc->checkPixel( 80,  95,  63,  63, 195);
-        sc->checkPixel( 96,  96,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(95, 80);
+        sc->expectBGColor(80, 95);
+        sc->expectBGColor(96, 96);
     }
 }
 
@@ -312,9 +321,9 @@
     {
         SCOPED_TRACE("before setLayer");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -324,9 +333,9 @@
         // This should hide the foreground surface beneath the background.
         SCOPED_TRACE("after setLayer");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -335,9 +344,9 @@
     {
         SCOPED_TRACE("before hide");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -347,9 +356,9 @@
         // This should hide the foreground surface.
         SCOPED_TRACE("after hide, before show");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -359,9 +368,9 @@
         // This should show the foreground surface.
         SCOPED_TRACE("after show");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -370,9 +379,9 @@
     {
         SCOPED_TRACE("before setAlpha");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -382,9 +391,9 @@
         // This should set foreground to be 75% opaque.
         SCOPED_TRACE("after setAlpha");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 162,  63,  96);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->checkPixel(75, 75, 162, 63, 96);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -393,9 +402,9 @@
     {
         SCOPED_TRACE("before setLayerStack");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -406,9 +415,9 @@
         // layer stack.
         SCOPED_TRACE("after setLayerStack");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -417,9 +426,9 @@
     {
         SCOPED_TRACE("before setFlags");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -430,9 +439,9 @@
         // This should hide the foreground surface
         SCOPED_TRACE("after setFlags");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 75,  75,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -441,10 +450,10 @@
     {
         SCOPED_TRACE("before setMatrix");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 91,  96, 195,  63,  63);
-        sc->checkPixel( 96, 101, 195,  63,  63);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(91, 96);
+        sc->expectFGColor(96, 101);
+        sc->expectBGColor(145, 145);
     }
 
     SurfaceComposerClient::openGlobalTransaction();
@@ -454,10 +463,10 @@
     {
         SCOPED_TRACE("after setMatrix");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 24,  24,  63,  63, 195);
-        sc->checkPixel( 91,  96, 195,  63,  63);
-        sc->checkPixel( 96,  91,  63,  63, 195);
-        sc->checkPixel(145, 145,  63,  63, 195);
+        sc->expectBGColor(24, 24);
+        sc->expectFGColor(91, 96);
+        sc->expectBGColor(96, 91);
+        sc->expectBGColor(145, 145);
     }
 }
 
@@ -466,9 +475,9 @@
     {
         SCOPED_TRACE("before anything");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 32,  32,  63,  63, 195);
-        sc->checkPixel( 96,  96, 195,  63,  63);
-        sc->checkPixel(160, 160,  63,  63, 195);
+        sc->expectBGColor(32, 32);
+        sc->expectFGColor(96, 96);
+        sc->expectBGColor(160, 160);
     }
 
     // set up two deferred transactions on different frames
@@ -487,9 +496,9 @@
     {
         SCOPED_TRACE("before any trigger");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 32,  32,  63,  63, 195);
-        sc->checkPixel( 96,  96, 195,  63,  63);
-        sc->checkPixel(160, 160,  63,  63, 195);
+        sc->expectBGColor(32, 32);
+        sc->expectFGColor(96, 96);
+        sc->expectBGColor(160, 160);
     }
 
     // should trigger the first deferred transaction, but not the second one
@@ -497,9 +506,9 @@
     {
         SCOPED_TRACE("after first trigger");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 32,  32,  63,  63, 195);
-        sc->checkPixel( 96,  96, 162,  63,  96);
-        sc->checkPixel(160, 160,  63,  63, 195);
+        sc->expectBGColor(32, 32);
+        sc->checkPixel(96, 96, 162, 63, 96);
+        sc->expectBGColor(160, 160);
     }
 
     // should show up immediately since it's not deferred
@@ -512,9 +521,174 @@
     {
         SCOPED_TRACE("after second trigger");
         ScreenCapture::captureScreen(&sc);
-        sc->checkPixel( 32,  32,  63,  63, 195);
-        sc->checkPixel( 96,  96,  63,  63, 195);
-        sc->checkPixel(160, 160, 195,  63,  63);
+        sc->expectBGColor(32, 32);
+        sc->expectBGColor(96, 96);
+        sc->expectFGColor(160, 160);
+    }
+}
+
+class ChildLayerTest : public LayerUpdateTest {
+protected:
+    void SetUp() override {
+        LayerUpdateTest::SetUp();
+        mChild = mComposerClient->createSurface(
+                String8("Child surface"),
+                10, 10, PIXEL_FORMAT_RGBA_8888,
+                0, mFGSurfaceControl.get());
+        fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+        {
+            SCOPED_TRACE("before anything");
+            ScreenCapture::captureScreen(&mCapture);
+            mCapture->expectChildColor(64, 64);
+        }
+    }
+    void TearDown() override {
+        LayerUpdateTest::TearDown();
+        mChild = 0;
+    }
+
+    sp<SurfaceControl> mChild;
+    sp<ScreenCapture> mCapture;
+};
+
+TEST_F(ChildLayerTest, ChildLayerPositioning) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(10, 10);
+    mFGSurfaceControl->setPosition(64, 64);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Top left of foreground should now be at 0, 0
+        mCapture->expectFGColor(0, 0);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(10, 10);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerConstraints) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mFGSurfaceControl->setPosition(0, 0);
+    mChild->setPosition(63, 63);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectFGColor(0, 0);
+        // Last pixel in foreground should now be the child.
+        mCapture->expectChildColor(63, 63);
+        // But the child should be constrained and the next pixel
+        // must be the background
+        mCapture->expectBGColor(64, 64);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerScaling) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setPosition(0, 0);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    // Find the boundary between the parent and child
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectFGColor(10, 10);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    // The boundary should be twice as far from the origin now.
+    // The pixels from the last test should all be child now
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+        mCapture->expectChildColor(19, 19);
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(10, 10);
+    mFGSurfaceControl->setPosition(64, 64);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+    mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle());
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectFGColor(64, 64);
+        // In reparenting we should have exposed the entire foreground surface.
+        mCapture->expectFGColor(74, 74);
+        // And the child layer should now begin at 10, 10 (since the BG
+        // layer is at (0, 0)).
+        mCapture->expectBGColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+    }
+}
+
+TEST_F(ChildLayerTest, DetachChildren) {
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->show();
+    mChild->setPosition(10, 10);
+    mFGSurfaceControl->setPosition(64, 64);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mFGSurfaceControl->detachChildren();
+    SurfaceComposerClient::closeGlobalTransaction();
+
+    SurfaceComposerClient::openGlobalTransaction();
+    mChild->hide();
+    SurfaceComposerClient::closeGlobalTransaction();
+
+    // Nothing should have changed.
+    {
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectChildColor(74, 74);
+        mCapture->expectFGColor(84, 84);
     }
 }
 
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..0db909c
--- /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 := \
+	libperformance \
+	libpdx_default_transport \
+	libbufferhub
+
+sharedLibraries := \
+	libbase \
+	libcutils \
+	libhardware \
+	liblog \
+	libsync \
+	libutils \
+	libgui
+
+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..0906476
--- /dev/null
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -0,0 +1,483 @@
+#include "buffer_hub.h"
+
+#include <log/log.h>
+#include <poll.h>
+#include <utils/Trace.h>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+#include "producer_channel.h"
+#include "producer_queue_channel.h"
+
+using android::pdx::Channel;
+using android::pdx::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 << (" channel_id=" + std::to_string(channel->channel_id()));
+        stream << std::endl;
+        continue;
+      }
+
+      if (info.format == HAL_PIXEL_FORMAT_BLOB) {
+        std::string size = std::to_string(info.width) + " B";
+        stream << std::setw(14) << size;
+      } else {
+        std::string dimensions = std::to_string(info.width) + "x" +
+                                 std::to_string(info.height) + "x" +
+                                 std::to_string(info.slice_count);
+        stream << std::setw(14) << dimensions;
+      }
+      stream << " ";
+      stream << info.name;
+      stream << std::endl;
+    }
+  }
+
+  stream << std::endl;
+  stream << "Active Producer Queues:\n";
+  stream << std::right << std::setw(6) << "Id";
+  stream << std::right << std::setw(12) << " Allocated";
+  stream << std::right << std::setw(12) << " Consumers";
+  stream << " UsageSetMask";
+  stream << " UsageClearMask";
+  stream << " UsageDenySetMask";
+  stream << " UsageDenyClearMask";
+  stream << " ";
+  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");
+  ALOGD_IF(TRACE,
+           "BufferHubChannel::SignalAvailable: channel_id=%d buffer_id=%d",
+           channel_id(), buffer_id());
+  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");
+  ALOGD_IF(TRACE,
+           "BufferHubChannel::ClearAvailable: channel_id=%d buffer_id=%d",
+           channel_id(), buffer_id());
+  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");
+  ALOGD_IF(TRACE, "BufferHubChannel::Hangup: channel_id=%d buffer_id=%d",
+           channel_id(), buffer_id());
+  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..d4fc540
--- /dev/null
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -0,0 +1,37 @@
+#include <sched.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include <dvr/performance_client_api.h>
+#include <pdx/default_transport/service_dispatcher.h>
+
+#include "buffer_hub.h"
+
+int main(int, char**) {
+  int ret = -1;
+  std::shared_ptr<android::pdx::Service> service;
+  std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
+
+  // We need to be able to create endpoints with full perms.
+  umask(0000);
+
+  dispatcher = android::pdx::default_transport::ServiceDispatcher::Create();
+  CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n");
+
+  service = android::dvr::BufferHubService::Create();
+  CHECK_ERROR(!service, error, "Failed to create buffer hub service\n");
+  dispatcher->AddService(service);
+
+  ret = dvrSetSchedulerClass(0, "graphics");
+  CHECK_ERROR(ret < 0, error, "Failed to set thread priority");
+
+  ALOGI("Entering message loop.");
+
+  ret = dispatcher->EnterDispatchLoop();
+  CHECK_ERROR(ret < 0, error, "Dispatch loop exited because: %s\n",
+              strerror(-ret));
+
+error:
+  return -ret;
+}
diff --git a/services/vr/bufferhubd/bufferhubd.rc b/services/vr/bufferhubd/bufferhubd.rc
new file mode 100644
index 0000000..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..2264cef
--- /dev/null
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -0,0 +1,183 @@
+#include "consumer_channel.h"
+
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <thread>
+
+#include <private/dvr/bufferhub_rpc.h>
+#include "producer_channel.h"
+
+using android::pdx::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 buffer_id=%d",
+           channel_id(), buffer_id());
+
+  if (auto producer = GetProducer()) {
+    if (!handled_)  // Producer is waiting for our Release.
+      producer->OnConsumerIgnored();
+    producer->RemoveConsumer(this);
+  }
+}
+
+BufferHubChannel::BufferInfo ConsumerChannel::GetBufferInfo() const {
+  BufferHubChannel::BufferInfo info;
+  if (auto producer = GetProducer()) {
+    // If producer has not hung up, copy most buffer info from the producer.
+    info = producer->GetBufferInfo();
+  }
+  info.id = buffer_id();
+  return info;
+}
+
+std::shared_ptr<ProducerChannel> ConsumerChannel::GetProducer() const {
+  return std::static_pointer_cast<ProducerChannel>(producer_.lock());
+}
+
+void ConsumerChannel::HandleImpulse(Message& message) {
+  ATRACE_NAME("ConsumerChannel::HandleImpulse");
+  switch (message.GetOp()) {
+    case BufferHubRPC::ConsumerRelease::Opcode:
+      OnConsumerRelease(message, {});
+      break;
+  }
+}
+
+bool ConsumerChannel::HandleMessage(Message& message) {
+  ATRACE_NAME("ConsumerChannel::HandleMessage");
+  auto producer = GetProducer();
+  if (!producer)
+    REPLY_ERROR_RETURN(message, EPIPE, true);
+
+  switch (message.GetOp()) {
+    case BufferHubRPC::GetBuffer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
+          *producer, &ProducerChannel::OnGetBuffer, message);
+      return true;
+
+    case BufferHubRPC::GetBuffers::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::GetBuffers>(
+          *producer, &ProducerChannel::OnGetBuffers, message);
+      return true;
+
+    case BufferHubRPC::NewConsumer::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::NewConsumer>(
+          *producer, &ProducerChannel::OnNewConsumer, message);
+      return true;
+
+    case BufferHubRPC::ConsumerAcquire::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ConsumerAcquire>(
+          *this, &ConsumerChannel::OnConsumerAcquire, message);
+      return true;
+
+    case BufferHubRPC::ConsumerRelease::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ConsumerRelease>(
+          *this, &ConsumerChannel::OnConsumerRelease, message);
+      return true;
+
+    case BufferHubRPC::ConsumerSetIgnore::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(
+          *this, &ConsumerChannel::OnConsumerSetIgnore, message);
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+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..98a419f
--- /dev/null
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -0,0 +1,377 @@
+#include "producer_channel.h"
+
+#include <log/log.h>
+#include <sync/sync.h>
+#include <sys/poll.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <atomic>
+#include <thread>
+
+#include <private/dvr/bufferhub_rpc.h>
+#include "consumer_channel.h"
+
+using android::pdx::BorrowedHandle;
+using android::pdx::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 buffer_id=%d",
+           channel_id(), buffer_id());
+  for (auto consumer : consumer_channels_)
+    consumer->OnProducerClosed();
+}
+
+BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
+  return BufferInfo(buffer_id(), consumer_channels_.size(), slices_[0].width(),
+                    slices_[0].height(), slices_[0].format(),
+                    slices_[0].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..1a3723f
--- /dev/null
+++ b/services/vr/performanced/cpu_set.cpp
@@ -0,0 +1,287 @@
+#include "cpu_set.h"
+
+#include <log/log.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <android-base/file.h>
+
+#include "directory_reader.h"
+#include "stdio_filebuf.h"
+#include "task.h"
+#include "unique_file.h"
+
+namespace {
+
+constexpr int kDirectoryFlags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
+constexpr pid_t kKernelThreadDaemonPid = 2;
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+bool CpuSet::prefix_enabled_ = false;
+
+void CpuSetManager::Load(const std::string& cpuset_root) {
+  if (!root_set_)
+    root_set_ = Create(cpuset_root);
+}
+
+std::unique_ptr<CpuSet> CpuSetManager::Create(const std::string& path) {
+  base::unique_fd root_cpuset_fd(open(path.c_str(), kDirectoryFlags));
+  if (root_cpuset_fd.get() < 0) {
+    ALOGE("CpuSet::Create: Failed to open \"%s\": %s", path.c_str(),
+          strerror(errno));
+    return nullptr;
+  }
+
+  return Create(std::move(root_cpuset_fd), "/", nullptr);
+}
+
+std::unique_ptr<CpuSet> CpuSetManager::Create(base::unique_fd base_fd,
+                                              const std::string& name,
+                                              CpuSet* parent) {
+  DirectoryReader directory(base::unique_fd(dup(base_fd)));
+  if (!directory) {
+    ALOGE("CpuSet::Create: Failed to opendir %s cpuset: %s", name.c_str(),
+          strerror(directory.GetError()));
+    return nullptr;
+  }
+
+  std::unique_ptr<CpuSet> group(
+      new CpuSet(parent, name, base::unique_fd(dup(base_fd))));
+  path_map_.insert(std::make_pair(group->path(), group.get()));
+
+  while (dirent* entry = directory.Next()) {
+    if (entry->d_type == DT_DIR) {
+      std::string directory_name(entry->d_name);
+
+      if (directory_name == "." || directory_name == "..")
+        continue;
+
+      base::unique_fd entry_fd(
+          openat(base_fd.get(), directory_name.c_str(), kDirectoryFlags));
+      if (entry_fd.get() >= 0) {
+        auto child =
+            Create(std::move(entry_fd), directory_name.c_str(), group.get());
+
+        if (child)
+          group->AddChild(std::move(child));
+        else
+          return nullptr;
+      } else {
+        ALOGE("CpuSet::Create: Failed to openat \"%s\": %s", entry->d_name,
+              strerror(errno));
+        return nullptr;
+      }
+    }
+  }
+
+  return group;
+}
+
+CpuSet* CpuSetManager::Lookup(const std::string& path) {
+  auto search = path_map_.find(path);
+  if (search != path_map_.end())
+    return search->second;
+  else
+    return nullptr;
+}
+
+std::vector<CpuSet*> CpuSetManager::GetCpuSets() {
+  std::vector<CpuSet*> sets(path_map_.size());
+
+  for (const auto& pair : path_map_) {
+    sets.push_back(pair.second);
+  }
+
+  return sets;
+}
+
+std::string CpuSetManager::DumpState() const {
+  size_t max_path = 0;
+  std::vector<CpuSet*> sets;
+
+  for (const auto& pair : path_map_) {
+    max_path = std::max(max_path, pair.second->path().length());
+    sets.push_back(pair.second);
+  }
+
+  std::sort(sets.begin(), sets.end(), [](const CpuSet* a, const CpuSet* b) {
+    return a->path() < b->path();
+  });
+
+  std::ostringstream stream;
+
+  stream << std::left;
+  stream << std::setw(max_path) << "Path";
+  stream << " ";
+  stream << std::setw(6) << "CPUs";
+  stream << " ";
+  stream << std::setw(6) << "Tasks";
+  stream << std::endl;
+
+  stream << std::string(max_path, '_');
+  stream << " ";
+  stream << std::string(6, '_');
+  stream << " ";
+  stream << std::string(6, '_');
+  stream << std::endl;
+
+  for (const auto set : sets) {
+    stream << std::left;
+    stream << std::setw(max_path) << set->path();
+    stream << " ";
+    stream << std::right;
+    stream << std::setw(6) << set->GetCpuList();
+    stream << " ";
+    stream << std::setw(6) << set->GetTasks().size();
+    stream << std::endl;
+  }
+
+  return stream.str();
+}
+
+void CpuSetManager::MoveUnboundTasks(const std::string& target_set) {
+  auto root = Lookup("/");
+  if (!root) {
+    ALOGE("CpuSetManager::MoveUnboundTasks: Failed to find root cpuset!");
+    return;
+  }
+
+  auto target = Lookup(target_set);
+  if (!target) {
+    ALOGE(
+        "CpuSetManager::MoveUnboundTasks: Failed to find target cpuset \"%s\"!",
+        target_set.c_str());
+    return;
+  }
+
+  auto cpu_list = root->GetCpuList();
+
+  for (auto task_id : root->GetTasks()) {
+    Task task(task_id);
+
+    // Move only unbound kernel threads to the target cpuset.
+    if (task.cpus_allowed_list() == cpu_list &&
+        task.parent_process_id() == kKernelThreadDaemonPid) {
+      ALOGD_IF(TRACE,
+               "CpuSetManager::MoveUnboundTasks: Moving task_id=%d name=%s to "
+               "target_set=%s tgid=%d ppid=%d.",
+               task_id, task.name().c_str(), target_set.c_str(),
+               task.thread_group_id(), task.parent_process_id());
+
+      const int ret = target->AttachTask(task_id);
+      ALOGW_IF(ret < 0 && ret != -EINVAL,
+               "CpuSetManager::MoveUnboundTasks: Failed to attach task_id=%d "
+               "to cpuset=%s: %s",
+               task_id, target_set.c_str(), strerror(-ret));
+    } else {
+      ALOGD_IF(TRACE,
+               "CpuSet::MoveUnboundTasks: Skipping task_id=%d name=%s cpus=%s.",
+               task_id, task.name().c_str(), task.cpus_allowed_list().c_str());
+    }
+  }
+}
+
+CpuSet::CpuSet(CpuSet* parent, const std::string& name,
+               base::unique_fd&& cpuset_fd)
+    : parent_(parent), name_(name), cpuset_fd_(std::move(cpuset_fd)) {
+  if (parent_ == nullptr)
+    path_ = name_;
+  else if (parent_->IsRoot())
+    path_ = parent_->name() + name_;
+  else
+    path_ = parent_->path() + "/" + name_;
+
+  ALOGI("CpuSet::CpuSet: path=%s", path().c_str());
+}
+
+base::unique_fd CpuSet::OpenPropertyFile(const std::string& name) const {
+  return OpenFile(prefix_enabled_ ? "cpuset." + name : name);
+}
+
+UniqueFile CpuSet::OpenPropertyFilePointer(const std::string& name) const {
+  return OpenFilePointer(prefix_enabled_ ? "cpuset." + name : name);
+}
+
+base::unique_fd CpuSet::OpenFile(const std::string& name, int flags) const {
+  const std::string relative_path = "./" + name;
+  return base::unique_fd(
+      openat(cpuset_fd_.get(), relative_path.c_str(), flags));
+}
+
+UniqueFile CpuSet::OpenFilePointer(const std::string& name, int flags) const {
+  const std::string relative_path = "./" + name;
+  base::unique_fd fd(openat(cpuset_fd_.get(), relative_path.c_str(), flags));
+  if (fd.get() < 0) {
+    ALOGE("CpuSet::OpenPropertyFilePointer: Failed to open %s/%s: %s",
+          path_.c_str(), name.c_str(), strerror(errno));
+    return nullptr;
+  }
+
+  UniqueFile fp(fdopen(fd.release(), "r"));
+  if (!fp)
+    ALOGE("CpuSet::OpenPropertyFilePointer: Failed to fdopen %s/%s: %s",
+          path_.c_str(), name.c_str(), strerror(errno));
+
+  return fp;
+}
+
+int CpuSet::AttachTask(pid_t task_id) const {
+  auto file = OpenFile("tasks", O_RDWR);
+  if (file.get() >= 0) {
+    std::ostringstream stream;
+    stream << task_id;
+    std::string value = stream.str();
+
+    const bool ret = base::WriteStringToFd(value, file.get());
+    return !ret ? -errno : 0;
+  } else {
+    ALOGE("CpuSet::AttachTask: Failed to open %s/tasks: %s", path_.c_str(),
+          strerror(errno));
+    return -errno;
+  }
+}
+
+std::vector<pid_t> CpuSet::GetTasks() const {
+  std::vector<pid_t> tasks;
+
+  if (auto file = OpenFilePointer("tasks")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    for (std::string line; std::getline(file_stream, line);) {
+      pid_t task_id = std::strtol(line.c_str(), nullptr, 10);
+      tasks.push_back(task_id);
+    }
+  }
+
+  return tasks;
+}
+
+std::string CpuSet::GetCpuList() const {
+  if (auto file = OpenPropertyFilePointer("cpus")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    std::string line;
+    if (std::getline(file_stream, line))
+      return line;
+  }
+
+  ALOGE("CpuSet::GetCpuList: Failed to read cpu list!!!");
+  return "";
+}
+
+void CpuSet::AddChild(std::unique_ptr<CpuSet> child) {
+  children_.push_back(std::move(child));
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h
new file mode 100644
index 0000000..fcf280a
--- /dev/null
+++ b/services/vr/performanced/cpu_set.h
@@ -0,0 +1,105 @@
+#ifndef ANDROID_DVR_PERFORMANCED_CPU_SET_H_
+#define ANDROID_DVR_PERFORMANCED_CPU_SET_H_
+
+#include <fcntl.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "unique_file.h"
+
+namespace android {
+namespace dvr {
+
+class CpuSet {
+ public:
+  // Returns the parent group for this group, if any. This pointer is owned by
+  // the group hierarchy and is only valid as long as the hierarchy is valid.
+  CpuSet* parent() const { return parent_; }
+  std::string name() const { return name_; }
+  std::string path() const { return path_; }
+
+  bool IsRoot() const { return parent_ == nullptr; }
+
+  std::string GetCpuList() const;
+
+  int AttachTask(pid_t task_id) const;
+  std::vector<pid_t> GetTasks() const;
+
+ private:
+  friend class CpuSetManager;
+
+  CpuSet(CpuSet* parent, const std::string& name, base::unique_fd&& cpuset_fd);
+
+  void AddChild(std::unique_ptr<CpuSet> child);
+
+  base::unique_fd OpenPropertyFile(const std::string& name) const;
+  UniqueFile OpenPropertyFilePointer(const std::string& name) const;
+
+  base::unique_fd OpenFile(const std::string& name, int flags = O_RDONLY) const;
+  UniqueFile OpenFilePointer(const std::string& name,
+                             int flags = O_RDONLY) const;
+
+  CpuSet* parent_;
+  std::string name_;
+  std::string path_;
+  base::unique_fd cpuset_fd_;
+  std::vector<std::unique_ptr<CpuSet>> children_;
+
+  static void SetPrefixEnabled(bool enabled) { prefix_enabled_ = enabled; }
+  static bool prefix_enabled_;
+
+  CpuSet(const CpuSet&) = delete;
+  void operator=(const CpuSet&) = delete;
+};
+
+class CpuSetManager {
+ public:
+  CpuSetManager() {}
+
+  // Creats a CpuSet hierarchy by walking the directory tree starting at
+  // |cpuset_root|. This argument must be the path to the root cpuset for the
+  // system, which is usually /dev/cpuset.
+  void Load(const std::string& cpuset_root);
+
+  // Lookup and return a CpuSet from a cpuset path. Ownership of the pointer
+  // DOES NOT pass to the caller; the pointer remains valid as long as the
+  // CpuSet hierarchy is valid.
+  CpuSet* Lookup(const std::string& path);
+
+  // Returns a vector of all the cpusets found at initializaiton. Ownership of
+  // the pointers to CpuSets DOES NOT pass to the caller; the pointers remain
+  // valid as long as the CpuSet hierarchy is valid.
+  std::vector<CpuSet*> GetCpuSets();
+
+  // Moves all unbound tasks from the root set into the target set. This is used
+  // to shield the system from interference from unbound kernel threads.
+  void MoveUnboundTasks(const std::string& target_set);
+
+  std::string DumpState() const;
+
+  operator bool() const { return root_set_ != nullptr; }
+
+ private:
+  // Creates a CpuSet from a path to a cpuset cgroup directory. Recursively
+  // creates child groups for each directory found under |path|.
+  std::unique_ptr<CpuSet> Create(const std::string& path);
+  std::unique_ptr<CpuSet> Create(base::unique_fd base_fd,
+                                 const std::string& name, CpuSet* parent);
+
+  std::unique_ptr<CpuSet> root_set_;
+  std::unordered_map<std::string, CpuSet*> path_map_;
+
+  CpuSetManager(const CpuSetManager&) = delete;
+  void operator=(const CpuSetManager&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_CPU_SET_H_
diff --git a/services/vr/performanced/directory_reader.h b/services/vr/performanced/directory_reader.h
new file mode 100644
index 0000000..7d7ecc5
--- /dev/null
+++ b/services/vr/performanced/directory_reader.h
@@ -0,0 +1,55 @@
+#ifndef ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
+#define ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace dvr {
+
+// Utility class around readdir() that handles automatic cleanup.
+class DirectoryReader {
+ public:
+  explicit DirectoryReader(base::unique_fd directory_fd) {
+    directory_ = fdopendir(directory_fd.get());
+    error_ = errno;
+    if (directory_ != nullptr)
+      directory_fd.release();
+  }
+
+  ~DirectoryReader() {
+    if (directory_)
+      closedir(directory_);
+  }
+
+  bool IsValid() const { return directory_ != nullptr; }
+  explicit operator bool() const { return IsValid(); }
+  int GetError() const { return error_; }
+
+  // Returns a pointer to a dirent describing the next directory entry. The
+  // pointer is only valid unitl the next call to Next() or the DirectoryReader
+  // is destroyed. Returns nullptr when the end of the directory is reached.
+  dirent* Next() {
+    if (directory_)
+      return readdir(directory_);
+    else
+      return nullptr;
+  }
+
+ private:
+  DIR* directory_;
+  int error_;
+
+  DirectoryReader(const DirectoryReader&) = delete;
+  void operator=(const DirectoryReader&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_DIRECTORY_READER_H_
diff --git a/services/vr/performanced/main.cpp b/services/vr/performanced/main.cpp
new file mode 100644
index 0000000..ca66c71
--- /dev/null
+++ b/services/vr/performanced/main.cpp
@@ -0,0 +1,76 @@
+#include <errno.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <log/log.h>
+#include <sys/resource.h>
+#include <utils/threads.h>
+
+#include <pdx/default_transport/service_dispatcher.h>
+#include <private/android_filesystem_config.h>
+
+#include "performance_service.h"
+
+namespace {
+
+// Annoying that sys/capability.h doesn't define this directly.
+constexpr int kMaxCapNumber = (CAP_TO_INDEX(CAP_LAST_CAP) + 1);
+
+}  // anonymous namespace
+
+int main(int /*argc*/, char** /*argv*/) {
+  int ret = -1;
+
+  struct __user_cap_header_struct capheader;
+  struct __user_cap_data_struct capdata[kMaxCapNumber];
+
+  std::shared_ptr<android::pdx::Service> service;
+  std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher;
+
+  ALOGI("Starting up...");
+
+  // We need to be able to create endpoints with full perms.
+  umask(0000);
+
+  // Keep capabilities when switching UID to AID_SYSTEM.
+  ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+  CHECK_ERROR(ret < 0, error, "Failed to set KEEPCAPS: %s", strerror(errno));
+
+  // Set UID and GID to system.
+  ret = setresgid(AID_SYSTEM, AID_SYSTEM, AID_SYSTEM);
+  CHECK_ERROR(ret < 0, error, "Failed to set GID: %s", strerror(errno));
+  ret = setresuid(AID_SYSTEM, AID_SYSTEM, AID_SYSTEM);
+  CHECK_ERROR(ret < 0, error, "Failed to set UID: %s", strerror(errno));
+
+  // Keep CAP_SYS_NICE, allowing control of scheduler class, priority, and
+  // cpuset for other tasks in the system.
+  memset(&capheader, 0, sizeof(capheader));
+  memset(&capdata, 0, sizeof(capdata));
+  capheader.version = _LINUX_CAPABILITY_VERSION_3;
+  capdata[CAP_TO_INDEX(CAP_SYS_NICE)].effective |= CAP_TO_MASK(CAP_SYS_NICE);
+  capdata[CAP_TO_INDEX(CAP_SYS_NICE)].permitted |= CAP_TO_MASK(CAP_SYS_NICE);
+
+  // Drop all caps but the ones configured above.
+  ret = capset(&capheader, capdata);
+  CHECK_ERROR(ret < 0, error, "Could not set capabilities: %s",
+              strerror(errno));
+
+  dispatcher = android::pdx::default_transport::ServiceDispatcher::Create();
+  CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher.");
+
+  service = android::dvr::PerformanceService::Create();
+  CHECK_ERROR(!service, error, "Failed to create performance service service.");
+  dispatcher->AddService(service);
+
+  ALOGI("Entering message loop.");
+
+  ret = dispatcher->EnterDispatchLoop();
+  CHECK_ERROR(ret < 0, error, "Dispatch loop exited because: %s\n",
+              strerror(-ret));
+
+error:
+  return ret;
+}
diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp
new file mode 100644
index 0000000..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..1175a7b
--- /dev/null
+++ b/services/vr/performanced/task.cpp
@@ -0,0 +1,163 @@
+#include "task.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <log/log.h>
+#include <stdio.h>
+
+#include <cctype>
+#include <cstdlib>
+#include <memory>
+#include <sstream>
+
+#include <android-base/unique_fd.h>
+
+#include "stdio_filebuf.h"
+#include "string_trim.h"
+
+namespace {
+
+const char kProcBase[] = "/proc";
+
+android::base::unique_fd OpenTaskDirectory(pid_t task_id) {
+  std::ostringstream stream;
+  stream << kProcBase << "/" << task_id;
+
+  return android::base::unique_fd(
+      open(stream.str().c_str(), O_RDONLY | O_DIRECTORY));
+}
+
+void ParseUidStatusField(const std::string& value, std::array<int, 4>& ids) {
+  const char* start = value.c_str();
+
+  ids[0] = std::strtol(start, const_cast<char**>(&start), 10);
+  ids[1] = std::strtol(start, const_cast<char**>(&start), 10);
+  ids[2] = std::strtol(start, const_cast<char**>(&start), 10);
+  ids[3] = std::strtol(start, const_cast<char**>(&start), 10);
+}
+
+}  // anonymous namespace
+
+namespace android {
+namespace dvr {
+
+Task::Task(pid_t task_id)
+    : task_id_(task_id),
+      thread_group_id_(-1),
+      parent_process_id_(-1),
+      thread_count_(0),
+      cpus_allowed_mask_(0) {
+  task_fd_ = OpenTaskDirectory(task_id_);
+  ALOGE_IF(task_fd_.get() < 0,
+           "Task::Task: Failed to open task directory for task_id=%d: %s",
+           task_id, strerror(errno));
+
+  ReadStatusFields();
+
+  ALOGD_IF(TRACE, "Task::Task: task_id=%d name=%s tgid=%d ppid=%d cpu_mask=%x",
+           task_id_, name_.c_str(), thread_group_id_, parent_process_id_,
+           cpus_allowed_mask_);
+}
+
+base::unique_fd Task::OpenTaskFile(const std::string& name) const {
+  const std::string relative_path = "./" + name;
+  return base::unique_fd(
+      openat(task_fd_.get(), relative_path.c_str(), O_RDONLY));
+}
+
+UniqueFile Task::OpenTaskFilePointer(const std::string& name) const {
+  const std::string relative_path = "./" + name;
+  base::unique_fd fd(openat(task_fd_.get(), relative_path.c_str(), O_RDONLY));
+  if (fd.get() < 0) {
+    ALOGE("Task::OpenTaskFilePointer: Failed to open /proc/%d/%s: %s", task_id_,
+          name.c_str(), strerror(errno));
+    return nullptr;
+  }
+
+  UniqueFile fp(fdopen(fd.release(), "r"));
+  if (!fp)
+    ALOGE("Task::OpenTaskFilePointer: Failed to fdopen /proc/%d/%s: %s",
+          task_id_, name.c_str(), strerror(errno));
+
+  return fp;
+}
+
+std::string Task::GetStatusField(const std::string& field) const {
+  if (auto file = OpenTaskFilePointer("status")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    for (std::string line; std::getline(file_stream, line);) {
+      auto offset = line.find(field);
+
+      ALOGD_IF(TRACE,
+               "Task::GetStatusField: field=\"%s\" line=\"%s\" offset=%zd",
+               field.c_str(), line.c_str(), offset);
+
+      if (offset == std::string::npos)
+        continue;
+
+      // The status file has lines with the format <field>:<value>. Extract the
+      // value after the colon.
+      return Trim(line.substr(offset + field.size() + 1));
+    }
+  }
+
+  return "[unknown]";
+}
+
+void Task::ReadStatusFields() {
+  if (auto file = OpenTaskFilePointer("status")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    for (std::string line; std::getline(file_stream, line);) {
+      auto offset = line.find(":");
+      if (offset == std::string::npos) {
+        ALOGW("ReadStatusFields: Failed to find delimiter \":\" in line=\"%s\"",
+              line.c_str());
+        continue;
+      }
+
+      std::string key = line.substr(0, offset);
+      std::string value = Trim(line.substr(offset + 1));
+
+      ALOGD_IF(TRACE, "Task::ReadStatusFields: key=\"%s\" value=\"%s\"",
+               key.c_str(), value.c_str());
+
+      if (key == "Name")
+        name_ = value;
+      else if (key == "Tgid")
+        thread_group_id_ = std::strtol(value.c_str(), nullptr, 10);
+      else if (key == "PPid")
+        parent_process_id_ = std::strtol(value.c_str(), nullptr, 10);
+      else if (key == "Uid")
+        ParseUidStatusField(value, user_id_);
+      else if (key == "Gid")
+        ParseUidStatusField(value, group_id_);
+      else if (key == "Threads")
+        thread_count_ = std::strtoul(value.c_str(), nullptr, 10);
+      else if (key == "Cpus_allowed")
+        cpus_allowed_mask_ = std::strtoul(value.c_str(), nullptr, 16);
+      else if (key == "Cpus_allowed_list")
+        cpus_allowed_list_ = value;
+    }
+  }
+}
+
+std::string Task::GetCpuSetPath() const {
+  if (auto file = OpenTaskFilePointer("cpuset")) {
+    stdio_filebuf<char> filebuf(file.get());
+    std::istream file_stream(&filebuf);
+
+    std::string line = "";
+    std::getline(file_stream, line);
+
+    return Trim(line);
+  } else {
+    return "";
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/performanced/task.h b/services/vr/performanced/task.h
new file mode 100644
index 0000000..4a3b7f2
--- /dev/null
+++ b/services/vr/performanced/task.h
@@ -0,0 +1,83 @@
+#ifndef ANDROID_DVR_PERFORMANCED_TASK_H_
+#define ANDROID_DVR_PERFORMANCED_TASK_H_
+
+#include <sys/types.h>
+
+#include <array>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "unique_file.h"
+
+namespace android {
+namespace dvr {
+
+// Task provides access to task-related information from the procfs
+// pseudo-filesystem.
+class Task {
+ public:
+  explicit Task(pid_t task_id);
+
+  bool IsValid() const { return task_fd_.get() >= 0; }
+  explicit operator bool() const { return IsValid(); }
+
+  pid_t task_id() const { return task_id_; }
+  std::string name() const { return name_; }
+  pid_t thread_group_id() const { return thread_group_id_; }
+  pid_t parent_process_id() const { return parent_process_id_; }
+  size_t thread_count() const { return thread_count_; }
+  uint32_t cpus_allowed_mask() const { return cpus_allowed_mask_; }
+  const std::string& cpus_allowed_list() const { return cpus_allowed_list_; }
+  const std::array<int, 4>& user_id() const { return user_id_; }
+  const std::array<int, 4>& group_id() const { return group_id_; }
+
+  // Indices into user and group id arrays.
+  enum {
+    kUidReal = 0,
+    kUidEffective,
+    kUidSavedSet,
+    kUidFilesystem,
+  };
+
+  std::string GetCpuSetPath() const;
+
+ private:
+  pid_t task_id_;
+  base::unique_fd task_fd_;
+
+  // Fields read from /proc/<task_id_>/status.
+  std::string name_;
+  pid_t thread_group_id_;
+  pid_t parent_process_id_;
+  std::array<int, 4> user_id_;
+  std::array<int, 4> group_id_;
+  size_t thread_count_;
+  uint32_t cpus_allowed_mask_;
+  std::string cpus_allowed_list_;
+
+  // Opens the file /proc/<task_id_>/|name| and returns the open file
+  // descriptor.
+  base::unique_fd OpenTaskFile(const std::string& name) const;
+
+  // Similar to OpenTaskFile() but returns a file pointer.
+  UniqueFile OpenTaskFilePointer(const std::string& name) const;
+
+  // Reads the field named |field| from /proc/<task_id_>/status.
+  std::string GetStatusField(const std::string& field) const;
+
+  // Reads a subset of the fields in /proc/<task_id_>/status.
+  void ReadStatusFields();
+
+  Task(const Task&) = delete;
+  void operator=(const Task&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_TASK_H_
diff --git a/services/vr/performanced/unique_file.h b/services/vr/performanced/unique_file.h
new file mode 100644
index 0000000..86e487a
--- /dev/null
+++ b/services/vr/performanced/unique_file.h
@@ -0,0 +1,20 @@
+#ifndef ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
+#define ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
+
+#include <stdio.h>
+
+#include <memory>
+
+namespace android {
+namespace dvr {
+
+// Utility to manage the lifetime of a file pointer.
+struct FileDeleter {
+  void operator()(FILE* fp) { fclose(fp); }
+};
+using UniqueFile = std::unique_ptr<FILE, FileDeleter>;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_PERFORMANCED_UNIQUE_FILE_H_
diff --git a/services/vr/sensord/Android.mk b/services/vr/sensord/Android.mk
new file mode 100644
index 0000000..e213bd6
--- /dev/null
+++ b/services/vr/sensord/Android.mk
@@ -0,0 +1,69 @@
+# 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 \
+	libposepredictor \
+
+sharedLibraries := \
+	libandroid \
+	libbase \
+	libbinder \
+	libcutils \
+	liblog \
+	libhardware \
+	libutils \
+
+cFlags := -DLOG_TAG=\"sensord\" \
+          -DTRACE=0
+
+include $(CLEAR_VARS)
+# Don't strip symbols so we see stack traces in logcat.
+LOCAL_STRIP_MODULE := false
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_CFLAGS := $(cFlags)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := sensord
+LOCAL_C_INCLUDES := $(includeFiles)
+LOCAL_C_INCLUDES += \
+    $(call local-generated-sources-dir)/proto/frameworks/native/services/vr/sensord
+LOCAL_INIT_RC := sensord.rc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_STATIC_LIBRARIES := $(staticLibraries)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_SRC_FILES := test/poselatencytest.cpp
+LOCAL_MODULE := poselatencytest
+include $(BUILD_EXECUTABLE)
diff --git a/services/vr/sensord/pose_service.cpp b/services/vr/sensord/pose_service.cpp
new file mode 100644
index 0000000..32a2160
--- /dev/null
+++ b/services/vr/sensord/pose_service.cpp
@@ -0,0 +1,657 @@
+#define ATRACE_TAG ATRACE_TAG_INPUT
+#include "pose_service.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <time.h>
+
+#include <array>
+#include <cmath>
+#include <cstdint>
+#include <sstream>
+#include <type_traits>
+
+#include <cutils/properties.h>
+#include <cutils/trace.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <hardware/sensors.h>
+#include <log/log.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <private/dvr/benchmark.h>
+#include <private/dvr/clock_ns.h>
+#include <private/dvr/platform_defines.h>
+#include <private/dvr/pose-ipc.h>
+#include <private/dvr/sensor_constants.h>
+#include <utils/Trace.h>
+
+using android::pdx::LocalChannelHandle;
+using android::pdx::default_transport::Endpoint;
+using android::pdx::Status;
+
+namespace android {
+namespace dvr {
+
+using Vector3d = vec3d;
+using Rotationd = quatd;
+using AngleAxisd = Eigen::AngleAxis<double>;
+
+namespace {
+// Wait a few seconds before checking if we need to disable sensors.
+static constexpr int64_t kSensorTimeoutNs = 5000000000ll;
+
+static constexpr float kTwoPi = 2.0 * M_PI;
+static constexpr float kDegToRad = M_PI / 180.f;
+
+// Head model code data.
+static constexpr float kDefaultNeckHorizontalOffset = 0.080f;  // meters
+static constexpr float kDefaultNeckVerticalOffset = 0.075f;    // meters
+
+static constexpr char kDisablePosePredictionProp[] =
+    "persist.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";
+static constexpr char kPredictorTypeProp[] = "dvr.predictor_type";
+
+// Persistent buffer names.
+static constexpr char kPoseRingBufferName[] = "PoseService:RingBuffer";
+
+static constexpr int kDatasetIdLength = 36;
+static constexpr char kDatasetIdChars[] = "0123456789abcdef-";
+
+// These are the flags used by BufferProducer::CreatePersistentUncachedBlob,
+// plus PRIVATE_ADSP_HEAP to allow access from the DSP.
+static constexpr int kPoseRingBufferFlags =
+    GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY |
+    GRALLOC_USAGE_PRIVATE_UNCACHED | GRALLOC_USAGE_PRIVATE_ADSP_HEAP;
+
+std::string GetPoseModeString(DvrPoseMode mode) {
+  switch (mode) {
+    case DVR_POSE_MODE_6DOF:
+      return "DVR_POSE_MODE_6DOF";
+    case DVR_POSE_MODE_3DOF:
+      return "DVR_POSE_MODE_3DOF";
+    case DVR_POSE_MODE_MOCK_FROZEN:
+      return "DVR_POSE_MODE_MOCK_FROZEN";
+    case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+      return "DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW";
+    case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+      return "DVR_POSE_MODE_MOCK_HEAD_TURN_FAST";
+    case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+      return "DVR_POSE_MODE_MOCK_ROTATE_SLOW";
+    case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+      return "DVR_POSE_MODE_MOCK_ROTATE_MEDIUM";
+    case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+      return "DVR_POSE_MODE_MOCK_ROTATE_FAST";
+    case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE:
+      return "DVR_POSE_MODE_MOCK_CIRCLE_STRAFE";
+    default:
+      return "Unknown pose mode";
+  }
+}
+
+}  // namespace
+
+PoseService::PoseService(SensorThread* sensor_thread)
+    : BASE("PoseService", Endpoint::Create(DVR_POSE_SERVICE_CLIENT)),
+      sensor_thread_(sensor_thread),
+      last_sensor_usage_time_ns_(0),
+      watchdog_shutdown_(false),
+      sensors_on_(false),
+      accelerometer_index_(-1),
+      gyroscope_index_(-1),
+      pose_mode_(DVR_POSE_MODE_6DOF),
+      mapped_pose_buffer_(nullptr),
+      vsync_count_(0),
+      photon_timestamp_(0),
+      // Will be updated by external service, but start with a non-zero value:
+      display_period_ns_(16000000) {
+  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());
+    }
+  }
+
+  switch (property_get_int32(kPredictorTypeProp, 0)) {
+    case 1:
+      pose_predictor_ = posepredictor::Predictor::Create(
+          posepredictor::PredictorType::Quadric);
+    default:
+      pose_predictor_ = posepredictor::Predictor::Create(
+          posepredictor::PredictorType::Linear);
+  }
+
+  enable_pose_recording_ = property_get_bool(kEnablePoseRecordProp, 0) == 1;
+
+  SetPoseMode(DVR_POSE_MODE_6DOF);
+}
+
+PoseService::~PoseService() {
+  if (watchdog_thread_.get_id() != std::thread::id()) {
+    {
+      std::lock_guard<std::mutex> guard(mutex_);
+      watchdog_shutdown_ = true;
+      watchdog_condition_.notify_one();
+    }
+    watchdog_thread_.join();
+  }
+}
+
+void PoseService::KickSensorWatchDogThread() {
+  // This method is called every frame while rendering so we want to make sure
+  // it is very light weight with synchronization.
+  // TODO(jbates) For better performance, we can consider a lock-free atomic
+  // solution instead of locking this mutex.
+
+  // Update the usage time. The watchdog thread will poll this value to know
+  // when to disable sensors.
+  last_sensor_usage_time_ns_ = GetSystemClockNs();
+
+  // If sensors are still on, there's nothing else to do.
+  if (sensors_on_)
+    return;
+
+  // Enable sensors.
+  ALOGI("Start using sensors.");
+  sensors_on_ = true;
+  if (accelerometer_index_ >= 0) {
+    sensor_thread_->StartUsingSensor(accelerometer_index_);
+  }
+  if (gyroscope_index_ >= 0) {
+    sensor_thread_->StartUsingSensor(gyroscope_index_);
+  }
+
+  // Tell the thread to wake up to disable the sensors when no longer needed.
+  watchdog_condition_.notify_one();
+
+  if (watchdog_thread_.get_id() == std::thread::id()) {
+    // The sensor watchdog thread runs while sensors are in use. When no APIs
+    // have requested sensors beyond a threshold (5 seconds), sensors are
+    // disabled.
+    watchdog_thread_ = std::thread([this] {
+      std::unique_lock<std::mutex> lock(mutex_);
+      while (!watchdog_shutdown_) {
+        int64_t remaining_sensor_time_ns =
+            last_sensor_usage_time_ns_ + kSensorTimeoutNs - GetSystemClockNs();
+
+        if (remaining_sensor_time_ns > 0) {
+          // Wait for the remaining usage time before checking again.
+          watchdog_condition_.wait_for(
+              lock, std::chrono::nanoseconds(remaining_sensor_time_ns));
+          continue;
+        }
+
+        if (sensors_on_) {
+          // Disable sensors.
+          ALOGI("Stop using sensors.");
+          sensors_on_ = false;
+          if (accelerometer_index_ >= 0) {
+            sensor_thread_->StopUsingSensor(accelerometer_index_);
+          }
+          if (gyroscope_index_ >= 0) {
+            sensor_thread_->StopUsingSensor(gyroscope_index_);
+          }
+        }
+
+        // Wait for sensors to be enabled again.
+        watchdog_condition_.wait(lock);
+      }
+    });
+  }
+}
+
+bool PoseService::IsInitialized() const {
+  return BASE::IsInitialized() && ring_buffer_ && mapped_pose_buffer_;
+}
+
+void PoseService::WriteAsyncPoses(const Vector3d& start_t_head,
+                                  const Rotationd& start_q_head,
+                                  int64_t pose_timestamp) {
+  if (enable_external_pose_) {
+    return;
+  }
+
+  // If playing back data, the timestamps are different enough from the
+  // current time that prediction doesn't work. This hack pretends that
+  // there was one nanosecond of latency between the sensors and here.
+  if (enable_sensor_playback_)
+    pose_timestamp = GetSystemClockNs() - 1;
+
+  // Feed the sample to the predictor
+  AddPredictorPose(pose_predictor_.get(), start_t_head, start_q_head,
+                   pose_timestamp, &last_known_pose_);
+
+  // Store one extra value, because the application is working on the next
+  // frame and expects the minimum count from that frame on.
+  for (uint32_t i = 0; i < kPoseAsyncBufferMinFutureCount + 1; ++i) {
+    int64_t target_time = photon_timestamp_ + i * display_period_ns_;
+
+    // TODO(jbates, cwolfe) For the DSP code, we may still want poses even when
+    // the vsyncs are not ticking up. But it's important not to update the pose
+    // data that's in the past so that applications have the most accurate
+    // estimate of the last frame's *actual* pose, so that they can update
+    // simulations and calculate collisions, etc.
+    if (target_time < pose_timestamp) {
+      // Already in the past, do not update this head pose slot.
+      continue;
+    }
+
+    // Write to the actual shared memory ring buffer.
+    uint32_t index = ((vsync_count_ + i) & kPoseAsyncBufferIndexMask);
+
+    // Make a pose prediction
+    if (enable_pose_prediction_) {
+      PredictPose(pose_predictor_.get(), target_time,
+                  target_time + right_eye_photon_offset_ns_,
+                  mapped_pose_buffer_->ring + index);
+    } else {
+      mapped_pose_buffer_->ring[index] = last_known_pose_;
+    }
+  }
+}
+
+void PoseService::UpdatePoseMode() {
+  ALOGI_IF(TRACE, "UpdatePoseMode: %f %f %f", last_known_pose_.translation[0],
+           last_known_pose_.translation[1], last_known_pose_.translation[2]);
+
+  const int64_t current_time_ns = GetSystemClockNs();
+
+  const PoseState pose_state = sensor_fusion_.GetLatestPoseState();
+
+  switch (pose_mode_) {
+    case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+    case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+    case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+    case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+    case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+    case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE: {
+      // Calculate a pose based on monotic system time.
+      const Vector3d y_axis(0., 1., 0.);
+      double time_s = current_time_ns / 1e9;
+
+      // Generate fake yaw data.
+      float yaw = 0.0f;
+      Vector3d head_trans(0.0, 0.0, 0.0);
+      switch (pose_mode_) {
+        default:
+        case DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW:
+          // Pan across 120 degrees in 15 seconds.
+          yaw = std::cos(kTwoPi * time_s / 15.0) * 60.0 * kDegToRad;
+          break;
+        case DVR_POSE_MODE_MOCK_HEAD_TURN_FAST:
+          // Pan across 120 degrees in 4 seconds.
+          yaw = std::cos(kTwoPi * time_s / 4.0) * 60.0 * kDegToRad;
+          break;
+        case DVR_POSE_MODE_MOCK_ROTATE_SLOW:
+          // Rotate 5 degrees per second.
+          yaw = std::fmod(time_s * 5.0 * kDegToRad, kTwoPi);
+          break;
+        case DVR_POSE_MODE_MOCK_ROTATE_MEDIUM:
+          // Rotate 30 degrees per second.
+          yaw = std::fmod(time_s * 30.0 * kDegToRad, kTwoPi);
+          break;
+        case DVR_POSE_MODE_MOCK_ROTATE_FAST:
+          // Rotate 90 degrees per second.
+          yaw = std::fmod(time_s * 90.0 * kDegToRad, kTwoPi);
+          break;
+        case DVR_POSE_MODE_MOCK_CIRCLE_STRAFE:
+          // Circle strafe around origin at distance of 3 meters.
+          yaw = std::fmod(time_s * 30.0 * kDegToRad, kTwoPi);
+          head_trans += 3.0 * Vector3d(sin(yaw), 0.0, cos(yaw));
+          break;
+      }
+
+      // Calculate the simulated head rotation in an absolute "head" space.
+      // This space is not related to start space and doesn't need a
+      // reference.
+      Rotationd head_rotation_in_head_space(AngleAxisd(yaw, y_axis));
+
+      WriteAsyncPoses(head_trans, head_rotation_in_head_space, current_time_ns);
+      break;
+    }
+    case DVR_POSE_MODE_MOCK_FROZEN: {
+      // Even when frozen, we still provide a current timestamp, because
+      // consumers may rely on it being monotonic.
+
+      Rotationd start_from_head_rotation(
+          frozen_state_.head_from_start_rotation.w,
+          frozen_state_.head_from_start_rotation.x,
+          frozen_state_.head_from_start_rotation.y,
+          frozen_state_.head_from_start_rotation.z);
+      Vector3d head_from_start_translation(
+          frozen_state_.head_from_start_translation.x,
+          frozen_state_.head_from_start_translation.y,
+          frozen_state_.head_from_start_translation.z);
+
+      WriteAsyncPoses(head_from_start_translation, start_from_head_rotation,
+                      current_time_ns);
+      break;
+    }
+    case DVR_POSE_MODE_3DOF: {
+      // Sensor fusion provides IMU-space data, transform to world space.
+
+      // Constants to perform IMU orientation adjustments. Note that these
+      // calculations will be optimized out in a release build.
+      constexpr double k90DegInRad = 90.0 * M_PI / 180.0;
+      const Vector3d kVecAxisX(1.0, 0.0, 0.0);
+      const Vector3d kVecAxisY(0.0, 1.0, 0.0);
+      const Vector3d kVecAxisZ(0.0, 0.0, 1.0);
+      const Rotationd kRotX90(AngleAxisd(k90DegInRad, kVecAxisX));
+
+      Rotationd start_from_head_rotation;
+      if (device_orientation_type_ == kOrientationTypeLandscape) {
+        const Rotationd kPostRotation =
+            kRotX90 * Rotationd(AngleAxisd(-k90DegInRad, kVecAxisY));
+        start_from_head_rotation =
+            (pose_state.sensor_from_start_rotation * kPostRotation).inverse();
+      } else {
+        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..899d5fb
--- /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/dvr_pose_predictor.h>
+#include <private/dvr/ring_buffer.h>
+
+#include "sensor_fusion.h"
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// PoseService implements the HMD pose service over ServiceFS.
+class PoseService : public pdx::ServiceBase<PoseService> {
+ public:
+  ~PoseService() override;
+
+  bool IsInitialized() const override;
+  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.
+  std::unique_ptr<posepredictor::Predictor> pose_predictor_;
+
+  // Pose ring buffer.
+  std::shared_ptr<BufferProducer> ring_buffer_;
+  // Temporary mapped ring buffer.
+  DvrPoseRingBuffer* mapped_pose_buffer_;
+  // Current vsync info, updated by displayd.
+  uint32_t vsync_count_;
+  int64_t photon_timestamp_;
+  int64_t display_period_ns_;
+  int64_t right_eye_photon_offset_ns_ = 0;
+
+  // Type for controlling pose orientation calculation.
+  OrientationType device_orientation_type_;
+
+  PoseService(const PoseService&) = delete;
+  void operator=(const PoseService&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_POSE_SERVICE_H_
diff --git a/services/vr/sensord/sensor_fusion.cpp b/services/vr/sensord/sensor_fusion.cpp
new file mode 100644
index 0000000..5663ae4
--- /dev/null
+++ b/services/vr/sensord/sensor_fusion.cpp
@@ -0,0 +1,348 @@
+#include "sensor_fusion.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include <private/dvr/eigen.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// --- start of added bits for porting to eigen
+
+// In general, we prefer to add wrappers for things like Inverse() to minimize
+// the changes to the imported code, so that merging in upstream changes becomes
+// simpler.
+
+inline Matrix3d Inverse(const Matrix3d& matrix) { return matrix.inverse(); }
+inline Matrix3d Transpose(const Matrix3d& matrix) { return matrix.transpose(); }
+inline Matrix3d RotationMatrixNH(const Rotationd& rotation) {
+  return rotation.toRotationMatrix();
+}
+inline double Length(const Vector3d& vector) { return vector.norm(); }
+
+using uint64 = uint64_t;
+
+// --- end of added bits for porting to eigen
+
+static const double kFiniteDifferencingEpsilon = 1e-7;
+static const double kEpsilon = 1e-15;
+// Default gyroscope frequency. This corresponds to 200 Hz.
+static const double kDefaultGyroscopeTimestep_s = 0.005f;
+// Maximum time between gyroscope before we start limiting the integration.
+static const double kMaximumGyroscopeSampleDelay_s = 0.04f;
+// Compute a first-order exponential moving average of changes in accel norm per
+// frame.
+static const double kSmoothingFactor = 0.5;
+// Minimum and maximum values used for accelerometer noise covariance matrix.
+// The smaller the sigma value, the more weight is given to the accelerometer
+// signal.
+static const double kMinAccelNoiseSigma = 0.75;
+static const double kMaxAccelNoiseSigma = 7.0;
+// Initial value for the diagonal elements of the different covariance matrices.
+static const double kInitialStateCovarianceValue = 25.0;
+static const double kInitialProcessCovarianceValue = 1.0;
+// Maximum accelerometer norm change allowed before capping it covariance to a
+// large value.
+static const double kMaxAccelNormChange = 0.15;
+// Timestep IIR filtering coefficient.
+static const double kTimestepFilterCoeff = 0.95;
+// Minimum number of sample for timestep filtering.
+static const uint32_t kTimestepFilterMinSamples = 10;
+
+// Z direction in start space.
+static const Vector3d kCanonicalZDirection(0.0, 0.0, 1.0);
+
+// Computes a axis angle rotation from the input vector.
+// angle = norm(a)
+// axis = a.normalized()
+// If norm(a) == 0, it returns an identity rotation.
+static Rotationd RotationFromVector(const Vector3d& a) {
+  const double norm_a = Length(a);
+  if (norm_a < kEpsilon) {
+    return Rotationd::Identity();
+  }
+  return Rotationd(AngleAxisd(norm_a, a / norm_a));
+}
+
+// --- start of functions ported from pose_prediction.cc
+
+namespace pose_prediction {
+
+// Returns a rotation matrix based on the integration of the gyroscope_value
+// over the timestep_s in seconds.
+// TODO(pfg): Document the space better here.
+//
+// @param gyroscope_value gyroscope sensor values.
+// @param timestep_s integration period in seconds.
+// @return Integration of the gyroscope value the rotation is from Start to
+//         Sensor Space.
+Rotationd GetRotationFromGyroscope(const Vector3d& gyroscope_value,
+                                   double timestep_s) {
+  const double velocity = Length(gyroscope_value);
+
+  // When there is no rotation data return an identity rotation.
+  if (velocity < kEpsilon) {
+    return Rotationd::Identity();
+  }
+  // Since the gyroscope_value is a start from sensor transformation we need to
+  // invert it to have a sensor from start transformation, hence the minus sign.
+  // For more info:
+  // http://developer.android.com/guide/topics/sensors/sensors_motion.html#sensors-motion-gyro
+  return Rotationd(AngleAxisd(-timestep_s * velocity,
+                              gyroscope_value / velocity));
+}
+
+}  // namespace pose_prediction
+
+// --- end of functions ported from pose_prediction.cc
+
+}  // namespace
+
+SensorFusion::SensorFusion()
+    : execute_reset_with_next_accelerometer_sample_(false) {
+  ResetState();
+}
+
+void SensorFusion::Reset() {
+  execute_reset_with_next_accelerometer_sample_ = true;
+}
+
+void SensorFusion::ResetState() {
+  current_state_.timestamp_ns = 0;
+  current_state_.sensor_from_start_rotation = Rotationd::Identity();
+  current_state_.sensor_from_start_rotation_velocity = Vector3d::Zero();
+
+  current_accelerometer_timestamp_ns_ = 0;
+
+  state_covariance_ = Matrix3d::Identity() * kInitialStateCovarianceValue;
+  process_covariance_ = Matrix3d::Identity() * kInitialProcessCovarianceValue;
+  accelerometer_measurement_covariance_ =
+      Matrix3d::Identity() * kMinAccelNoiseSigma * kMinAccelNoiseSigma;
+  innovation_covariance_.setIdentity();
+
+  accelerometer_measurement_jacobian_ = Matrix3d::Zero();
+  kalman_gain_ = Matrix3d::Zero();
+  innovation_ = Vector3d::Zero();
+  accelerometer_measurement_ = Vector3d::Zero();
+  prediction_ = Vector3d::Zero();
+  control_input_ = Vector3d::Zero();
+  state_update_ = Vector3d::Zero();
+
+  moving_average_accelerometer_norm_change_ = 0.0;
+
+  is_timestep_filter_initialized_ = false;
+  is_gyroscope_filter_valid_ = false;
+  is_aligned_with_gravity_ = false;
+}
+
+// Here I am doing something wrong relative to time stamps. The state timestamps
+// always correspond to the gyrostamps because it would require additional
+// extrapolation if I wanted to do otherwise.
+// TODO(pfg): investigate about published an updated pose after accelerometer
+// data was used for filtering.
+PoseState SensorFusion::GetLatestPoseState() const {
+  std::unique_lock<std::mutex> lock(mutex_);
+  return current_state_;
+}
+
+void SensorFusion::ProcessGyroscopeSample(float v_x, float v_y, float v_z,
+                                          uint64 timestamp_ns) {
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  // Don't accept gyroscope sample when waiting for a reset.
+  if (execute_reset_with_next_accelerometer_sample_) {
+    return;
+  }
+
+  // Discard outdated samples.
+  if (current_state_.timestamp_ns >= timestamp_ns) {
+    // TODO(pfg): Investigate why this happens.
+    return;
+  }
+
+  // Checks that we received at least one gyroscope sample in the past.
+  if (current_state_.timestamp_ns != 0) {
+    // TODO(pfg): roll this in filter gyroscope timestep function.
+    double current_timestep_s =
+        static_cast<double>(timestamp_ns - current_state_.timestamp_ns) * 1e-9;
+    if (current_timestep_s > kMaximumGyroscopeSampleDelay_s) {
+      if (is_gyroscope_filter_valid_) {
+        // Replaces the delta timestamp by the filtered estimates of the delta
+        // time.
+        current_timestep_s = filtered_gyroscope_timestep_s_;
+      } else {
+        current_timestep_s = kDefaultGyroscopeTimestep_s;
+      }
+    } else {
+      FilterGyroscopeTimestep(current_timestep_s);
+    }
+
+    // Only integrate after receiving a accelerometer sample.
+    if (is_aligned_with_gravity_) {
+      const Rotationd rotation_from_gyroscope =
+          pose_prediction::GetRotationFromGyroscope(Vector3d(v_x, v_y, v_z),
+                                                    current_timestep_s);
+      current_state_.sensor_from_start_rotation =
+          rotation_from_gyroscope * current_state_.sensor_from_start_rotation;
+      current_state_.sensor_from_start_rotation.normalize();
+      UpdateStateCovariance(RotationMatrixNH(rotation_from_gyroscope));
+      state_covariance_ =
+          state_covariance_ +
+          (process_covariance_ * (current_timestep_s * current_timestep_s));
+    }
+  }
+
+  // Saves gyroscope event for future prediction.
+  current_state_.timestamp_ns = timestamp_ns;
+  current_state_.sensor_from_start_rotation_velocity = Vector3d(v_x, v_y, v_z);
+}
+
+// TODO(pfg): move to rotation object for the input.
+Vector3d SensorFusion::ComputeInnovation(const Rotationd& pose) {
+  const Vector3d predicted_down_direction =
+      RotationMatrixNH(pose) * kCanonicalZDirection;
+
+  const Rotationd rotation = Rotationd::FromTwoVectors(
+      predicted_down_direction, accelerometer_measurement_);
+  AngleAxisd angle_axis(rotation);
+  return angle_axis.axis() * angle_axis.angle();
+}
+
+void SensorFusion::ComputeMeasurementJacobian() {
+  for (int dof = 0; dof < 3; dof++) {
+    // TODO(pfg): Create this delta rotation in the constructor and used unitX..
+    Vector3d delta = Vector3d::Zero();
+    delta[dof] = kFiniteDifferencingEpsilon;
+
+    const Rotationd epsilon_rotation = RotationFromVector(delta);
+    const Vector3d delta_rotation = ComputeInnovation(
+        epsilon_rotation * current_state_.sensor_from_start_rotation);
+
+    const Vector3d col =
+        (innovation_ - delta_rotation) / kFiniteDifferencingEpsilon;
+    accelerometer_measurement_jacobian_(0, dof) = col[0];
+    accelerometer_measurement_jacobian_(1, dof) = col[1];
+    accelerometer_measurement_jacobian_(2, dof) = col[2];
+  }
+}
+
+void SensorFusion::ProcessAccelerometerSample(float acc_x, float acc_y,
+                                              float acc_z,
+                                              uint64 timestamp_ns) {
+  std::unique_lock<std::mutex> lock(mutex_);
+
+  // Discard outdated samples.
+  if (current_accelerometer_timestamp_ns_ >= timestamp_ns) {
+    // TODO(pfg): Investigate why this happens.
+    return;
+  }
+
+  // Call reset state if required.
+  if (execute_reset_with_next_accelerometer_sample_.exchange(false)) {
+    ResetState();
+  }
+
+  accelerometer_measurement_ = Vector3d(acc_x, acc_y, acc_z);
+  current_accelerometer_timestamp_ns_ = timestamp_ns;
+
+  if (!is_aligned_with_gravity_) {
+    // This is the first accelerometer measurement so it initializes the
+    // orientation estimate.
+    current_state_.sensor_from_start_rotation = Rotationd::FromTwoVectors(
+        kCanonicalZDirection, accelerometer_measurement_);
+    is_aligned_with_gravity_ = true;
+
+    previous_accelerometer_norm_ = Length(accelerometer_measurement_);
+    return;
+  }
+
+  UpdateMeasurementCovariance();
+
+  innovation_ = ComputeInnovation(current_state_.sensor_from_start_rotation);
+  ComputeMeasurementJacobian();
+
+  // S = H * P * H' + R
+  innovation_covariance_ = accelerometer_measurement_jacobian_ *
+                               state_covariance_ *
+                               Transpose(accelerometer_measurement_jacobian_) +
+                           accelerometer_measurement_covariance_;
+
+  // K = P * H' * S^-1
+  kalman_gain_ = state_covariance_ *
+                 Transpose(accelerometer_measurement_jacobian_) *
+                 Inverse(innovation_covariance_);
+
+  // x_update = K*nu
+  state_update_ = kalman_gain_ * innovation_;
+
+  // P = (I - K * H) * P;
+  state_covariance_ = (Matrix3d::Identity() -
+                       kalman_gain_ * accelerometer_measurement_jacobian_) *
+                      state_covariance_;
+
+  // Updates pose and associate covariance matrix.
+  const Rotationd rotation_from_state_update =
+      RotationFromVector(state_update_);
+
+  current_state_.sensor_from_start_rotation =
+      rotation_from_state_update * current_state_.sensor_from_start_rotation;
+  UpdateStateCovariance(RotationMatrixNH(rotation_from_state_update));
+}
+
+void SensorFusion::UpdateStateCovariance(const Matrix3d& motion_update) {
+  state_covariance_ =
+      motion_update * state_covariance_ * Transpose(motion_update);
+}
+
+void SensorFusion::FilterGyroscopeTimestep(double gyroscope_timestep_s) {
+  if (!is_timestep_filter_initialized_) {
+    // Initializes the filter.
+    filtered_gyroscope_timestep_s_ = gyroscope_timestep_s;
+    num_gyroscope_timestep_samples_ = 1;
+    is_timestep_filter_initialized_ = true;
+    return;
+  }
+
+  // Computes the IIR filter response.
+  filtered_gyroscope_timestep_s_ =
+      kTimestepFilterCoeff * filtered_gyroscope_timestep_s_ +
+      (1 - kTimestepFilterCoeff) * gyroscope_timestep_s;
+  ++num_gyroscope_timestep_samples_;
+
+  if (num_gyroscope_timestep_samples_ > kTimestepFilterMinSamples) {
+    is_gyroscope_filter_valid_ = true;
+  }
+}
+
+void SensorFusion::UpdateMeasurementCovariance() {
+  const double current_accelerometer_norm = Length(accelerometer_measurement_);
+  // Norm change between current and previous accel readings.
+  const double current_accelerometer_norm_change =
+      std::abs(current_accelerometer_norm - previous_accelerometer_norm_);
+  previous_accelerometer_norm_ = current_accelerometer_norm;
+
+  moving_average_accelerometer_norm_change_ =
+      kSmoothingFactor * current_accelerometer_norm_change +
+      (1. - kSmoothingFactor) * moving_average_accelerometer_norm_change_;
+
+  // If we hit the accel norm change threshold, we use the maximum noise sigma
+  // for the accel covariance. For anything below that, we use a linear
+  // combination between min and max sigma values.
+  const double norm_change_ratio =
+      moving_average_accelerometer_norm_change_ / kMaxAccelNormChange;
+  const double accelerometer_noise_sigma = std::min(
+      kMaxAccelNoiseSigma,
+      kMinAccelNoiseSigma +
+          norm_change_ratio * (kMaxAccelNoiseSigma - kMinAccelNoiseSigma));
+
+  // Updates the accel covariance matrix with the new sigma value.
+  accelerometer_measurement_covariance_ = Matrix3d::Identity() *
+                                          accelerometer_noise_sigma *
+                                          accelerometer_noise_sigma;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/sensor_fusion.h b/services/vr/sensord/sensor_fusion.h
new file mode 100644
index 0000000..0ceae21
--- /dev/null
+++ b/services/vr/sensord/sensor_fusion.h
@@ -0,0 +1,181 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
+#define ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
+
+#include <atomic>
+#include <cstdlib>
+#include <mutex>
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+using Matrix3d = Eigen::Matrix<double, 3, 3>;
+using Rotationd = quatd;
+using Vector3d = vec3d;
+using AngleAxisd = Eigen::AngleAxisd;
+
+// Ported from GVR's pose_state.h.
+// Stores a 3dof pose plus derivatives. This can be used for prediction.
+struct PoseState {
+  // Time in nanoseconds for the current pose.
+  uint64_t timestamp_ns;
+
+  // Rotation from Sensor Space to Start Space.
+  Rotationd sensor_from_start_rotation;
+
+  // First derivative of the rotation.
+  // TODO(pfg): currently storing gyro data, switch to first derivative instead.
+  Vector3d sensor_from_start_rotation_velocity;
+};
+
+// Sensor fusion class that implements an Extended Kalman Filter (EKF) to
+// estimate a 3D rotation from a gyroscope and and accelerometer.
+// This system only has one state, the pose. It does not estimate any velocity
+// or acceleration.
+//
+// To learn more about Kalman filtering one can read this article which is a
+// good introduction: http://en.wikipedia.org/wiki/Kalman_filter
+//
+// Start Space is :
+// z is up.
+// y is forward based on the first sensor data.
+// x = y \times z
+// Sensor Space follows the android specification {@link
+// http://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords}
+// See http://go/vr-coords for definitions of Start Space and Sensor Space.
+//
+// This is a port from GVR's SensorFusion code (See
+// https://cs/vr/gvr/sensors/sensor_fusion.h)
+// which in turn is a port from java of OrientationEKF (See
+// https://cs/java/com/google/vr/cardboard/vrtoolkit/vrtoolkit/src/main/java/com/google/vrtoolkit/cardboard/sensors/internal/OrientationEKF.java)
+class SensorFusion {
+ public:
+  SensorFusion();
+  SensorFusion(const SensorFusion&) = delete;
+  void operator=(const SensorFusion&) = delete;
+
+  // Resets the state of the sensor fusion. It sets the velocity for
+  // prediction to zero. The reset will happen with the next
+  // accelerometer sample. Gyroscope sample will be discarded until a new
+  // accelerometer sample arrives.
+  void Reset();
+
+  // Gets the PoseState representing the latest pose and  derivatives at a
+  // particular timestamp as estimated by SensorFusion.
+  PoseState GetLatestPoseState() const;
+
+  // Processes one gyroscope sample event. This updates the pose of the system
+  // and the prediction model. The gyroscope data is assumed to be in axis angle
+  // form. Angle = ||v|| and Axis = v / ||v||, with v = [v_x, v_y, v_z]^T.
+  //
+  // @param v_x velocity in x.
+  // @param v_y velocity in y.
+  // @param v_z velocity in z.
+  // @param timestamp_ns gyroscope event timestamp in nanosecond.
+  void ProcessGyroscopeSample(float v_x, float v_y, float v_z,
+                              uint64_t timestamp_ns);
+
+  // Processes one accelerometer sample event. This updates the pose of the
+  // system. If the Accelerometer norm changes too much between sample it is not
+  // trusted as much.
+  //
+  // @param acc_x accelerometer data in x.
+  // @param acc_y accelerometer data in y.
+  // @param acc_z accelerometer data in z.
+  // @param timestamp_ns accelerometer event timestamp in nanosecond.
+  void ProcessAccelerometerSample(float acc_x, float acc_y, float acc_z,
+                                  uint64_t timestamp_ns);
+
+ private:
+  // Estimates the average timestep between gyroscope event.
+  void FilterGyroscopeTimestep(double gyroscope_timestep);
+
+  // Updates the state covariance with an incremental motion. It changes the
+  // space of the quadric.
+  void UpdateStateCovariance(const Matrix3d& motion_update);
+
+  // Computes the innovation vector of the Kalman based on the input pose.
+  // It uses the latest measurement vector (i.e. accelerometer data), which must
+  // be set prior to calling this function.
+  Vector3d ComputeInnovation(const Rotationd& pose);
+
+  // This computes the measurement_jacobian_ via numerical differentiation based
+  // on the current value of sensor_from_start_rotation_.
+  void ComputeMeasurementJacobian();
+
+  // Updates the accelerometer covariance matrix.
+  //
+  // This looks at the norm of recent accelerometer readings. If it has changed
+  // significantly, it means the phone receives additional acceleration than
+  // just gravity, and so the down vector information gravity signal is noisier.
+  //
+  // TODO(dcoz,pfg): this function is very simple, we probably need something
+  // more elaborated here once we have proper regression testing.
+  void UpdateMeasurementCovariance();
+
+  // Reset all internal states. This is not thread safe. Lock should be acquired
+  // outside of it. This function is called in ProcessAccelerometerSample.
+  void ResetState();
+
+  // Current transformation from Sensor Space to Start Space.
+  // x_sensor = sensor_from_start_rotation_ * x_start;
+  PoseState current_state_;
+
+  // Filtering of the gyroscope timestep started?
+  bool is_timestep_filter_initialized_;
+  // Filtered gyroscope timestep valid?
+  bool is_gyroscope_filter_valid_;
+  // Sensor fusion currently aligned with gravity? After initialization
+  // it will requires a couple of accelerometer data for the system to get
+  // aligned.
+  bool is_aligned_with_gravity_;
+
+  // Covariance of Kalman filter state (P in common formulation).
+  Matrix3d state_covariance_;
+  // Covariance of the process noise (Q in common formulation).
+  Matrix3d process_covariance_;
+  // Covariance of the accelerometer measurement (R in common formulation).
+  Matrix3d accelerometer_measurement_covariance_;
+  // Covariance of innovation (S in common formulation).
+  Matrix3d innovation_covariance_;
+  // Jacobian of the measurements (H in common formulation).
+  Matrix3d accelerometer_measurement_jacobian_;
+  // Gain of the Kalman filter (K in common formulation).
+  Matrix3d kalman_gain_;
+  // Parameter update a.k.a. innovation vector. (\nu in common formulation).
+  Vector3d innovation_;
+  // Measurement vector (z in common formulation).
+  Vector3d accelerometer_measurement_;
+  // Current prediction vector (g in common formulation).
+  Vector3d prediction_;
+  // Control input, currently this is only the gyroscope data (\mu in common
+  // formulation).
+  Vector3d control_input_;
+  // Update of the state vector. (x in common formulation).
+  Vector3d state_update_;
+
+  // Time of the last accelerometer processed event.
+  uint64_t current_accelerometer_timestamp_ns_;
+
+  // Estimates of the timestep between gyroscope event in seconds.
+  double filtered_gyroscope_timestep_s_;
+  // Number of timestep samples processed so far by the filter.
+  uint32_t num_gyroscope_timestep_samples_;
+  // Norm of the accelerometer for the previous measurement.
+  double previous_accelerometer_norm_;
+  // Moving average of the accelerometer norm changes. It is computed for every
+  // sensor datum.
+  double moving_average_accelerometer_norm_change_;
+
+  // Flag indicating if a state reset should be executed with the next
+  // accelerometer sample.
+  std::atomic<bool> execute_reset_with_next_accelerometer_sample_;
+
+  mutable std::mutex mutex_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_SENSOR_FUSION_H_
diff --git a/services/vr/sensord/sensor_hal_thread.cpp b/services/vr/sensord/sensor_hal_thread.cpp
new file mode 100644
index 0000000..c321d4f
--- /dev/null
+++ b/services/vr/sensord/sensor_hal_thread.cpp
@@ -0,0 +1,158 @@
+#include "sensor_hal_thread.h"
+
+#include <dvr/performance_client_api.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+SensorHalThread::SensorHalThread(bool* out_success)
+    : shutting_down_(false),
+      paused_(false),
+      sensor_module_(nullptr),
+      sensor_device_(nullptr),
+      sensor_list_(nullptr) {
+  // Assume failure; we will change this to true on success.
+  *out_success = false;
+
+  // TODO(segal): module & device should be singletons.
+  int32_t err = hw_get_module_by_class(SENSORS_HARDWARE_MODULE_ID, "platform",
+                                       (hw_module_t const**)&sensor_module_);
+
+  if (err) {
+    ALOGE("couldn't load %s module (%s)", SENSORS_HARDWARE_MODULE_ID,
+          strerror(-err));
+    return;
+  }
+
+  err = sensors_open_1(&sensor_module_->common, &sensor_device_);
+  if (err) {
+    ALOGE("couldn't open device for module %s (%s)", SENSORS_HARDWARE_MODULE_ID,
+          strerror(-err));
+    return;
+  }
+
+  const int sensor_count =
+      sensor_module_->get_sensors_list(sensor_module_, &sensor_list_);
+
+  // Deactivate all of the sensors initially.
+  sensor_user_count_.resize(sensor_count, 0);
+  for (int i = 0; i < sensor_count; ++i) {
+    err = sensor_device_->activate(
+        reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+        sensor_list_[i].handle, 0);
+
+    if (err) {
+      ALOGE("failed to deactivate sensor %d (%s)", i, strerror(-err));
+      return;
+    }
+  }
+
+  // At this point, we've successfully initialized everything.
+  *out_success = true;
+}
+
+SensorHalThread::~SensorHalThread() {
+  {
+    std::unique_lock<std::mutex> lock(mutex_);
+    shutting_down_ = true;
+    condition_.notify_one();
+  }
+
+  // Implicitly joins *thread_ if it's running.
+}
+
+void SensorHalThread::StartPolling(const EventConsumer& consumer) {
+  if (thread_) {
+    ALOGE("SensorHalThread::Start() called but thread is already running!");
+    return;
+  }
+
+  thread_.reset(new std::thread([this, consumer] {
+    const int priority_error = dvrSetSchedulerClass(0, "sensors:high");
+    LOG_ALWAYS_FATAL_IF(
+        priority_error < 0,
+        "SensorHalTread::StartPolling: Failed to set scheduler class: %s",
+        strerror(-priority_error));
+
+    for (;;) {
+      for (;;) {
+        std::unique_lock<std::mutex> lock(mutex_);
+        if (shutting_down_)
+          return;
+        if (!paused_)
+          break;
+        condition_.wait(lock);
+      }
+      const int kMaxEvents = 100;
+      sensors_event_t events[kMaxEvents];
+      ssize_t event_count = 0;
+      do {
+        if (sensor_device_) {
+          event_count = sensor_device_->poll(
+              reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+              events, kMaxEvents);
+        } else {
+          // When there is no sensor_device_, we still call the consumer at
+          // regular intervals in case mock poses are in use. Note that this
+          // will never be the case for production devices, but this helps
+          // during bringup.
+          usleep(5000);
+        }
+      } while (event_count == -EINTR);
+      if (event_count == kMaxEvents)
+        ALOGI("max events (%d) reached", kMaxEvents);
+
+      if (event_count >= 0) {
+        consumer(events, events + event_count);
+      } else {
+        ALOGE(
+            "SensorHalThread::StartPolling: Error while polling sensor: %s "
+            "(%zd)",
+            strerror(-event_count), -event_count);
+      }
+    }
+  }));
+}
+
+void SensorHalThread::SetPaused(bool is_paused) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  paused_ = is_paused;
+  condition_.notify_one();
+}
+
+void SensorHalThread::StartUsingSensor(const int sensor_index) {
+  if (sensor_index < 0 || sensor_index >= GetSensorCount()) {
+    ALOGE("StartUsingSensor(): sensor index %d out of range [0, %d)",
+          sensor_index, GetSensorCount());
+    return;
+  }
+
+  std::lock_guard<std::mutex> guard(user_count_mutex_);
+  if (sensor_user_count_[sensor_index]++ == 0) {
+    sensor_device_->activate(
+        reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+        sensor_list_[sensor_index].handle, 1);
+    sensor_device_->setDelay(
+        reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+        sensor_list_[sensor_index].handle, 0);
+  }
+}
+
+void SensorHalThread::StopUsingSensor(const int sensor_index) {
+  if (sensor_index < 0 || sensor_index >= GetSensorCount()) {
+    ALOGE("StopUsingSensor(): sensor index %d out of range [0, %d)",
+          sensor_index, GetSensorCount());
+    return;
+  }
+
+  std::lock_guard<std::mutex> guard(user_count_mutex_);
+  if (--sensor_user_count_[sensor_index] == 0) {
+    sensor_device_->activate(
+        reinterpret_cast<struct sensors_poll_device_t*>(sensor_device_),
+        sensor_list_[sensor_index].handle, 0);
+  }
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/sensor_hal_thread.h b/services/vr/sensord/sensor_hal_thread.h
new file mode 100644
index 0000000..9220757
--- /dev/null
+++ b/services/vr/sensord/sensor_hal_thread.h
@@ -0,0 +1,99 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
+#define ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
+
+#include <hardware/sensors.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// Manages initialization and polling of the sensor HAL. Polling is performed
+// continuously on a thread that passes events along to an arbitrary consumer.
+// All const member functions are thread-safe; otherwise, thread safety is noted
+// for each function.
+class SensorHalThread : public SensorThread {
+ public:
+  // Initializes the sensor HAL, but does not yet start polling (see Start()
+  // below). Sets *out_success to true on success; otherwise, sets *out_success
+  // to false and logs an error.
+  explicit SensorHalThread(bool* out_success);
+
+  // Tells the polling thread to shut down if it's running, and waits for it to
+  // complete its polling loop.
+  ~SensorHalThread() override;
+
+  // Begins polling on the thread. The provided consumer will be notified of
+  // events. Event notification occurs on the polling thread.
+  // Calling Start() more than once on an instance of SensorHalThread is
+  // invalid.
+  void StartPolling(const EventConsumer& consumer) override;
+
+  // Set whether the sensor polling thread is paused or not. This is useful
+  // while we need to support both 3DoF and 6DoF codepaths. This 3DoF codepath
+  // must be paused while the 6DoF codepath is using the IMU event stream.
+  void SetPaused(bool is_paused) override;
+
+  // Increase the number of users of the given sensor by one. Activates the
+  // sensor if it wasn't already active.
+  // Safe to call concurrently with any other functions in this class.
+  void StartUsingSensor(int sensor_index) override;
+
+  // Decrease the number of users of the given sensor by one. Deactivates the
+  // sensor if its usage count has dropped to zero.
+  // Safe to call concurrently with any other functions in this class.
+  void StopUsingSensor(int sensor_index) override;
+
+  // The number of sensors that are available. Returns a negative number if
+  // initialization failed.
+  int GetSensorCount() const override {
+    return static_cast<int>(sensor_user_count_.size());
+  }
+
+  // The underlying sensor HAL data structure for the sensor at the given index.
+  int GetSensorType(int index) const override {
+    return sensor_list_[index].type;
+  }
+
+ private:
+  // The actual thread on which we consume events.
+  std::unique_ptr<std::thread> thread_;
+
+  // Mutex for access to shutting_down_ and paused_ members.
+  std::mutex mutex_;
+
+  // Condition for signaling pause/unpause to the thread.
+  std::condition_variable condition_;
+
+  // If this member is set to true, the thread will stop running at its next
+  // iteration. Only set with the mutex held and signal condition_ when changed.
+  bool shutting_down_;
+
+  // If this member is set to true, the thread will pause at its next
+  // iteration. Only set with the mutex held and signal condition_ when changed.
+  bool paused_;
+
+  // HAL access
+  struct sensors_module_t* sensor_module_;
+  sensors_poll_device_1_t* sensor_device_;
+
+  // Contiguous array of available sensors, owned by the sensor HAL.
+  const sensor_t* sensor_list_;
+
+  // Mutex that protects access to sensor_user_count_.data().
+  std::mutex user_count_mutex_;
+
+  // A count of how many users each sensor has. Protected by user_count_mutex.
+  std::vector<int> sensor_user_count_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_SENSOR_HAL_THREAD_H_
diff --git a/services/vr/sensord/sensor_ndk_thread.cpp b/services/vr/sensord/sensor_ndk_thread.cpp
new file mode 100644
index 0000000..9c3abbc
--- /dev/null
+++ b/services/vr/sensord/sensor_ndk_thread.cpp
@@ -0,0 +1,269 @@
+#include "sensor_ndk_thread.h"
+
+#include <dvr/performance_client_api.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+static constexpr int kLooperIdUser = 5;
+}  // namespace
+
+SensorNdkThread::SensorNdkThread(bool* out_success)
+    : shutting_down_(false),
+      paused_(true),
+      thread_started_(false),
+      initialization_result_(false),
+      looper_(nullptr),
+      sensor_manager_(nullptr),
+      event_queue_(nullptr),
+      sensor_list_(nullptr),
+      sensor_count_(0) {
+  // Assume failure; we will change this to true on success.
+  *out_success = false;
+
+  // These structs are the same, but sanity check the sizes.
+  static_assert(sizeof(sensors_event_t) == sizeof(ASensorEvent),
+                "Error: sizeof(sensors_event_t) != sizeof(ASensorEvent)");
+
+  thread_.reset(new std::thread([this] {
+    const int priority_error = dvrSetSchedulerClass(0, "sensors:high");
+    LOG_ALWAYS_FATAL_IF(
+        priority_error < 0,
+        "SensorHalTread::StartPolling: Failed to set scheduler class: %s",
+        strerror(-priority_error));
+
+    // Start ALooper and initialize sensor access.
+    {
+      std::unique_lock<std::mutex> lock(mutex_);
+      InitializeSensors();
+      thread_started_ = true;
+      init_condition_.notify_one();
+      // Continue on failure - the loop below will periodically retry.
+    }
+
+    EventConsumer consumer;
+    for (;;) {
+      for (;;) {
+        std::unique_lock<std::mutex> lock(mutex_);
+        UpdateSensorUse();
+        if (!consumer)
+          consumer = consumer_;
+        if (shutting_down_)
+          return;
+        if (!paused_)
+          break;
+        condition_.wait(lock);
+      }
+
+      constexpr int kMaxEvents = 100;
+      sensors_event_t events[kMaxEvents];
+      ssize_t event_count = 0;
+      if (initialization_result_) {
+        int poll_fd, poll_events;
+        void* poll_source;
+        // Poll for events.
+        int ident = ALooper_pollAll(-1, &poll_fd, &poll_events, &poll_source);
+
+        if (ident != kLooperIdUser)
+          continue;
+
+        ASensorEvent* event = reinterpret_cast<ASensorEvent*>(&events[0]);
+        event_count =
+            ASensorEventQueue_getEvents(event_queue_, event, kMaxEvents);
+
+        if (event_count == 0) {
+          ALOGE("Detected sensor service failure, restarting sensors");
+          // This happens when sensorservice has died and restarted. To avoid
+          // spinning we need to restart the sensor access.
+          DestroySensors();
+        }
+      } else {
+        // When there is no sensor_device_, we still call the consumer at
+        // regular intervals in case mock poses are in use. Note that this
+        // will never be the case for production devices, but this helps
+        // during bringup.
+        usleep(5000);
+      }
+      if (event_count == kMaxEvents)
+        ALOGI("max events (%d) reached", kMaxEvents);
+
+      if (event_count >= 0) {
+        consumer(events, events + event_count);
+      } else {
+        ALOGE(
+            "SensorNdkThread::StartPolling: Error while polling sensor: %s "
+            "(%zd)",
+            strerror(-event_count), -event_count);
+      }
+    }
+
+    // About to exit sensor thread, destroy sensor objects.
+    DestroySensors();
+  }));
+
+  // Wait for thread to startup and initialize sensors so that we know whether
+  // it succeeded.
+  {
+    std::unique_lock<std::mutex> lock(mutex_);
+    while (!thread_started_)
+      init_condition_.wait(lock);
+  }
+
+  // At this point, we've successfully initialized everything.
+  // The NDK sensor thread will continue to retry on error, so assume success here.
+  *out_success = true;
+}
+
+SensorNdkThread::~SensorNdkThread() {
+  {
+    if (looper_)
+      ALooper_wake(looper_);
+    std::unique_lock<std::mutex> lock(mutex_);
+    shutting_down_ = true;
+    condition_.notify_one();
+  }
+
+  thread_->join();
+}
+
+bool SensorNdkThread::InitializeSensors() {
+  looper_ = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+  if (!looper_) {
+    ALOGE("Failed to create ALooper.");
+    return false;
+  }
+
+  // Prepare to monitor accelerometer
+  sensor_manager_ = ASensorManager_getInstanceForPackage(nullptr);
+  if (!sensor_manager_) {
+    ALOGE("Failed to create ASensorManager.");
+    return false;
+  }
+
+  event_queue_ = ASensorManager_createEventQueue(
+      sensor_manager_, looper_, kLooperIdUser, nullptr, nullptr);
+  if (!event_queue_) {
+    ALOGE("Failed to create sensor EventQueue.");
+    return false;
+  }
+
+  sensor_count_ = ASensorManager_getSensorList(sensor_manager_, &sensor_list_);
+  ALOGI("Sensor count %d", sensor_count_);
+
+  sensor_user_count_.resize(sensor_count_, 0);
+
+  // To recover from sensorservice restart, enable the sensors that are already
+  // requested.
+  for (size_t sensor_index = 0; sensor_index < sensor_user_count_.size();
+       ++sensor_index) {
+    if (sensor_user_count_[sensor_index] > 0) {
+      int result = ASensorEventQueue_registerSensor(
+          event_queue_, sensor_list_[sensor_index], 0, 0);
+      ALOGE_IF(result < 0, "ASensorEventQueue_registerSensor failed: %d",
+               result);
+    }
+  }
+
+  initialization_result_ = true;
+  return true;
+}
+
+void SensorNdkThread::DestroySensors() {
+  if (!event_queue_)
+    return;
+  for (size_t sensor_index = 0; sensor_index < sensor_user_count_.size();
+       ++sensor_index) {
+    if (sensor_user_count_[sensor_index] > 0) {
+      ASensorEventQueue_disableSensor(event_queue_, sensor_list_[sensor_index]);
+    }
+  }
+  ASensorManager_destroyEventQueue(sensor_manager_, event_queue_);
+  event_queue_ = nullptr;
+  initialization_result_ = false;
+}
+
+void SensorNdkThread::UpdateSensorUse() {
+  if (!initialization_result_) {
+    // Sleep for 1 second to avoid spinning during system instability.
+    usleep(1000 * 1000);
+    InitializeSensors();
+    if (!initialization_result_)
+      return;
+  }
+
+  if (!enable_sensors_.empty()) {
+    for (int sensor_index : enable_sensors_) {
+      if (sensor_user_count_[sensor_index]++ == 0) {
+        int result = ASensorEventQueue_registerSensor(
+            event_queue_, sensor_list_[sensor_index], 0, 0);
+        ALOGE_IF(result < 0, "ASensorEventQueue_registerSensor failed: %d",
+                 result);
+      }
+    }
+    enable_sensors_.clear();
+  }
+
+  if (!disable_sensors_.empty()) {
+    for (int sensor_index : disable_sensors_) {
+      if (--sensor_user_count_[sensor_index] == 0) {
+        int result = ASensorEventQueue_disableSensor(
+            event_queue_, sensor_list_[sensor_index]);
+        ALOGE_IF(result < 0, "ASensorEventQueue_disableSensor failed: %d",
+                 result);
+      }
+    }
+    disable_sensors_.clear();
+  }
+}
+
+void SensorNdkThread::StartPolling(const EventConsumer& consumer) {
+  {
+    std::unique_lock<std::mutex> lock(mutex_);
+    if (consumer_) {
+      ALOGE("Already started sensor thread.");
+      return;
+    }
+    consumer_ = consumer;
+  }
+  SetPaused(false);
+}
+
+void SensorNdkThread::SetPaused(bool is_paused) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  // SetPaused may be called before we have StartPolling, make sure we have
+  // an event consumer. Otherwise we defer until StartPolling is called.
+  if (!consumer_)
+    return;
+  paused_ = is_paused;
+  condition_.notify_one();
+  ALooper_wake(looper_);
+}
+
+void SensorNdkThread::StartUsingSensor(const int sensor_index) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  if (sensor_index < 0 || sensor_index >= sensor_count_) {
+    ALOGE("StartUsingSensor(): sensor index %d out of range [0, %d)",
+          sensor_index, sensor_count_);
+    return;
+  }
+
+  enable_sensors_.push_back(sensor_index);
+  ALooper_wake(looper_);
+}
+
+void SensorNdkThread::StopUsingSensor(const int sensor_index) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  if (sensor_index < 0 || sensor_index >= sensor_count_) {
+    ALOGE("StopUsingSensor(): sensor index %d out of range [0, %d)",
+          sensor_index, sensor_count_);
+    return;
+  }
+
+  disable_sensors_.push_back(sensor_index);
+  ALooper_wake(looper_);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/sensord/sensor_ndk_thread.h b/services/vr/sensord/sensor_ndk_thread.h
new file mode 100644
index 0000000..eb3cf9d
--- /dev/null
+++ b/services/vr/sensord/sensor_ndk_thread.h
@@ -0,0 +1,124 @@
+#ifndef ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
+#define ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
+
+#include <android/sensor.h>
+#include <hardware/sensors.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "sensor_thread.h"
+
+namespace android {
+namespace dvr {
+
+// Manages initialization and polling of the sensor data. Polling is performed
+// continuously on a thread that passes events along to an arbitrary consumer.
+// All const member functions are thread-safe; otherwise, thread safety is noted
+// for each function.
+class SensorNdkThread : public SensorThread {
+ public:
+  // Initializes the sensor access, but does not yet start polling (see Start()
+  // below). Sets *out_success to true on success; otherwise, sets *out_success
+  // to false and logs an error.
+  explicit SensorNdkThread(bool* out_success);
+
+  // Tells the polling thread to shut down if it's running, and waits for it to
+  // complete its polling loop.
+  ~SensorNdkThread() override;
+
+  // Begins polling on the thread. The provided consumer will be notified of
+  // events. Event notification occurs on the polling thread.
+  // Calling Start() more than once on an instance of SensorNdkThread is
+  // invalid.
+  void StartPolling(const EventConsumer& consumer) override;
+
+  // Set whether the sensor polling thread is paused or not. This is useful
+  // while we need to support both 3DoF and 6DoF codepaths. This 3DoF codepath
+  // must be paused while the 6DoF codepath is using the IMU event stream.
+  void SetPaused(bool is_paused) override;
+
+  // Increase the number of users of the given sensor by one. Activates the
+  // sensor if it wasn't already active.
+  // Safe to call concurrently with any other functions in this class.
+  void StartUsingSensor(int sensor_index) override;
+
+  // Decrease the number of users of the given sensor by one. Deactivates the
+  // sensor if its usage count has dropped to zero.
+  // Safe to call concurrently with any other functions in this class.
+  void StopUsingSensor(int sensor_index) override;
+
+  // The number of sensors that are available. Returns a negative number if
+  // initialization failed.
+  int GetSensorCount() const override { return sensor_count_; }
+
+  // The underlying sensor HAL data structure for the sensor at the given index.
+  int GetSensorType(int index) const override {
+    return ASensor_getType(sensor_list_[index]);
+  }
+
+ private:
+  // Initialize ALooper and sensor access on the thread.
+  // Returns true on success, false on failure.
+  bool InitializeSensors();
+
+  // Destroy sensor access.
+  void DestroySensors();
+
+  // Start or stop requested sensors from the thread. Class mutex must already
+  // be locked.
+  void UpdateSensorUse();
+
+  // The actual thread on which we consume events.
+  std::unique_ptr<std::thread> thread_;
+
+  // Mutex for access to shutting_down_ and paused_ members.
+  std::mutex mutex_;
+
+  // Condition for signaling pause/unpause to the thread.
+  std::condition_variable condition_;
+
+  // Condition for signaling thread initialization.
+  std::condition_variable init_condition_;
+
+  // If this member is set to true, the thread will stop running at its next
+  // iteration. Only set with the mutex held and signal condition_ when changed.
+  bool shutting_down_;
+
+  // If this member is set to true, the thread will pause at its next
+  // iteration. Only set with the mutex held and signal condition_ when changed.
+  bool paused_;
+
+  // Thread start hand shake to verify that sensor initialization succeeded.
+  bool thread_started_;
+
+  // Initialization result (true for success).
+  bool initialization_result_;
+
+  // The callback.
+  EventConsumer consumer_;
+
+  // Sensor access
+  ALooper* looper_;
+  ASensorManager* sensor_manager_;
+  ASensorEventQueue* event_queue_;
+
+  // Sensor list from NDK.
+  ASensorList sensor_list_;
+  int sensor_count_;
+
+  // Requests to the sensor thread to enable or disable given sensors.
+  std::vector<int> enable_sensors_;
+  std::vector<int> disable_sensors_;
+
+  // A count of how many users each sensor has. Protected by user_count_mutex.
+  std::vector<int> sensor_user_count_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_SENSORD_SENSOR_NDK_THREAD_H_
diff --git a/services/vr/sensord/sensor_service.cpp b/services/vr/sensord/sensor_service.cpp
new file mode 100644
index 0000000..1b809b0
--- /dev/null
+++ b/services/vr/sensord/sensor_service.cpp
@@ -0,0 +1,187 @@
+#include "sensor_service.h"
+
+#include <hardware/sensors.h>
+#include <log/log.h>
+#include <pdx/default_transport/service_endpoint.h>
+#include <poll.h>
+#include <private/dvr/sensor-ipc.h>
+#include <time.h>
+
+using android::pdx::default_transport::Endpoint;
+
+namespace android {
+namespace dvr {
+
+SensorService::SensorService(SensorThread* sensor_thread)
+    : BASE("SensorService", Endpoint::Create(DVR_SENSOR_SERVICE_CLIENT)),
+      sensor_thread_(sensor_thread) {
+  sensor_clients_.resize(sensor_thread_->GetSensorCount());
+
+  for (int i = 0; i < sensor_thread_->GetSensorCount(); ++i)
+    type_to_sensor_[sensor_thread_->GetSensorType(i)] = i;
+}
+
+std::shared_ptr<pdx::Channel> SensorService::OnChannelOpen(pdx::Message& msg) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  const pdx::MessageInfo& info = msg.GetInfo();
+
+  std::shared_ptr<SensorClient> client(
+      new SensorClient(*this, info.pid, info.cid));
+  AddClient(client);
+  return client;
+}
+
+void SensorService::OnChannelClose(pdx::Message& /*msg*/,
+                                   const std::shared_ptr<pdx::Channel>& chan) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  auto client = std::static_pointer_cast<SensorClient>(chan);
+  if (!client) {
+    ALOGW("WARNING: SensorClient was NULL!\n");
+    return;
+  }
+  RemoveClient(client);
+}
+
+void SensorService::AddClient(const std::shared_ptr<SensorClient>& client) {
+  clients_.push_front(client);
+}
+
+void SensorService::RemoveClient(const std::shared_ptr<SensorClient>& client) {
+  // First remove it from the clients associated with its sensor, if any.
+  RemoveSensorClient(client.get());
+
+  // Finally, remove it from the list of clients we're aware of, and decrease
+  // its reference count.
+  clients_.remove(client);
+}
+
+void SensorService::RemoveSensorClient(SensorClient* client) {
+  if (!client->has_sensor())
+    return;
+
+  std::forward_list<SensorClient*>& sensor_clients =
+      sensor_clients_[client->sensor()];
+  sensor_clients.remove(client);
+  sensor_thread_->StopUsingSensor(client->sensor());
+
+  client->unset_sensor();
+}
+
+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..d868a7e
--- /dev/null
+++ b/services/vr/sensord/sensord.rc
@@ -0,0 +1,9 @@
+on init
+  mkdir /dev/socket/pdx/system/vr/pose 0775 system system
+  mkdir /dev/socket/pdx/system/vr/sensors 0775 system system
+
+service sensord /system/bin/sensord
+  class core
+  user system
+  group system camera sdcard_rw
+  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..b78eb99
--- /dev/null
+++ b/services/vr/virtual_touchpad/Android.mk
@@ -0,0 +1,104 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+
+# Touchpad implementation.
+
+src := \
+  EvdevInjector.cpp \
+  VirtualTouchpadEvdev.cpp
+
+shared_libs := \
+  libbase
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+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 \
+  libutils \
+  libvirtualtouchpad
+
+$(foreach file,$(test_src_files), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_C_INCLUDES := $(LOCAL_PATH)/include) \
+    $(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_C_INCLUDES := $(LOCAL_PATH)/include
+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)
+
+
+# Touchpad client library.
+
+src := \
+  VirtualTouchpadClient.cpp \
+  aidl/android/dvr/VirtualTouchpadService.aidl
+
+shared_libs := \
+  libbase \
+  libbinder \
+  libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(src)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+LOCAL_CPPFLAGS += -std=c++11
+LOCAL_CFLAGS += -DLOG_TAG=\"VirtualTouchpadClient\"
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE := libvirtualtouchpadclient
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
new file mode 100644
index 0000000..d8a1dfa
--- /dev/null
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -0,0 +1,311 @@
+#include "EvdevInjector.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/input.h>
+#include <log/log.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+namespace android {
+namespace dvr {
+
+int EvdevInjector::UInput::Open() {
+  errno = 0;
+  fd_.reset(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+  if (fd_.get() < 0) {
+    ALOGE("couldn't open uinput (r=%d errno=%d)", fd_.get(), errno);
+  }
+  return errno;
+}
+
+int EvdevInjector::UInput::Close() {
+  errno = 0;
+  fd_.reset();
+  return errno;
+}
+
+int EvdevInjector::UInput::Write(const void* buf, size_t count) {
+  ALOGV("UInput::Write(%zu, %02X...)", count, *static_cast<const char*>(buf));
+  errno = 0;
+  ssize_t r = write(fd_.get(), buf, count);
+  if (r != static_cast<ssize_t>(count)) {
+    ALOGE("write(%zu) failed (r=%zd errno=%d)", count, r, errno);
+  }
+  return errno;
+}
+
+int EvdevInjector::UInput::IoctlSetInt(int request, int value) {
+  ALOGV("UInput::IoctlSetInt(0x%X, 0x%X)", request, value);
+  errno = 0;
+  if (const int status = ioctl(fd_.get(), request, value)) {
+    ALOGE("ioctl(%d, 0x%X, 0x%X) failed (r=%d errno=%d)", fd_.get(), request,
+          value, status, errno);
+  }
+  return errno;
+}
+
+int EvdevInjector::UInput::IoctlVoid(int request) {
+  ALOGV("UInput::IoctlVoid(0x%X)", request);
+  errno = 0;
+  if (const int status = ioctl(fd_.get(), request)) {
+    ALOGE("ioctl(%d, 0x%X) failed (r=%d errno=%d)", fd_.get(), request, status,
+          errno);
+  }
+  return errno;
+}
+
+void EvdevInjector::Close() {
+  uinput_->Close();
+  state_ = State::CLOSED;
+}
+
+int EvdevInjector::ConfigureBegin(const char* device_name, int16_t bustype,
+                                  int16_t vendor, int16_t product,
+                                  int16_t version) {
+  ALOGV("ConfigureBegin %s 0x%04" PRIX16 " 0x%04" PRIX16 " 0x%04" PRIX16
+        " 0x%04" PRIX16 "",
+        device_name, bustype, vendor, product, version);
+  if (!device_name || strlen(device_name) >= UINPUT_MAX_NAME_SIZE) {
+    return Error(ERROR_DEVICE_NAME);
+  }
+  if (const int status = RequireState(State::NEW)) {
+    return status;
+  }
+  if (!uinput_) {
+    owned_uinput_.reset(new EvdevInjector::UInput());
+    uinput_ = owned_uinput_.get();
+  }
+  if (const int status = uinput_->Open()) {
+    // Without uinput we're dead in the water.
+    state_ = State::CLOSED;
+    return Error(status);
+  }
+  state_ = State::CONFIGURING;
+  // Initialize device setting structure.
+  memset(&uidev_, 0, sizeof(uidev_));
+  strncpy(uidev_.name, device_name, UINPUT_MAX_NAME_SIZE);
+  uidev_.id.bustype = bustype;
+  uidev_.id.vendor = vendor;
+  uidev_.id.product = product;
+  uidev_.id.version = version;
+  return 0;
+}
+
+int EvdevInjector::ConfigureInputProperty(int property) {
+  ALOGV("ConfigureInputProperty %d", property);
+  if (property < 0 || property >= INPUT_PROP_CNT) {
+    ALOGE("property 0x%X out of range [0,0x%X)", property, INPUT_PROP_CNT);
+    return Error(ERROR_PROPERTY_RANGE);
+  }
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_PROPBIT, property)) {
+    ALOGE("failed to set property %d", property);
+    return Error(status);
+  }
+  return 0;
+}
+
+int EvdevInjector::ConfigureKey(uint16_t key) {
+  ALOGV("ConfigureKey 0x%02" PRIX16 "", key);
+  if (key < 0 || key >= KEY_CNT) {
+    ALOGE("key 0x%X out of range [0,0x%X)", key, KEY_CNT);
+    return Error(ERROR_KEY_RANGE);
+  }
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (const int status = EnableEventType(EV_KEY)) {
+    return status;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_KEYBIT, key)) {
+    ALOGE("failed to enable EV_KEY 0x%02" PRIX16 "", key);
+    return Error(status);
+  }
+  return 0;
+}
+
+int EvdevInjector::ConfigureAbs(uint16_t abs_type, int32_t min, int32_t max,
+                                int32_t fuzz, int32_t flat) {
+  ALOGV("ConfigureAbs 0x%" PRIX16 " %" PRId32 " %" PRId32 " %" PRId32
+        " %" PRId32 "",
+        abs_type, min, max, fuzz, flat);
+  if (abs_type < 0 || abs_type >= ABS_CNT) {
+    ALOGE("EV_ABS type 0x%" PRIX16 " out of range [0,0x%X)", abs_type, ABS_CNT);
+    return Error(ERROR_ABS_RANGE);
+  }
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (const int status = EnableEventType(EV_ABS)) {
+    return status;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_ABSBIT, abs_type)) {
+    ALOGE("failed to enable EV_ABS 0x%" PRIX16 "", abs_type);
+    return Error(status);
+  }
+  uidev_.absmin[abs_type] = min;
+  uidev_.absmax[abs_type] = max;
+  uidev_.absfuzz[abs_type] = fuzz;
+  uidev_.absflat[abs_type] = flat;
+  return 0;
+}
+
+int EvdevInjector::ConfigureMultiTouchXY(int x0, int y0, int x1, int y1) {
+  if (const int status = ConfigureAbs(ABS_MT_POSITION_X, x0, x1, 0, 0)) {
+    return status;
+  }
+  if (const int status = ConfigureAbs(ABS_MT_POSITION_Y, y0, y1, 0, 0)) {
+    return status;
+  }
+  return 0;
+}
+
+int EvdevInjector::ConfigureAbsSlots(int slots) {
+  return ConfigureAbs(ABS_MT_SLOT, 0, slots, 0, 0);
+}
+
+int EvdevInjector::ConfigureEnd() {
+  ALOGV("ConfigureEnd:");
+  ALOGV("  name=\"%s\"", uidev_.name);
+  ALOGV("  id.bustype=0x%04" PRIX16, uidev_.id.bustype);
+  ALOGV("  id.vendor=0x%04" PRIX16, uidev_.id.vendor);
+  ALOGV("  id.product=0x%04" PRIX16, uidev_.id.product);
+  ALOGV("  id.version=0x%04" PRIX16, uidev_.id.version);
+  ALOGV("  ff_effects_max=%" PRIu32, uidev_.ff_effects_max);
+  for (int i = 0; i < ABS_CNT; ++i) {
+    if (uidev_.absmin[i]) {
+      ALOGV("  absmin[%d]=%" PRId32, i, uidev_.absmin[i]);
+    }
+    if (uidev_.absmax[i]) {
+      ALOGV("  absmax[%d]=%" PRId32, i, uidev_.absmax[i]);
+    }
+    if (uidev_.absfuzz[i]) {
+      ALOGV("  absfuzz[%d]=%" PRId32, i, uidev_.absfuzz[i]);
+    }
+    if (uidev_.absflat[i]) {
+      ALOGV("  absflat[%d]=%" PRId32, i, uidev_.absflat[i]);
+    }
+  }
+
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  // Write out device settings.
+  if (const int status = uinput_->Write(&uidev_, sizeof uidev_)) {
+    ALOGE("failed to write device settings");
+    return Error(status);
+  }
+  // Create device node.
+  if (const int status = uinput_->IoctlVoid(UI_DEV_CREATE)) {
+    ALOGE("failed to create device node");
+    return Error(status);
+  }
+  state_ = State::READY;
+  return 0;
+}
+
+int EvdevInjector::Send(uint16_t type, uint16_t code, int32_t value) {
+  ALOGV("Send(0x%" PRIX16 ", 0x%" PRIX16 ", 0x%" PRIX32 ")", type, code, value);
+  if (const int status = RequireState(State::READY)) {
+    return status;
+  }
+  struct input_event event;
+  memset(&event, 0, sizeof(event));
+  event.type = type;
+  event.code = code;
+  event.value = value;
+  if (const int status = uinput_->Write(&event, sizeof(event))) {
+    ALOGE("failed to write event 0x%" PRIX16 ", 0x%" PRIX16 ", 0x%" PRIX32,
+          type, code, value);
+    return Error(status);
+  }
+  return 0;
+}
+
+int EvdevInjector::SendSynReport() { return Send(EV_SYN, SYN_REPORT, 0); }
+
+int EvdevInjector::SendKey(uint16_t code, int32_t value) {
+  return Send(EV_KEY, code, value);
+}
+
+int EvdevInjector::SendAbs(uint16_t code, int32_t value) {
+  return Send(EV_ABS, code, value);
+}
+
+int EvdevInjector::SendMultiTouchSlot(int32_t slot) {
+  if (latest_slot_ != slot) {
+    if (const int status = SendAbs(ABS_MT_SLOT, slot)) {
+      return status;
+    }
+    latest_slot_ = slot;
+  }
+  return 0;
+}
+
+int EvdevInjector::SendMultiTouchXY(int32_t slot, int32_t id, int32_t x,
+                                    int32_t y) {
+  if (const int status = SendMultiTouchSlot(slot)) {
+    return status;
+  }
+  if (const int status = SendAbs(ABS_MT_TRACKING_ID, id)) {
+    return status;
+  }
+  if (const int status = SendAbs(ABS_MT_POSITION_X, x)) {
+    return status;
+  }
+  if (const int status = SendAbs(ABS_MT_POSITION_Y, y)) {
+    return status;
+  }
+  return 0;
+}
+
+int EvdevInjector::SendMultiTouchLift(int32_t slot) {
+  if (const int status = SendMultiTouchSlot(slot)) {
+    return status;
+  }
+  if (const int status = SendAbs(ABS_MT_TRACKING_ID, -1)) {
+    return status;
+  }
+  return 0;
+}
+
+int EvdevInjector::Error(int code) {
+  if (!error_) {
+    error_ = code;
+  }
+  return code;
+}
+
+int EvdevInjector::RequireState(State required_state) {
+  if (error_) {
+    return error_;
+  }
+  if (state_ != required_state) {
+    ALOGE("in state %d but require state %d", static_cast<int>(state_),
+          static_cast<int>(required_state));
+    return Error(ERROR_SEQUENCING);
+  }
+  return 0;
+}
+
+int EvdevInjector::EnableEventType(uint16_t type) {
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (enabled_event_types_.count(type) > 0) {
+    return 0;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_EVBIT, type)) {
+    ALOGE("failed to enable event type 0x%X", type);
+    return Error(status);
+  }
+  enabled_event_types_.insert(type);
+  return 0;
+}
+
+}  // 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/VirtualTouchpadClient.cpp b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
new file mode 100644
index 0000000..23a2e31
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
@@ -0,0 +1,52 @@
+#include "VirtualTouchpadClient.h"
+
+#include <android/dvr/IVirtualTouchpadService.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class VirtualTouchpadClientImpl : public VirtualTouchpadClient {
+ public:
+  VirtualTouchpadClientImpl(sp<IVirtualTouchpadService> service)
+      : service_(service) {}
+  ~VirtualTouchpadClientImpl() {}
+
+  status_t Touch(float x, float y, float pressure) override {
+    if (service_ == nullptr) {
+      return NO_INIT;
+    }
+    return service_->touch(x, y, pressure).transactionError();
+  }
+  status_t ButtonState(int buttons) override {
+    if (service_ == nullptr) {
+      return NO_INIT;
+    }
+    return service_->buttonState(buttons).transactionError();
+  }
+
+ private:
+  sp<IVirtualTouchpadService> service_;
+};
+
+}  // anonymous namespace
+
+sp<VirtualTouchpad> VirtualTouchpadClient::Create() {
+  sp<IServiceManager> sm = defaultServiceManager();
+  if (sm == nullptr) {
+    ALOGE("no service manager");
+    return sp<VirtualTouchpad>();
+  }
+  sp<IVirtualTouchpadService> service = interface_cast<IVirtualTouchpadService>(
+      sm->getService(IVirtualTouchpadService::SERVICE_NAME()));
+  if (service == nullptr) {
+    ALOGE("failed to get service");
+    return sp<VirtualTouchpad>();
+  }
+  return new VirtualTouchpadClientImpl(service);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
new file mode 100644
index 0000000..ae31156
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
@@ -0,0 +1,130 @@
+#include "VirtualTouchpadEvdev.h"
+
+#include <android/input.h>
+#include <inttypes.h>
+#include <linux/input.h>
+#include <log/log.h>
+
+// References:
+//  [0] Multi-touch (MT) Protocol,
+//      https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+// Virtual evdev device properties. The name is arbitrary, but Android can
+// use it to look up device configuration, so it must be unique. Vendor and
+// product values must be 0 to indicate an internal device and prevent a
+// similar lookup that could conflict with a physical device.
+static const char* const 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
+
+sp<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
+  VirtualTouchpadEvdev* const touchpad = new VirtualTouchpadEvdev();
+  const status_t status = touchpad->Initialize();
+  if (status) {
+    ALOGE("initialization failed: %d", status);
+    return sp<VirtualTouchpad>();
+  }
+  return sp<VirtualTouchpad>(touchpad);
+}
+
+int VirtualTouchpadEvdev::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 VirtualTouchpadEvdev::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 VirtualTouchpadEvdev::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/VirtualTouchpadEvdev.h b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
new file mode 100644
index 0000000..c763529
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
@@ -0,0 +1,59 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H
+
+#include <memory>
+
+#include "VirtualTouchpad.h"
+#include "EvdevInjector.h"
+
+namespace android {
+namespace dvr {
+
+class EvdevInjector;
+
+// VirtualTouchpadEvdev implements a VirtualTouchpad by injecting evdev events.
+//
+class VirtualTouchpadEvdev : public VirtualTouchpad {
+ public:
+  static sp<VirtualTouchpad> Create();
+
+  // VirtualTouchpad implementation:
+  status_t Touch(float x, float y, float pressure) override;
+  status_t ButtonState(int buttons) override;
+
+ protected:
+  VirtualTouchpadEvdev() {}
+  ~VirtualTouchpadEvdev() {}
+  status_t Initialize();
+
+  // 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;
+
+  VirtualTouchpadEvdev(const VirtualTouchpadEvdev&) = delete;
+  void operator=(const VirtualTouchpadEvdev&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_EVDEV_H
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
new file mode 100644
index 0000000..3fcb8fc
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -0,0 +1,24 @@
+#include "VirtualTouchpadService.h"
+
+#include <binder/Status.h>
+#include <linux/input.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+namespace android {
+namespace dvr {
+
+binder::Status VirtualTouchpadService::touch(float x, float y, float pressure) {
+  const status_t error = touchpad_->Touch(x, y, pressure);
+  return error ? binder::Status::fromStatusT(error)
+               : binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::buttonState(int buttons) {
+  const status_t error = touchpad_->ButtonState(buttons);
+  return error ? binder::Status::fromStatusT(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..b832c8f
--- /dev/null
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.h
@@ -0,0 +1,34 @@
+#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(sp<VirtualTouchpad> touchpad)
+      : touchpad_(touchpad) {}
+
+ protected:
+  // Implements IVirtualTouchpadService.
+  binder::Status touch(float x, float y, float pressure) override;
+  binder::Status buttonState(int buttons) override;
+
+ private:
+  sp<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..c2044da
--- /dev/null
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
@@ -0,0 +1,25 @@
+package android.dvr;
+
+/** @hide */
+interface VirtualTouchpadService
+{
+  const String SERVICE_NAME = "virtual_touchpad";
+
+  /**
+   * 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/include/VirtualTouchpad.h b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
new file mode 100644
index 0000000..bbaf69b
--- /dev/null
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
@@ -0,0 +1,57 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace dvr {
+
+// Provides a virtual touchpad for injecting events into the input system.
+//
+class VirtualTouchpad : public RefBase {
+ public:
+  // Create a virtual touchpad.
+  // Implementations should provide this, and hide their constructors.
+  // For the user, switching implementations should be as simple as changing
+  // the class whose |Create()| is called.
+  static sp<VirtualTouchpad> Create() {
+    return sp<VirtualTouchpad>();
+  }
+
+  // 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 OK on success.
+  //
+  virtual status_t Touch(float x, float y, float pressure) = 0;
+
+  // Generate a simulated touchpad button state.
+  //
+  // @param buttons A union of MotionEvent BUTTON_* values.
+  // @returns OK on success.
+  //
+  // Currently only BUTTON_BACK is supported, as the implementation
+  // restricts itself to operations actually required by VrWindowManager.
+  //
+  virtual status_t ButtonState(int buttons) = 0;
+
+ protected:
+  VirtualTouchpad() {}
+  virtual ~VirtualTouchpad() {}
+
+ private:
+  VirtualTouchpad(const VirtualTouchpad&) = delete;
+  void operator=(const VirtualTouchpad&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_INTERFACE_H
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
new file mode 100644
index 0000000..46bec0e
--- /dev/null
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
+#define ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
+
+#include "VirtualTouchpad.h"
+
+namespace android {
+namespace dvr {
+
+// VirtualTouchpadClient implements a VirtualTouchpad by connecting to
+// a VirtualTouchpadService over Binder.
+//
+class VirtualTouchpadClient : public VirtualTouchpad {
+ public:
+  // VirtualTouchpad implementation:
+  static sp<VirtualTouchpad> Create();
+  status_t Touch(float x, float y, float pressure) override;
+  status_t ButtonState(int buttons) override;
+
+ protected:
+  VirtualTouchpadClient() {}
+  virtual ~VirtualTouchpadClient() {}
+
+ private:
+  VirtualTouchpadClient(const VirtualTouchpadClient&) = delete;
+  void operator=(const VirtualTouchpadClient&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VIRTUAL_TOUCHPAD_CLIENT_H
diff --git a/services/vr/virtual_touchpad/main.cpp b/services/vr/virtual_touchpad/main.cpp
new file mode 100644
index 0000000..e73f8b9
--- /dev/null
+++ b/services/vr/virtual_touchpad/main.cpp
@@ -0,0 +1,32 @@
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <log/log.h>
+
+#include "VirtualTouchpadEvdev.h"
+#include "VirtualTouchpadService.h"
+
+int main() {
+  ALOGI("Starting");
+  android::dvr::VirtualTouchpadService touchpad_service(
+      android::dvr::VirtualTouchpadEvdev::Create());
+
+  signal(SIGPIPE, SIG_IGN);
+  android::sp<android::ProcessState> ps(android::ProcessState::self());
+  ps->setThreadPoolMaxThreadCount(4);
+  ps->startThreadPool();
+  ps->giveThreadPoolName();
+
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+  const android::status_t service_status =
+      sm->addService(android::String16(touchpad_service.SERVICE_NAME()),
+                     &touchpad_service, false /*allowIsolated*/);
+  if (service_status != android::OK) {
+    ALOGE("virtual touchpad service not added: %d",
+          static_cast<int>(service_status));
+    exit(2);
+  }
+
+  android::IPCThreadState::self()->joinThreadPool();
+  return 0;
+}
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
new file mode 100644
index 0000000..b448fd1
--- /dev/null
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -0,0 +1,256 @@
+#include <android/input.h>
+#include <gtest/gtest.h>
+#include <linux/input.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "EvdevInjector.h"
+#include "VirtualTouchpadEvdev.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+class UInputForTesting : public EvdevInjector::UInput {
+ public:
+  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 VirtualTouchpadEvdev {
+ public:
+  static sp<VirtualTouchpad> Create(EvdevInjectorForTesting& injector) {
+    VirtualTouchpadForTesting* const touchpad = new VirtualTouchpadForTesting();
+    touchpad->SetEvdevInjectorForTesting(&injector);
+    touchpad->Initialize();
+    return sp<VirtualTouchpad>(touchpad);
+  }
+};
+
+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);
+  sp<VirtualTouchpad> touchpad(VirtualTouchpadForTesting::Create(injector));
+
+  // 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);
+  sp<VirtualTouchpad> touchpad(
+      VirtualTouchpadForTesting::Create(injector));
+
+  // Touch off-screen should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  status_t touch_status = touchpad->Touch(-0.25f, 0.75f, 1.0f);
+  EXPECT_NE(OK, touch_status);
+  touch_status = touchpad->Touch(0.25f, -0.75f, 1.0f);
+  EXPECT_NE(OK, touch_status);
+  touch_status = touchpad->Touch(1.25f, 0.75f, 1.0f);
+  EXPECT_NE(OK, touch_status);
+  touch_status = touchpad->Touch(0.25f, 1.75f, 1.0f);
+  EXPECT_NE(OK, touch_status);
+  EXPECT_EQ(expect.GetString(), record.GetString());
+
+  // Unsupported button should return an error,
+  // and should not result in any system calls.
+  expect.Reset();
+  record.Reset();
+  touch_status = touchpad->ButtonState(AMOTION_EVENT_BUTTON_FORWARD);
+  EXPECT_NE(OK, 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_window_manager/Android.mk b/services/vr/vr_window_manager/Android.mk
new file mode 100644
index 0000000..ddb58e9
--- /dev/null
+++ b/services/vr/vr_window_manager/Android.mk
@@ -0,0 +1,99 @@
+# 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 \
+  shell_view.cpp \
+  surface_flinger_view.cpp \
+  texture.cpp \
+  vr_window_manager.cpp \
+  vr_window_manager_binder.cpp \
+  aidl/android/service/vr/IVrWindowManager.aidl
+
+static_libs := \
+  libdisplay \
+  libbufferhub \
+  libbufferhubqueue \
+  libeds \
+  libdvrgraphics \
+  libdvrcommon \
+  libhwcomposer-client \
+  libsensor \
+  libperformance \
+  libpdx_default_transport \
+  libcutils \
+  libvr_manager \
+  libvirtualtouchpadclient
+
+shared_libs := \
+  android.dvr.composer@1.0 \
+  android.hardware.graphics.composer@2.1 \
+  libvrhwc \
+  libbase \
+  libbinder \
+  libinput \
+  libhardware \
+  libhwbinder \
+  libsync \
+  libutils \
+  libgui \
+  libEGL \
+  libGLESv2 \
+  libvulkan \
+  libsync \
+  libui \
+  libhidlbase \
+  libhidltransport
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(native_src)
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+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)
+
+cmd_src := \
+  vr_wm_ctl.cpp \
+  aidl/android/service/vr/IVrWindowManager.aidl
+
+static_libs := \
+  libcutils
+
+shared_libs := \
+  libbase \
+  libbinder \
+  libutils
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(cmd_src)
+LOCAL_STATIC_LIBRARIES := $(static_libs)
+LOCAL_SHARED_LIBRARIES := $(shared_libs)
+LOCAL_CPPFLAGS += -std=c++11
+LOCAL_CFLAGS += -DLOG_TAG=\"vrwmctl\"
+LOCAL_LDLIBS := -llog
+LOCAL_MODULE := vr_wm_ctl
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
diff --git a/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl b/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl
new file mode 100644
index 0000000..b5dbb8b
--- /dev/null
+++ b/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.vr;
+
+/** @hide */
+interface IVrWindowManager {
+    const String SERVICE_NAME = "vr_window_manager";
+    void connectController(in FileDescriptor fd) = 0;
+    void disconnectController() = 1;
+    void enterVrMode() = 2;
+    void exitVrMode() = 3;
+    void setDebugMode(int mode) = 4;
+}
+
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
new file mode 100644
index 0000000..24087c9
--- /dev/null
+++ b/services/vr/vr_window_manager/application.cpp
@@ -0,0 +1,325 @@
+#include "application.h"
+
+#include <inttypes.h>
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+#include <binder/IServiceManager.h>
+#include <dvr/graphics.h>
+#include <dvr/performance_client_api.h>
+#include <dvr/pose_client.h>
+#include <gui/ISurfaceComposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
+#include <private/dvr/graphics/vr_gl_extensions.h>
+
+#include <vector>
+
+namespace android {
+namespace dvr {
+
+Application::Application() {
+  vr_mode_listener_ = new VrModeListener(this);
+}
+
+Application::~Application() {
+  sp<IVrManager> vrManagerService = interface_cast<IVrManager>(
+      defaultServiceManager()->getService(String16("vrmanager")));
+  if (vrManagerService.get()) {
+    vrManagerService->unregisterListener(vr_mode_listener_);
+  }
+}
+
+int Application::Initialize() {
+  dvrSetCpuPartition(0, "/application/performance");
+
+  bool is_right_handed = true;  // TODO: retrieve setting from system
+  elbow_model_.Enable(ElbowModel::kDefaultNeckPosition, is_right_handed);
+  last_frame_time_ = std::chrono::system_clock::now();
+
+  sp<IVrManager> vrManagerService = interface_cast<IVrManager>(
+      defaultServiceManager()->getService(String16("vrmanager")));
+  if (vrManagerService.get()) {
+    vrManagerService->registerListener(vr_mode_listener_);
+  }
+  return 0;
+}
+
+int Application::AllocateResources() {
+  int surface_width = 0, surface_height = 0;
+  DvrLensInfo lens_info = {};
+  GLuint texture_id = 0;
+  GLenum texture_target = 0;
+  std::vector<DvrSurfaceParameter> surface_params = {
+    DVR_SURFACE_PARAMETER_OUT(SURFACE_WIDTH, &surface_width),
+    DVR_SURFACE_PARAMETER_OUT(SURFACE_HEIGHT, &surface_height),
+    DVR_SURFACE_PARAMETER_OUT(INTER_LENS_METERS, &lens_info.inter_lens_meters),
+    DVR_SURFACE_PARAMETER_OUT(LEFT_FOV_LRBT, &lens_info.left_fov),
+    DVR_SURFACE_PARAMETER_OUT(RIGHT_FOV_LRBT, &lens_info.right_fov),
+    DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_TYPE, &texture_target),
+    DVR_SURFACE_PARAMETER_OUT(SURFACE_TEXTURE_TARGET_ID, &texture_id),
+    DVR_SURFACE_PARAMETER_IN(VISIBLE, 0),
+    DVR_SURFACE_PARAMETER_IN(Z_ORDER, 1),
+    DVR_SURFACE_PARAMETER_IN(GEOMETRY, DVR_SURFACE_GEOMETRY_SINGLE),
+    DVR_SURFACE_PARAMETER_IN(ENABLE_LATE_LATCH, 0),
+    DVR_SURFACE_PARAMETER_IN(DISABLE_DISTORTION, 0),
+    DVR_SURFACE_PARAMETER_LIST_END,
+  };
+
+  int ret = dvrGraphicsContextCreate(surface_params.data(), &graphics_context_);
+  if (ret)
+    return ret;
+
+  GLuint fbo = 0;
+  GLuint depth_stencil_buffer = 0;
+  GLuint samples = 1;
+  glGenFramebuffers(1, &fbo);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+  glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                       texture_target, texture_id, 0, samples);
+
+  glGenRenderbuffers(1, &depth_stencil_buffer);
+  glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer);
+  glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
+                                   GL_DEPTH_COMPONENT24, surface_width,
+                                   surface_height);
+
+  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                            GL_RENDERBUFFER, depth_stencil_buffer);
+
+  ALOGI("Surface size=%dx%d", surface_width, surface_height);
+  pose_client_ = dvrPoseCreate();
+  if (!pose_client_)
+    return 1;
+
+  vec2i eye_size(surface_width / 2, surface_height);
+
+  eye_viewport_[0] = Range2i::FromSize(vec2i(0, 0), eye_size);
+  eye_viewport_[1] = Range2i::FromSize(vec2i(surface_width / 2, 0), eye_size);
+
+  eye_from_head_[0] = Eigen::Translation3f(
+      vec3(lens_info.inter_lens_meters * 0.5f, 0.0f, 0.0f));
+  eye_from_head_[1] = Eigen::Translation3f(
+      vec3(-lens_info.inter_lens_meters * 0.5f, 0.0f, 0.0f));
+
+  fov_[0] = FieldOfView(lens_info.left_fov[0], lens_info.left_fov[1],
+                        lens_info.left_fov[2], lens_info.left_fov[3]);
+  fov_[1] = FieldOfView(lens_info.right_fov[0], lens_info.right_fov[1],
+                        lens_info.right_fov[2], lens_info.right_fov[3]);
+
+  return 0;
+}
+
+void Application::DeallocateResources() {
+  if (graphics_context_)
+    dvrGraphicsContextDestroy(graphics_context_);
+
+  if (pose_client_)
+    dvrPoseDestroy(pose_client_);
+
+  initialized_ = false;
+}
+
+void Application::ProcessTasks(const std::vector<MainThreadTask>& tasks) {
+  for (auto task : tasks) {
+    switch (task) {
+      case MainThreadTask::EnableDebugMode:
+        if (!debug_mode_) {
+          debug_mode_ = true;
+          SetVisibility(debug_mode_);
+        }
+        break;
+      case MainThreadTask::DisableDebugMode:
+        if (debug_mode_) {
+          debug_mode_ = false;
+          SetVisibility(debug_mode_);
+        }
+        break;
+      case MainThreadTask::EnteringVrMode:
+        if (!initialized_) {
+          LOG_ALWAYS_FATAL_IF(AllocateResources(),
+                              "Failed to allocate resources");
+        }
+        break;
+      case MainThreadTask::ExitingVrMode:
+        if (initialized_)
+          DeallocateResources();
+        break;
+      case MainThreadTask::Show:
+        if (!is_visible_)
+          SetVisibility(true);
+        break;
+    }
+  }
+}
+
+void Application::DrawFrame() {
+  // Thread should block if we are invisible or not fully initialized.
+  std::unique_lock<std::mutex> lock(mutex_);
+  wake_up_init_and_render_.wait(lock, [this]() {
+    return (is_visible_ && initialized_) || !main_thread_tasks_.empty();
+  });
+
+  // Process main thread tasks if there are any.
+  std::vector<MainThreadTask> tasks;
+  tasks.swap(main_thread_tasks_);
+  lock.unlock();
+
+  if (!tasks.empty())
+    ProcessTasks(tasks);
+
+  if (!initialized_)
+    return;
+
+  // TODO(steventhomas): If we're not visible, block until we are. For now we
+  // throttle by calling dvrGraphicsWaitNextFrame.
+  DvrFrameSchedule schedule;
+  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;
+
+    controller_position_ = elbow_model_.Update(delta, last_pose_.GetRotation(),
+                                               controller_orientation_, 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_data_provider_) {
+    shmem_controller_active_ = false;
+    const void* data = controller_data_provider_->LockControllerData();
+    // TODO(kpschoedel): define wire format.
+    if (data) {
+      struct wire_format {
+        uint32_t version;
+        uint32_t timestamph;
+        uint32_t timestampl;
+        uint32_t quat_count;
+        float q[4];
+        uint32_t buttonsh;
+        uint32_t buttonsl;
+      } __attribute__((__aligned__(32)));
+      const wire_format* wire_data = static_cast<const wire_format*>(data);
+      static uint64_t last_timestamp = 0;
+      if (wire_data->version == 1) {
+        shmem_controller_active_ = true;
+        uint64_t timestamp =
+            (((uint64_t)wire_data->timestamph) << 32) | wire_data->timestampl;
+        if (timestamp == last_timestamp) {
+          static uint64_t last_logged_timestamp = 0;
+          if (last_logged_timestamp != last_timestamp) {
+            last_logged_timestamp = last_timestamp;
+            ALOGI("Controller shmem stale T=0x%" PRIX64, last_timestamp);
+          }
+        } else {
+          last_timestamp = timestamp;
+          controller_orientation_ = quat(wire_data->q[3], wire_data->q[0],
+                                         wire_data->q[1], wire_data->q[2]);
+          shmem_controller_buttons_ =
+              (((uint64_t)wire_data->buttonsh) << 32) | wire_data->buttonsl;
+        }
+      } else if (wire_data->version == 0xFEEDFACE) {
+        static bool logged_init = false;
+        if (!logged_init) {
+          logged_init = true;
+          ALOGI("Controller shmem waiting for data");
+        }
+      }
+    }
+    controller_data_provider_->UnlockControllerData();
+    if (shmem_controller_active_) {
+      // TODO(kpschoedel): change to ALOGV or remove.
+      ALOGI("Controller shmem orientation: %f %f %f %f",
+            controller_orientation_.x(), controller_orientation_.y(),
+            controller_orientation_.z(), controller_orientation_.w());
+      if (shmem_controller_buttons_) {
+        ALOGI("Controller shmem buttons: %017" PRIX64,
+            shmem_controller_buttons_);
+      }
+      return;
+    }
+  }
+}
+
+void Application::SetVisibility(bool visible) {
+  if (visible && !initialized_) {
+    if (AllocateResources())
+      ALOGE("Failed to allocate resources");
+  }
+
+  bool changed = is_visible_ != visible;
+  if (changed) {
+    is_visible_ = visible;
+    // TODO (alexst): b/36036583 Disable vr_wm visibility until we figure out
+    // why it's always on top. Still make it visible in debug mode.
+    if (debug_mode_)
+      dvrGraphicsSurfaceSetVisible(graphics_context_, is_visible_);
+    OnVisibilityChanged(is_visible_);
+  }
+}
+
+void Application::OnVisibilityChanged(bool visible) {
+  if (visible) {
+    fade_value_ = 0;
+    // We have been sleeping so to ensure correct deltas, reset the time.
+    last_frame_time_ = std::chrono::system_clock::now();
+  }
+}
+
+void Application::QueueTask(MainThreadTask task) {
+  std::unique_lock<std::mutex> lock(mutex_);
+  main_thread_tasks_.push_back(task);
+  wake_up_init_and_render_.notify_one();
+}
+
+void Application::VrModeListener::onVrStateChanged(bool enabled) {
+  if (!enabled)
+    app_->QueueTask(MainThreadTask::ExitingVrMode);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/application.h b/services/vr/vr_window_manager/application.h
new file mode 100644
index 0000000..2c60e0a
--- /dev/null
+++ b/services/vr/vr_window_manager/application.h
@@ -0,0 +1,107 @@
+#ifndef VR_WINDOW_MANAGER_APPLICATION_H_
+#define VR_WINDOW_MANAGER_APPLICATION_H_
+
+#include <memory>
+#include <private/dvr/types.h>
+#include <stdint.h>
+#include <vr/vr_manager/vr_manager.h>
+
+#include <chrono>
+#include <mutex>
+#include <vector>
+
+#include "controller_data_provider.h"
+#include "elbow_model.h"
+
+struct DvrGraphicsContext;
+struct DvrPose;
+
+namespace android {
+namespace dvr {
+
+class Application {
+ public:
+  Application();
+  virtual ~Application();
+
+  virtual int Initialize();
+
+  virtual int AllocateResources();
+  virtual void DeallocateResources();
+
+  void DrawFrame();
+
+  void SetControllerDataProvider(ControllerDataProvider* provider) {
+    controller_data_provider_ = provider;
+  }
+
+ protected:
+  enum class MainThreadTask {
+    EnteringVrMode,
+    ExitingVrMode,
+    EnableDebugMode,
+    DisableDebugMode,
+    Show,
+  };
+
+  class VrModeListener : public BnVrStateCallbacks {
+   public:
+    VrModeListener(Application *app) : app_(app) {}
+    void onVrStateChanged(bool enabled) override;
+
+   private:
+    Application *app_;
+  };
+
+  sp<VrModeListener> vr_mode_listener_;
+  virtual void OnDrawFrame() = 0;
+  virtual void DrawEye(EyeType eye, const mat4& perspective,
+                       const mat4& eye_matrix, const mat4& head_matrix) = 0;
+
+  void SetVisibility(bool visible);
+  virtual void OnVisibilityChanged(bool visible);
+
+  void ProcessControllerInput();
+
+  void ProcessTasks(const std::vector<MainThreadTask>& tasks);
+
+  void QueueTask(MainThreadTask task);
+
+  DvrGraphicsContext* graphics_context_ = nullptr;
+  DvrPose* pose_client_ = nullptr;
+
+  Range2i eye_viewport_[2];
+  mat4 eye_from_head_[2];
+  FieldOfView fov_[2];
+  Posef last_pose_;
+
+  quat controller_orientation_;
+  bool shmem_controller_active_ = false;
+  uint64_t shmem_controller_buttons_;
+
+  bool is_visible_ = false;
+  std::chrono::time_point<std::chrono::system_clock> visibility_button_press_;
+  bool debug_mode_ = false;
+
+  std::chrono::time_point<std::chrono::system_clock> last_frame_time_;
+  vec3 controller_position_;
+  ElbowModel elbow_model_;
+
+  float fade_value_ = 0;
+
+  std::mutex mutex_;
+  std::condition_variable wake_up_init_and_render_;
+  bool initialized_ = false;
+  std::vector<MainThreadTask> main_thread_tasks_;
+
+  // Controller data provider from shared memory buffer.
+  ControllerDataProvider* controller_data_provider_ = nullptr;
+
+  Application(const Application&) = delete;
+  void operator=(const Application&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_APPLICATION_H_
diff --git a/services/vr/vr_window_manager/composer/1.0/Android.bp b/services/vr/vr_window_manager/composer/1.0/Android.bp
new file mode 100644
index 0000000..58f83f8
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/1.0/Android.bp
@@ -0,0 +1,54 @@
+// 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",
+    ],
+    out: [
+        "android/dvr/composer/1.0/VrComposerClientAll.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",
+    ],
+    out: [
+        "android/dvr/composer/1.0/IVrComposerClient.h",
+        "android/dvr/composer/1.0/IHwVrComposerClient.h",
+        "android/dvr/composer/1.0/BnHwVrComposerClient.h",
+        "android/dvr/composer/1.0/BpHwVrComposerClient.h",
+        "android/dvr/composer/1.0/BsVrComposerClient.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/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/Android.bp b/services/vr/vr_window_manager/composer/Android.bp
new file mode 100644
index 0000000..7c8bb86
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/Android.bp
@@ -0,0 +1,38 @@
+subdirs = [
+  "1.0",
+]
+
+cc_library_shared {
+  name: "libvrhwc",
+
+  srcs: [
+    "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: ["."],
+
+  cflags: [
+    "-DLOG_TAG=\"vrhwc\"",
+  ],
+}
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..299e8f1
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_composer_view.cpp
@@ -0,0 +1,28 @@
+#include "vr_composer_view.h"
+
+namespace android {
+namespace dvr {
+
+VrComposerView::VrComposerView(
+    std::unique_ptr<VrComposerView::Callback> callback)
+    : composer_view_(nullptr), callback_(std::move(callback)) {}
+
+VrComposerView::~VrComposerView() {
+  composer_view_->UnregisterObserver(this);
+}
+
+void VrComposerView::Initialize(ComposerView* composer_view) {
+  composer_view_ = composer_view;
+  composer_view_->RegisterObserver(this);
+}
+
+base::unique_fd VrComposerView::OnNewFrame(const ComposerView::Frame& frame) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (!callback_.get())
+    return base::unique_fd();
+
+  return callback_->OnNewFrame(frame);
+}
+
+}  // 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..8c5ee1f
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_composer_view.h
@@ -0,0 +1,36 @@
+#ifndef VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_VIEW_H_
+#define VR_WINDOW_MANAGER_COMPOSER_IMPL_VR_COMPOSER_VIEW_H_
+
+#include <memory>
+
+#include "vr_hwc.h"
+
+namespace android {
+namespace dvr {
+
+class VrComposerView : public ComposerView::Observer {
+ public:
+  class Callback {
+   public:
+    virtual ~Callback() = default;
+    virtual base::unique_fd OnNewFrame(const ComposerView::Frame& frame) = 0;
+  };
+
+  VrComposerView(std::unique_ptr<Callback> callback);
+  ~VrComposerView() override;
+
+  void Initialize(ComposerView* composer_view);
+
+  // ComposerView::Observer
+  base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override;
+
+ private:
+  ComposerView* composer_view_;
+  std::unique_ptr<Callback> callback_;
+  std::mutex mutex_;
+};
+
+}  // 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..6a78c98
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.cpp
@@ -0,0 +1,676 @@
+/*
+ * 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 <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include <mutex>
+
+#include "vr_composer_client.h"
+
+using namespace android::hardware::graphics::common::V1_0;
+using namespace android::hardware::graphics::composer::V2_1;
+
+using android::hardware::hidl_handle;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+
+namespace android {
+namespace dvr {
+namespace {
+
+using android::hardware::graphics::common::V1_0::PixelFormat;
+
+const Display kDefaultDisplayId = 1;
+const Config kDefaultConfigId = 1;
+
+sp<GraphicBuffer> GetBufferFromHandle(const native_handle_t* handle) {
+  uint32_t width = 0, height = 0, stride = 0, layer_count = 1;
+  uint64_t producer_usage = 0, consumer_usage = 0;
+  int32_t format = 0;
+
+  GraphicBufferMapper& mapper = GraphicBufferMapper::get();
+  // Need to register |handle| otherwise we can't read its properties.
+  if (mapper.registerBuffer(handle) != OK) {
+    ALOGE("Failed to register buffer");
+    return nullptr;
+  }
+
+  if (mapper.getDimensions(handle, &width, &height) ||
+      mapper.getStride(handle, &stride) ||
+      mapper.getFormat(handle, &format) ||
+      mapper.getProducerUsage(handle, &producer_usage) ||
+      mapper.getConsumerUsage(handle, &consumer_usage)) {
+    ALOGE("Failed to read handle properties");
+    return nullptr;
+  }
+
+  // This will only succeed if gralloc has GRALLOC1_CAPABILITY_LAYERED_BUFFERS
+  // capability. Otherwise assume a count of 1.
+  mapper.getLayerCount(handle, &layer_count);
+
+  // NOTE: Can't re-use |handle| since we don't own it.
+  sp<GraphicBuffer> buffer = new GraphicBuffer(
+      width, height, format, layer_count, producer_usage, consumer_usage,
+      stride, native_handle_clone(handle), true);
+  // Need to register the cloned buffer otherwise it can't be used later on.
+  if (mapper.registerBuffer(buffer.get()) != OK) {
+    ALOGE("Failed to register cloned buffer");
+    return nullptr;
+  }
+
+  return buffer;
+}
+
+}  // namespace
+
+HwcDisplay::HwcDisplay() {}
+
+HwcDisplay::~HwcDisplay() {}
+
+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].info.id == id) return &layers_[i];
+
+  return nullptr;
+}
+
+bool HwcDisplay::DestroyLayer(Layer id) {
+  for (auto it = layers_.begin(); it != layers_.end(); ++it) {
+    if (it->info.id == id) {
+      layers_.erase(it);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void HwcDisplay::GetChangedCompositionTypes(
+    std::vector<Layer>* layer_ids,
+    std::vector<IComposerClient::Composition>* types) {
+  std::sort(layers_.begin(), layers_.end(),
+            [](const auto& lhs, const auto& rhs) {
+              return lhs.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].info.id);
+        types->push_back(IComposerClient::Composition::CLIENT);
+        layers_[i].composition_type = IComposerClient::Composition::CLIENT;
+      }
+
+      continue;
+    }
+
+    if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) {
+      layer_ids->push_back(layers_[i].info.id);
+      types->push_back(IComposerClient::Composition::DEVICE);
+      layers_[i].composition_type = IComposerClient::Composition::DEVICE;
+    }
+  }
+}
+
+Error HwcDisplay::GetFrame(
+    std::vector<ComposerView::ComposerLayer>* out_frames) {
+  bool queued_client_target = false;
+  std::vector<ComposerView::ComposerLayer> frame;
+  for (const auto& layer : layers_) {
+    if (layer.composition_type == IComposerClient::Composition::CLIENT) {
+      if (queued_client_target)
+        continue;
+
+      if (!buffer_.get()) {
+        ALOGE("Client composition requested but no client target buffer");
+        return Error::BAD_LAYER;
+      }
+
+      ComposerView::ComposerLayer client_target_layer = {
+          .buffer = buffer_,
+          .fence = fence_.get() ? fence_ : new Fence(-1),
+          .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
+            static_cast<int32_t>(buffer_->getHeight())},
+          .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
+            static_cast<float>(buffer_->getHeight())},
+          .blend_mode = IComposerClient::BlendMode::NONE,
+      };
+
+      frame.push_back(client_target_layer);
+      queued_client_target = true;
+    } else {
+      if (!layer.info.buffer.get() || !layer.info.fence.get()) {
+        ALOGE("Layer requested without valid buffer");
+        return Error::BAD_LAYER;
+      }
+
+      frame.push_back(layer.info);
+    }
+  }
+
+  if (frame.empty()) {
+    ALOGE("Requested frame with no layers");
+    return Error::BAD_LAYER;
+  }
+
+  out_frames->swap(frame);
+  return Error::NONE;
+}
+
+std::vector<Layer> HwcDisplay::UpdateLastFrameAndGetLastFrameLayers() {
+  std::vector<Layer> last_frame_layers;
+  last_frame_layers.swap(last_frame_layers_ids_);
+
+  for (const auto& layer : layers_)
+    last_frame_layers_ids_.push_back(layer.info.id);
+
+  return last_frame_layers;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VrHwcClient
+
+VrHwc::VrHwc() {}
+
+VrHwc::~VrHwc() {}
+
+bool VrHwc::hasCapability(Capability capability) const { return false; }
+
+void VrHwc::removeClient() {
+  std::lock_guard<std::mutex> guard(mutex_);
+  client_ = nullptr;
+}
+
+void VrHwc::enableCallback(bool enable) {
+  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->info.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;
+
+  ALOGE("Virtual display support not implemented");
+  return Error::UNSUPPORTED;
+}
+
+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;
+  outLayers->clear();
+  outReleaseFences->clear();
+
+  if (display != kDefaultDisplayId) {
+    return Error::BAD_DISPLAY;
+  }
+
+  std::vector<ComposerView::ComposerLayer> frame;
+  std::vector<Layer> last_frame_layers;
+  std::lock_guard<std::mutex> guard(mutex_);
+  Error status = display_.GetFrame(&frame);
+  if (status != Error::NONE)
+    return status;
+
+  last_frame_layers = display_.UpdateLastFrameAndGetLastFrameLayers();
+
+  base::unique_fd fence;
+  if (observer_)
+    fence = observer_->OnNewFrame(frame);
+
+  if (fence.get() < 0)
+    return Error::NONE;
+
+  *outPresentFence = dup(fence.get());
+  outLayers->swap(last_frame_layers);
+  for (size_t i = 0; i < outLayers->size(); ++i)
+    outReleaseFences->push_back(dup(fence.get()));
+
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
+                                    int32_t y) {
+  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;
+}
+
+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..df09687
--- /dev/null
+++ b/services/vr/vr_window_manager/composer/impl/vr_hwc.h
@@ -0,0 +1,258 @@
+/*
+ * 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-base/unique_fd.h>
+#include <android/hardware/graphics/composer/2.1/IComposer.h>
+#include <ComposerBase.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/StrongPointer.h>
+
+#include <mutex>
+
+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.
+    Layer id;
+    sp<GraphicBuffer> buffer;
+    sp<Fence> fence;
+    Recti display_frame;
+    Rectf crop;
+    BlendMode blend_mode;
+    float alpha;
+    uint32_t type;
+    uint32_t app_id;
+  };
+
+  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 base::unique_fd OnNewFrame(const Frame& frame) = 0;
+  };
+
+  virtual ~ComposerView() {}
+
+  virtual void RegisterObserver(Observer* observer) = 0;
+  virtual void UnregisterObserver(Observer* observer) = 0;
+};
+
+struct HwcLayer {
+  using Composition =
+      hardware::graphics::composer::V2_1::IComposerClient::Composition;
+
+  HwcLayer(Layer new_id) {
+    info.id = new_id;
+  }
+
+  Composition composition_type;
+  uint32_t z_order;
+  ComposerView::ComposerLayer info;
+};
+
+class HwcDisplay {
+ public:
+  HwcDisplay();
+  ~HwcDisplay();
+
+  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);
+
+  Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame);
+
+  std::vector<Layer> UpdateLastFrameAndGetLastFrameLayers();
+
+ 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_;
+
+  std::vector<Layer> last_frame_layers_ids_;
+
+  // Layer ID generator.
+  uint64_t layer_ids_ = 1;
+
+  HwcDisplay(const HwcDisplay&) = delete;
+  void operator=(const HwcDisplay&) = delete;
+};
+
+class VrHwc : public IComposer, public ComposerBase, public ComposerView {
+ public:
+  VrHwc();
+  ~VrHwc() override;
+
+  bool hasCapability(Capability capability) const;
+
+  Error setLayerInfo(Display display, Layer layer, uint32_t type,
+                     uint32_t appId);
+
+  // ComposerBase
+  void removeClient() override;
+  void enableCallback(bool enable) override;
+
+  uint32_t getMaxVirtualDisplayCount() override;
+  Error createVirtualDisplay(uint32_t width, uint32_t height,
+      PixelFormat* format, Display* outDisplay) override;
+  Error destroyVirtualDisplay(Display display) override;
+
+  Error createLayer(Display display, Layer* outLayer) override;
+  Error destroyLayer(Display display, Layer layer) override;
+
+  Error getActiveConfig(Display display, Config* outConfig) override;
+  Error getClientTargetSupport(Display display,
+          uint32_t width, uint32_t height,
+          PixelFormat format, Dataspace dataspace) override;
+  Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
+  Error getDisplayAttribute(Display display, Config config,
+          IComposerClient::Attribute attribute, int32_t* outValue) override;
+  Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+  Error getDisplayName(Display display, hidl_string* outName) override;
+  Error getDisplayType(Display display,
+          IComposerClient::DisplayType* outType) override;
+  Error getDozeSupport(Display display, bool* outSupport) override;
+  Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
+          float* outMaxLuminance, float* outMaxAverageLuminance,
+          float* outMinLuminance) override;
+
+  Error setActiveConfig(Display display, Config config) override;
+  Error setColorMode(Display display, ColorMode mode) override;
+  Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+  Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+  Error setColorTransform(Display display, const float* matrix,
+          int32_t hint) override;
+  Error setClientTarget(Display display, buffer_handle_t target,
+          int32_t acquireFence, int32_t dataspace,
+          const std::vector<hwc_rect_t>& damage) override;
+  Error setOutputBuffer(Display display, buffer_handle_t buffer,
+          int32_t releaseFence) override;
+  Error validateDisplay(Display display,
+          std::vector<Layer>* outChangedLayers,
+          std::vector<IComposerClient::Composition>* outCompositionTypes,
+          uint32_t* outDisplayRequestMask,
+          std::vector<Layer>* outRequestedLayers,
+          std::vector<uint32_t>* outRequestMasks) override;
+  Error acceptDisplayChanges(Display display) override;
+  Error presentDisplay(Display display, int32_t* outPresentFence,
+          std::vector<Layer>* outLayers,
+          std::vector<int32_t>* outReleaseFences) override;
+
+  Error setLayerCursorPosition(Display display, Layer layer,
+          int32_t x, int32_t y) override;
+  Error setLayerBuffer(Display display, Layer layer,
+          buffer_handle_t buffer, int32_t acquireFence) override;
+  Error setLayerSurfaceDamage(Display display, Layer layer,
+          const std::vector<hwc_rect_t>& damage) override;
+  Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+  Error setLayerColor(Display display, Layer layer,
+          IComposerClient::Color color) override;
+  Error setLayerCompositionType(Display display, Layer layer,
+          int32_t type) override;
+  Error setLayerDataspace(Display display, Layer layer,
+          int32_t dataspace) override;
+  Error setLayerDisplayFrame(Display display, Layer layer,
+          const hwc_rect_t& frame) override;
+  Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+  Error setLayerSidebandStream(Display display, Layer layer,
+          buffer_handle_t stream) override;
+  Error setLayerSourceCrop(Display display, Layer layer,
+          const hwc_frect_t& crop) override;
+  Error setLayerTransform(Display display, Layer layer,
+          int32_t transform) override;
+  Error setLayerVisibleRegion(Display display, Layer layer,
+          const std::vector<hwc_rect_t>& visible) override;
+  Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+  // IComposer:
+  Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+  Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+  Return<void> createClient(createClient_cb hidl_cb) override;
+
+  // ComposerView:
+  void RegisterObserver(Observer* observer) override;
+  void UnregisterObserver(Observer* observer) override;
+
+ private:
+  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_data_provider.h b/services/vr/vr_window_manager/controller_data_provider.h
new file mode 100644
index 0000000..bc1450c
--- /dev/null
+++ b/services/vr/vr_window_manager/controller_data_provider.h
@@ -0,0 +1,19 @@
+#ifndef VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
+#define VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
+
+namespace android {
+namespace dvr {
+
+class ControllerDataProvider {
+ public:
+  virtual ~ControllerDataProvider() {}
+  // Returns data pointer or nullptr. If pointer is valid, call to
+  // UnlockControllerData is required.
+  virtual const void* LockControllerData() = 0;
+  virtual void UnlockControllerData() = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_CONTROLLER_DATA_PROVIDER_H_
\ No newline at end of file
diff --git a/services/vr/vr_window_manager/controller_mesh.cpp b/services/vr/vr_window_manager/controller_mesh.cpp
new file mode 100644
index 0000000..c6095b1
--- /dev/null
+++ b/services/vr/vr_window_manager/controller_mesh.cpp
@@ -0,0 +1,75 @@
+#include "controller_mesh.h"
+
+namespace android {
+namespace dvr {
+
+const int kNumControllerMeshVertices = 60;
+
+// Vertices in position.xyz, normal.xyz, uv.xy oder.
+// Generated from an .obj mesh.
+const float kControllerMeshVertices[] = {
+    0.002023,  0.001469,  -0.5, 0.809016,  0.587787,  0, 0,   0,
+    0.000773,  0.002378,  -0.5, 0.309004,  0.951061,  0, 0.1, 0,
+    0.000773,  0.002378,  0,    0.309004,  0.951061,  0, 0.1, 1,
+    0.002023,  0.001469,  -0.5, 0.809016,  0.587787,  0, 0,   0,
+    0.000773,  0.002378,  0,    0.309004,  0.951061,  0, 0.1, 1,
+    0.002023,  0.001469,  0,    0.809016,  0.587787,  0, 0,   1,
+    0.000773,  0.002378,  -0.5, 0.309004,  0.951061,  0, 0.1, 0,
+    -0.000773, 0.002378,  -0.5, -0.309004, 0.951061,  0, 0.2, 0,
+    -0.000773, 0.002378,  0,    -0.309004, 0.951061,  0, 0.2, 1,
+    0.000773,  0.002378,  -0.5, 0.309004,  0.951061,  0, 0.1, 0,
+    -0.000773, 0.002378,  0,    -0.309004, 0.951061,  0, 0.2, 1,
+    0.000773,  0.002378,  0,    0.309004,  0.951061,  0, 0.1, 1,
+    -0.000773, 0.002378,  -0.5, -0.309004, 0.951061,  0, 0.2, 0,
+    -0.002023, 0.001469,  -0.5, -0.809016, 0.587787,  0, 0.3, 0,
+    -0.002023, 0.001469,  0,    -0.809016, 0.587787,  0, 0.3, 1,
+    -0.000773, 0.002378,  -0.5, -0.309004, 0.951061,  0, 0.2, 0,
+    -0.002023, 0.001469,  0,    -0.809016, 0.587787,  0, 0.3, 1,
+    -0.000773, 0.002378,  0,    -0.309004, 0.951061,  0, 0.2, 1,
+    -0.002023, 0.001469,  -0.5, -0.809016, 0.587787,  0, 0.3, 0,
+    -0.0025,   0,         -0.5, -1,        -0,        0, 0.4, 0,
+    -0.0025,   0,         0,    -1,        -0,        0, 0.4, 1,
+    -0.002023, 0.001469,  -0.5, -0.809016, 0.587787,  0, 0.3, 0,
+    -0.0025,   0,         0,    -1,        -0,        0, 0.4, 1,
+    -0.002023, 0.001469,  0,    -0.809016, 0.587787,  0, 0.3, 1,
+    -0.0025,   0,         -0.5, -1,        -0,        0, 0.4, 0,
+    -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+    -0.002023, -0.001469, 0,    -0.809016, -0.587787, 0, 0.5, 1,
+    -0.0025,   0,         -0.5, -1,        -0,        0, 0.4, 0,
+    -0.002023, -0.001469, 0,    -0.809016, -0.587787, 0, 0.5, 1,
+    -0.0025,   0,         0,    -1,        -0,        0, 0.4, 1,
+    -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+    -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+    -0.000773, -0.002378, 0,    -0.309004, -0.951061, 0, 0.6, 1,
+    -0.002023, -0.001469, -0.5, -0.809016, -0.587787, 0, 0.5, 0,
+    -0.000773, -0.002378, 0,    -0.309004, -0.951061, 0, 0.6, 1,
+    -0.002023, -0.001469, 0,    -0.809016, -0.587787, 0, 0.5, 1,
+    -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+    0.000773,  -0.002378, -0.5, 0.309004,  -0.951061, 0, 0.7, 0,
+    0.000773,  -0.002378, 0,    0.309004,  -0.951061, 0, 0.7, 1,
+    -0.000773, -0.002378, -0.5, -0.309004, -0.951061, 0, 0.6, 0,
+    0.000773,  -0.002378, 0,    0.309004,  -0.951061, 0, 0.7, 1,
+    -0.000773, -0.002378, 0,    -0.309004, -0.951061, 0, 0.6, 1,
+    0.000773,  -0.002378, -0.5, 0.309004,  -0.951061, 0, 0.7, 0,
+    0.002023,  -0.001469, -0.5, 0.809016,  -0.587787, 0, 0.8, 0,
+    0.002023,  -0.001469, 0,    0.809016,  -0.587787, 0, 0.8, 1,
+    0.000773,  -0.002378, -0.5, 0.309004,  -0.951061, 0, 0.7, 0,
+    0.002023,  -0.001469, 0,    0.809016,  -0.587787, 0, 0.8, 1,
+    0.000773,  -0.002378, 0,    0.309004,  -0.951061, 0, 0.7, 1,
+    0.002023,  -0.001469, -0.5, 0.809016,  -0.587787, 0, 0.8, 0,
+    0.0025,    0,         -0.5, 1,         0,         0, 0.9, 0,
+    0.0025,    0,         0,    1,         0,         0, 0.9, 1,
+    0.002023,  -0.001469, -0.5, 0.809016,  -0.587787, 0, 0.8, 0,
+    0.0025,    0,         0,    1,         0,         0, 0.9, 1,
+    0.002023,  -0.001469, 0,    0.809016,  -0.587787, 0, 0.8, 1,
+    0.0025,    0,         -0.5, 1,         0,         0, 0.9, 0,
+    0.002023,  0.001469,  -0.5, 0.809016,  0.587787,  0, 1,   0,
+    0.002023,  0.001469,  0,    0.809016,  0.587787,  0, 1,   1,
+    0.0025,    0,         -0.5, 1,         0,         0, 0.9, 0,
+    0.002023,  0.001469,  0,    0.809016,  0.587787,  0, 1,   1,
+    0.0025,    0,         0,    1,         0,         0, 0.9, 1,
+
+};
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/controller_mesh.h b/services/vr/vr_window_manager/controller_mesh.h
new file mode 100644
index 0000000..88872c7
--- /dev/null
+++ b/services/vr/vr_window_manager/controller_mesh.h
@@ -0,0 +1,13 @@
+#ifndef VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
+#define VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
+
+namespace android {
+namespace dvr {
+
+extern const int kNumControllerMeshVertices;
+extern const float kControllerMeshVertices[];
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_CONTROLLER_MESH_H_
diff --git a/services/vr/vr_window_manager/elbow_model.cpp b/services/vr/vr_window_manager/elbow_model.cpp
new file mode 100644
index 0000000..9543f17
--- /dev/null
+++ b/services/vr/vr_window_manager/elbow_model.cpp
@@ -0,0 +1,134 @@
+#include "elbow_model.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+namespace {
+
+const vec3 kControllerForearm(0.0f, 0.0f, -0.25f);
+const vec3 kControllerPosition(0.0f, 0.0f, -0.05f);
+const vec3 kLeftElbowPosition(-0.195f, -0.5f, 0.075f);
+const vec3 kLeftArmExtension(0.13f, 0.14f, -0.08f);
+const vec3 kRightElbowPosition(0.195f, -0.5f, 0.075f);
+const vec3 kRightArmExtension(-0.13f, 0.14f, -0.08f);
+constexpr float kElbowBendRatio = 0.4f;
+constexpr float kCosMaxExtensionAngle =
+    0.87f;  // Cos of 30 degrees (90-30 = 60)
+constexpr float kCosMinExtensionAngle = 0.12f;  // Cos of 83 degrees (90-83 = 7)
+constexpr float kYAxisExtensionFraction = 0.4f;
+constexpr float kMinRotationSpeed = 0.61f;  // 35 degrees in radians
+constexpr float kMinAngleDelta = 0.175f;    // 10 degrees in radians
+
+float clamp(float v, float min, float max) {
+  if (v < min)
+    return min;
+  if (v > max)
+    return max;
+  return v;
+}
+
+float NormalizeAngle(float angle) {
+  if (angle > M_PI)
+    angle = 2.0f * M_PI - angle;
+  return angle;
+}
+
+}  // namespace
+
+const vec3 ElbowModel::kDefaultNeckPosition = vec3(0, -0.075f, -0.080f);
+
+ElbowModel::ElbowModel() {}
+ElbowModel::~ElbowModel() {}
+
+void ElbowModel::Enable(const vec3& neck_position, bool right_handed) {
+  enabled_ = true;
+  neck_position_ = neck_position;
+
+  if (right_handed) {
+    elbow_position_ = kRightElbowPosition;
+    arm_extension_ = kRightArmExtension;
+  } else {
+    elbow_position_ = kLeftElbowPosition;
+    arm_extension_ = kLeftArmExtension;
+  }
+
+  ResetRoot();
+}
+
+void ElbowModel::Disable() { enabled_ = false; }
+
+vec3 ElbowModel::Update(float delta_t, const quat& hmd_orientation,
+                        const quat& controller_orientation, bool recenter) {
+  if (!enabled_)
+    return vec3::Zero();
+
+  float heading_rad = GetHeading(hmd_orientation);
+
+  quat y_rotation;
+  y_rotation = Eigen::AngleAxis<float>(heading_rad, vec3::UnitY());
+
+  // If the controller's angular velocity is above a certain amount, we can
+  // assume torso rotation and move the elbow joint relative to the
+  // camera orientation.
+  float angle_delta = last_controller_.angularDistance(controller_orientation);
+  float rot_speed = angle_delta / delta_t;
+
+  if (recenter) {
+    root_rot_ = y_rotation;
+  } else if (rot_speed > kMinRotationSpeed) {
+    root_rot_.slerp(angle_delta / kMinAngleDelta, y_rotation);
+  }
+
+  // Calculate angle (or really, cos thereof) between controller forward vector
+  // and Y axis to determine extension amount.
+  vec3 controller_forward_rotated = controller_orientation * -vec3::UnitZ();
+  float dot_y = controller_forward_rotated.y();
+  float amt_extension = clamp(dot_y - kCosMinExtensionAngle, 0, 1);
+
+  // Remove the root rotation from the orientation reading--we'll add it back in
+  // later.
+  quat controller_rot = root_rot_.inverse() * controller_orientation;
+  controller_forward_rotated = controller_rot * -vec3::UnitZ();
+  quat rot_xy;
+  rot_xy.setFromTwoVectors(-vec3::UnitZ(), controller_forward_rotated);
+
+  // Fixing polar singularity
+  float total_angle = NormalizeAngle(atan2f(rot_xy.norm(), rot_xy.w()) * 2.0f);
+  float lerp_amount = (1.0f - powf(total_angle / M_PI, 6.0f)) *
+                      (1.0f - (kElbowBendRatio +
+                               (1.0f - kElbowBendRatio) *
+                                   (amt_extension + kYAxisExtensionFraction)));
+
+  // Calculate the relative rotations of the elbow and wrist joints.
+  quat wrist_rot = quat::Identity();
+  wrist_rot.slerp(lerp_amount, rot_xy);
+  quat elbow_rot = wrist_rot.inverse() * rot_xy;
+
+  last_controller_ = controller_orientation;
+
+  vec3 position =
+      root_rot_ *
+      ((controller_root_offset_ + arm_extension_ * amt_extension) +
+       elbow_rot * (kControllerForearm + wrist_rot * kControllerPosition));
+
+  return position;
+}
+
+float ElbowModel::GetHeading(const quat& orientation) {
+  vec3 gaze = orientation * -vec3::UnitZ();
+
+  if (gaze.y() > 0.99)
+    gaze = orientation * -vec3::UnitY();
+  else if (gaze.y() < -0.99)
+    gaze = orientation * vec3::UnitY();
+
+  return atan2f(-gaze.x(), -gaze.z());
+}
+
+void ElbowModel::ResetRoot() {
+  controller_root_offset_ = elbow_position_ + neck_position_;
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/elbow_model.h b/services/vr/vr_window_manager/elbow_model.h
new file mode 100644
index 0000000..a6d5ca9
--- /dev/null
+++ b/services/vr/vr_window_manager/elbow_model.h
@@ -0,0 +1,45 @@
+#ifndef VR_WINDOW_MANAGER_ELBOW_MODEL_H_
+#define VR_WINDOW_MANAGER_ELBOW_MODEL_H_
+
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class ElbowModel {
+ public:
+  ElbowModel();
+  ~ElbowModel();
+
+  void Enable(const vec3& neck_position, bool right_handed);
+  void Disable();
+
+  vec3 Update(float delta_t, const quat& hmd_orientation,
+              const quat& controller_orientation, bool recenter);
+
+  static const vec3 kDefaultNeckPosition;
+
+ private:
+  ElbowModel(const ElbowModel&) = delete;
+  void operator=(const ElbowModel&) = delete;
+
+  void ResetRoot();
+
+  float GetHeading(const quat& orientation);
+
+  bool enabled_ = false;
+
+  quat last_controller_ = quat::Identity();
+
+  quat root_rot_ = quat::Identity();
+
+  vec3 controller_root_offset_ = vec3::Zero();
+  vec3 elbow_position_ = vec3::Zero();
+  vec3 arm_extension_ = vec3::Zero();
+  vec3 neck_position_ = vec3::Zero();
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_ELBOW_MODEL_H_
diff --git a/services/vr/vr_window_manager/hwc_callback.cpp b/services/vr/vr_window_manager/hwc_callback.cpp
new file mode 100644
index 0000000..05ec64a
--- /dev/null
+++ b/services/vr/vr_window_manager/hwc_callback.cpp
@@ -0,0 +1,70 @@
+#include "hwc_callback.h"
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+#include <private/dvr/native_buffer.h>
+#include <sync/sync.h>
+#include <ui/GraphicBufferMapper.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+HwcCallback::FrameStatus GetFrameStatus(const HwcCallback::Frame& frame) {
+  for (const auto& layer : frame.layers()) {
+    // If there is no fence it means the buffer is already finished.
+    if (layer.fence->isValid()) {
+      status_t result = layer.fence->wait(0);
+      if (result != OK) {
+        if (result != -ETIME) {
+          ALOGE("fence wait on buffer fence failed. status=%d (%s).",
+                result, strerror(-result));
+          return HwcCallback::FrameStatus::kError;
+        }
+        return HwcCallback::FrameStatus::kUnfinished;
+      }
+    }
+  }
+
+  return HwcCallback::FrameStatus::kFinished;
+}
+
+}  // namespace
+
+HwcCallback::HwcCallback(Client* client) : client_(client) {
+}
+
+HwcCallback::~HwcCallback() {
+}
+
+base::unique_fd HwcCallback::OnNewFrame(const ComposerView::Frame& frame) {
+  std::vector<HwcLayer> hwc_frame(frame.size());
+  for (size_t i = 0; i < frame.size(); ++i) {
+    hwc_frame[i] = HwcLayer{
+      .fence = frame[i].fence,
+      .buffer = frame[i].buffer,
+      .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,
+    };
+  }
+
+  return client_->OnFrame(
+      std::make_unique<Frame>(std::move(hwc_frame)));
+}
+
+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..be56856
--- /dev/null
+++ b/services/vr/vr_window_manager/hwc_callback.h
@@ -0,0 +1,115 @@
+#ifndef VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+#define VR_WINDOW_MANAGER_HWC_CALLBACK_H_
+
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <impl/vr_composer_view.h>
+#include <impl/vr_hwc.h>
+
+namespace android {
+
+class Fence;
+class GraphicBuffer;
+
+namespace dvr {
+
+using Recti = ComposerView::ComposerLayer::Recti;
+using Rectf = ComposerView::ComposerLayer::Rectf;
+
+class HwcCallback : public VrComposerView::Callback {
+ public:
+  struct HwcLayer {
+    enum LayerType : uint32_t {
+      // These are from frameworks/base/core/java/android/view/WindowManager.java
+      kSurfaceFlingerLayer = 0,
+      kUndefinedWindow = ~0U,
+      kFirstApplicationWindow = 1,
+      kLastApplicationWindow = 99,
+      kFirstSubWindow = 1000,
+      kLastSubWindow = 1999,
+      kFirstSystemWindow = 2000,
+      kStatusBar = kFirstSystemWindow,
+      kInputMethod = kFirstSystemWindow + 11,
+      kNavigationBar = kFirstSystemWindow + 19,
+      kLastSystemWindow = 2999,
+    };
+
+    bool should_skip_layer() const {
+      switch (type) {
+        // Always skip the following layer types
+      case kNavigationBar:
+      case kStatusBar:
+      case kSurfaceFlingerLayer:
+      case kUndefinedWindow:
+        return true;
+      default:
+        return false;
+      }
+    }
+
+    // This is a layer that provides some other functionality, eg dim layer.
+    // We use this to determine the point at which layers are "on top".
+    bool is_extra_layer() const {
+      switch(type) {
+      case kSurfaceFlingerLayer:
+      case kUndefinedWindow:
+        return true;
+      default:
+        return false;
+      }
+    }
+
+    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 base::unique_fd OnFrame(std::unique_ptr<Frame>) = 0;
+  };
+
+  explicit HwcCallback(Client* client);
+  ~HwcCallback() override;
+
+ private:
+  base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override;
+
+  Client *client_;
+
+  HwcCallback(const HwcCallback&) = delete;
+  void operator=(const HwcCallback&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_HWC_CALLBACK_H_
diff --git a/services/vr/vr_window_manager/proguard.flags b/services/vr/vr_window_manager/proguard.flags
new file mode 100644
index 0000000..7683d6e
--- /dev/null
+++ b/services/vr/vr_window_manager/proguard.flags
@@ -0,0 +1,22 @@
+# Don't obfuscate any NDK/SDK code. This makes the debugging of stack traces in
+# in release builds easier.
+-keepnames class com.google.vr.ndk.** { *; }
+-keepnames class com.google.vr.sdk.** { *; }
+
+# These are part of the SDK <-> VrCore interfaces for GVR.
+-keepnames class com.google.vr.vrcore.library.api.** { *; }
+
+# These are part of the Java <-> native interfaces for GVR.
+-keep class com.google.vr.** { native <methods>; }
+
+-keep class com.google.vr.cardboard.annotations.UsedByNative
+-keep @com.google.vr.cardboard.annotations.UsedByNative class *
+-keepclassmembers class * {
+    @com.google.vr.cardboard.annotations.UsedByNative *;
+}
+
+-keep class com.google.vr.cardboard.UsedByNative
+-keep @com.google.vr.cardboard.UsedByNative class *
+-keepclassmembers class * {
+    @com.google.vr.cardboard.UsedByNative *;
+}
diff --git a/services/vr/vr_window_manager/reticle.cpp b/services/vr/vr_window_manager/reticle.cpp
new file mode 100644
index 0000000..cbd0caf
--- /dev/null
+++ b/services/vr/vr_window_manager/reticle.cpp
@@ -0,0 +1,100 @@
+#include "reticle.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+const std::string kVertexShader = SHADER0([]() {
+  layout(location = 0) in vec4 aPosition;
+  layout(location = 1) in vec4 aTexCoord;
+  uniform mat4 uViewProjection;
+  uniform mat4 uTransform;
+
+  out vec2 vTexCoord;
+  void main() {
+    gl_Position = uViewProjection * uTransform * aPosition;
+    vTexCoord = aTexCoord.xy;
+  }
+});
+
+const std::string kFragmentShader = SHADER0([]() {
+  precision mediump float;
+
+  in vec2 vTexCoord;
+  uniform vec3 uColor;
+
+  out vec4 fragColor;
+  void main() {
+    float alpha = smoothstep(1.0, 0.0, length(vTexCoord));
+    fragColor = vec4(uColor, alpha);
+  }
+});
+
+}  // namespace
+
+Reticle::Reticle() {}
+
+Reticle::~Reticle() {}
+
+bool Reticle::Initialize() {
+  program_.Link(kVertexShader, kFragmentShader);
+  if (!program_)
+    return false;
+
+  return true;
+}
+
+void Reticle::ShowAt(const mat4& hit_transform, const vec3& color) {
+  transform_ = hit_transform;
+  shown_ = true;
+
+  GLint view_projection_location =
+      glGetUniformLocation(program_.GetProgram(), "uColor");
+  glProgramUniform3f(program_.GetProgram(), view_projection_location, color.x(),
+                     color.y(), color.z());
+}
+
+void Reticle::Draw(const mat4& perspective, const mat4& eye_matrix,
+                   const mat4& head_matrix) {
+  if (!shown_)
+    return;
+
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  program_.Use();
+
+  const float kRadius = 0.015;
+  GLfloat vertices[] = {
+      -kRadius, -kRadius, 0, kRadius, -kRadius, 0,
+      -kRadius, kRadius,  0, kRadius, kRadius,  0,
+  };
+  GLfloat texture_vertices[] = {
+      -1, 1, 1, 1, -1, -1, 1, -1,
+  };
+
+  mat4 mvp = perspective * eye_matrix * head_matrix;
+  GLint view_projection_location =
+      glGetUniformLocation(program_.GetProgram(), "uViewProjection");
+  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+  GLint transform_location =
+      glGetUniformLocation(program_.GetProgram(), "uTransform");
+  glUniformMatrix4fv(transform_location, 1, 0, transform_.data());
+
+  glEnableVertexAttribArray(0);
+  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
+  glEnableVertexAttribArray(1);
+  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texture_vertices);
+
+  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+  glDisable(GL_BLEND);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/vr_window_manager/reticle.h b/services/vr/vr_window_manager/reticle.h
new file mode 100644
index 0000000..d8522aa
--- /dev/null
+++ b/services/vr/vr_window_manager/reticle.h
@@ -0,0 +1,35 @@
+#ifndef VR_WINDOW_MANAGER_SHELL_RETICLE_H_
+#define VR_WINDOW_MANAGER_SHELL_RETICLE_H_
+
+#include <private/dvr/graphics/shader_program.h>
+#include <private/dvr/types.h>
+
+namespace android {
+namespace dvr {
+
+class Reticle {
+ public:
+  Reticle();
+  ~Reticle();
+
+  bool Initialize();
+
+  void ShowAt(const mat4& hit_transform, const vec3& color);
+  void Hide() { shown_ = false; }
+
+  void Draw(const mat4& perspective, const mat4& eye_matrix,
+            const mat4& head_matrix);
+
+ private:
+  bool shown_ = false;
+  ShaderProgram program_;
+  mat4 transform_;
+
+  Reticle(const Reticle&) = delete;
+  void operator=(const Reticle&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_SHELL_RETICLE_H_
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
new file mode 100644
index 0000000..7321ed0
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -0,0 +1,764 @@
+#include "shell_view.h"
+
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <android/input.h>
+#include <binder/IServiceManager.h>
+#include <hardware/hwcomposer2.h>
+#include <log/log.h>
+
+#include "controller_mesh.h"
+#include "texture.h"
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+constexpr float kLayerScaleFactor = 4.0f;
+
+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;
+
+  xscale *= kLayerScaleFactor;
+  yscale *= kLayerScaleFactor;
+
+  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 * Eigen::AngleAxisf(M_PI * 0.5f, vec3::UnitZ());
+}
+
+// Helper function that applies the crop transform to the texture layer and
+// positions (and scales) the texture layer in the appropriate location in the
+// display space.
+mat4 GetLayerTransform(const TextureLayer& texture_layer, float display_width,
+                       float display_height) {
+  // Map from vertex coordinates to [0, 1] coordinates:
+  //  1) Flip y since in vertex coordinates (-1, -1) is at the bottom left and
+  //     in texture coordinates (0, 0) is at the top left.
+  //  2) Translate by (1, 1) to map vertex coordinates to [0, 2] on x and y.
+  //  3) Scale by 1 / 2 to map coordinates to [0, 1] on x  and y.
+  mat4 unit_space(
+      Eigen::AlignedScaling3f(0.5f, 0.5f, 1.0f) *
+      Eigen::Translation3f(1.0f, 1.0f, 0.0f) *
+      Eigen::AlignedScaling3f(1.0f, -1.0f, 1.0f));
+
+  mat4 texture_space(Eigen::AlignedScaling3f(
+      texture_layer.texture->width(), texture_layer.texture->height(), 1.0f));
+
+  // 1) Translate the layer to crop the left and top edge.
+  // 2) Scale the layer such that the cropped right and bottom edges map outside
+  //    the exture region.
+  float crop_width = texture_layer.crop.right - texture_layer.crop.left;
+  float crop_height = texture_layer.crop.bottom - texture_layer.crop.top;
+  mat4 texture_crop(
+      Eigen::AlignedScaling3f(
+          texture_layer.texture->width() / crop_width,
+          texture_layer.texture->height() / crop_height,
+          1.0f) *
+      Eigen::Translation3f(
+          -texture_layer.crop.left, -texture_layer.crop.top, 0.0f));
+
+  mat4 display_space(
+      Eigen::AlignedScaling3f(display_width, display_height, 1.0f));
+
+  // 1) Scale the texture to fit the display frame.
+  // 2) Translate the texture in the display frame location.
+  float display_frame_width = texture_layer.display_frame.right -
+      texture_layer.display_frame.left;
+  float display_frame_height = texture_layer.display_frame.bottom -
+      texture_layer.display_frame.top;
+  mat4 display_frame(
+      Eigen::Translation3f(
+          texture_layer.display_frame.left,
+          texture_layer.display_frame.top,
+          0.0f) *
+      Eigen::AlignedScaling3f(
+          display_frame_width / display_width,
+          display_frame_height / display_height,
+          1.0f));
+
+  mat4 layer_transform = unit_space.inverse() * display_space.inverse() *
+      display_frame * display_space * texture_space.inverse() * texture_crop *
+      texture_space * unit_space;
+  return layer_transform;
+}
+
+// Determine if ths frame should be shown or hidden.
+ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
+                                            uint32_t vr_app) {
+  auto& layers = frame.layers();
+
+  // We assume the first two layers are the VR app. In the case of a 2D app,
+  // there will be the app + at least one system layer so this is still safe.
+  if (layers.size() < kVRAppLayerCount)
+    return ViewMode::Hidden;
+
+  if (vr_app != layers[0].appid || layers[0].appid == 0 ||
+      layers[1].appid != layers[0].appid) {
+    if (layers[1].appid != layers[0].appid && layers[0].appid) {
+      // This might be a 2D app.
+      // If a dim layer exists afterwards it is much more likely that this is
+      // actually an app launch artifact.
+      for (size_t i = 2; i < layers.size(); i++) {
+        if (layers[i].is_extra_layer())
+          return ViewMode::Hidden;
+      }
+      return ViewMode::App;
+    }
+    return ViewMode::Hidden;
+  }
+
+  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 == HwcCallback::HwcLayer::kSurfaceFlingerLayer) {
+      index = i + 1;
+    }
+  }
+
+  // If any non-skipped layers exist now then we show, otherwise hide.
+  for (size_t i = index; i < layers.size(); i++) {
+    if (!layers[i].should_skip_layer())
+      return ViewMode::VR;
+  }
+  return ViewMode::Hidden;
+}
+
+
+}  // namespace
+
+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() {
+  int ret = Application::Initialize();
+  if (ret)
+    return ret;
+
+  translate_ = Eigen::Translation3f(0, 0, -2.5f);
+
+  if (!InitializeTouch())
+    ALOGE("Failed to initialize virtual touchpad");
+
+  surface_flinger_view_.reset(new SurfaceFlingerView);
+  if (!surface_flinger_view_->Initialize(this))
+    return 1;
+
+  return 0;
+}
+
+int ShellView::AllocateResources() {
+  int ret = Application::AllocateResources();
+  if (ret)
+    return ret;
+
+  program_.reset(new ShaderProgram);
+  program_->Link(kVertexShader, kFragmentShader);
+  overlay_program_.reset(new ShaderProgram);
+  overlay_program_->Link(kVertexShader, kOverlayFragmentShader);
+  controller_program_.reset(new ShaderProgram);
+  controller_program_->Link(kVertexShader, kControllerFragmentShader);
+  if (!program_ || !overlay_program_ || !controller_program_)
+    return 1;
+
+  reticle_.reset(new Reticle());
+  if (!reticle_->Initialize())
+    return 1;
+
+  controller_mesh_.reset(new Mesh<vec3, vec3, vec2>());
+  controller_mesh_->SetVertices(kNumControllerMeshVertices,
+                                kControllerMeshVertices);
+
+  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) {
+  ALOGI("EnableDebug(%d)", (int)debug); // XXX TODO delete
+  QueueTask(debug ? MainThreadTask::EnableDebugMode
+                  : MainThreadTask::DisableDebugMode);
+}
+
+void ShellView::VrMode(bool mode) {
+  ALOGI("VrMode(%d)", (int)mode); // XXX TODO delete
+  QueueTask(mode ? MainThreadTask::EnteringVrMode
+                 : MainThreadTask::ExitingVrMode);
+}
+
+void ShellView::dumpInternal(String8& result) {
+  result.append("[shell]\n");
+  result.appendFormat("initialized = %s\n", initialized_ ? "true" : "false");
+  result.appendFormat("is_visible = %s\n", is_visible_ ? "true" : "false");
+  result.appendFormat("debug_mode = %s\n\n", debug_mode_ ? "true" : "false");
+}
+
+void ShellView::AdvanceFrame() {
+  if (!pending_frames_.empty()) {
+    // Check if we should advance the frame.
+    auto& frame = pending_frames_.front();
+    if (frame.visibility == ViewMode::Hidden ||
+        frame.frame->Finish() == HwcCallback::FrameStatus::kFinished) {
+      current_frame_ = std::move(frame);
+      pending_frames_.pop_front();
+    }
+  }
+}
+
+void ShellView::OnDrawFrame() {
+  textures_.clear();
+  has_ime_ = false;
+
+  {
+    std::unique_lock<std::mutex> l(pending_frame_mutex_);
+    AdvanceFrame();
+  }
+
+  bool visible = current_frame_.visibility != ViewMode::Hidden;
+
+  if (!debug_mode_ && visible != is_visible_) {
+    SetVisibility(current_frame_.visibility != ViewMode::Hidden);
+  }
+
+  if (!debug_mode_ && !visible)
+    return;
+
+  ime_texture_ = TextureLayer();
+
+  surface_flinger_view_->GetTextures(*current_frame_.frame.get(), &textures_,
+                                     &ime_texture_, debug_mode_,
+                                     current_frame_.visibility == ViewMode::VR);
+  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;
+}
+
+base::unique_fd ShellView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame) {
+  ViewMode visibility =
+      CalculateVisibilityFromLayerConfig(*frame.get(), current_vr_app_);
+
+  if (visibility == ViewMode::Hidden && debug_mode_)
+    visibility = ViewMode::VR;
+
+  if (frame->layers().empty())
+    current_vr_app_ = 0;
+  else
+    current_vr_app_ = frame->layers().front().appid;
+
+  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 (visibility == ViewMode::Hidden &&
+      current_frame_.visibility == ViewMode::Hidden) {
+    // Consume all frames while hidden.
+    while (!pending_frames_.empty())
+      AdvanceFrame();
+  }
+
+  // If we are showing ourselves the main thread is not processing anything,
+  // so give it a kick.
+  if (visibility != ViewMode::Hidden &&
+      current_frame_.visibility == ViewMode::Hidden) {
+    QueueTask(MainThreadTask::Show);
+  }
+
+  return base::unique_fd(dup(release_fence_.get()));
+}
+
+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();
+  }
+
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  EGLSyncKHR sync = eglCreateSyncKHR(display, EGL_SYNC_NATIVE_FENCE_ANDROID,
+                                     nullptr);
+  if (sync != EGL_NO_SYNC_KHR) {
+    // Need to flush in order to get the fence FD.
+    glFlush();
+    base::unique_fd fence(eglDupNativeFenceFDANDROID(display, sync));
+    eglDestroySyncKHR(display, sync);
+    UpdateReleaseFence(std::move(fence));
+  } else {
+    ALOGE("Failed to create sync fence");
+    UpdateReleaseFence(base::unique_fd());
+  }
+}
+
+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 (shmem_controller_active_) {
+    view_quaternion = controller_orientation_;
+    vec4 controller_location = controller_translate_ * vec4(0, 0, 0, 1);
+    pointer_location = vec3(controller_location.x(), controller_location.y(),
+                            controller_location.z());
+
+    if (shmem_controller_active_) {
+      uint64_t buttons = shmem_controller_buttons_;
+      shmem_controller_buttons_ = 0;
+      while (buttons) {
+        switch (buttons & 0xF) {
+          case 0x1:
+            OnClick(false);
+            break;
+          case 0x3:
+            OnTouchpadButton(false, AMOTION_EVENT_BUTTON_BACK);
+            break;
+          case 0x9:
+            OnClick(true);
+            break;
+          case 0xB:
+            OnTouchpadButton(true, AMOTION_EVENT_BUTTON_BACK);
+            break;
+          default:
+            break;
+        }
+        buttons >>= 4;
+      }
+    }
+  }
+
+  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 (!shmem_controller_active_)
+    return;
+
+  controller_program_->Use();
+  mat4 mvp = perspective * eye_matrix * head_matrix;
+
+  GLint view_projection_location = glGetUniformLocation(
+      controller_program_->GetProgram(), "uViewProjection");
+  glUniformMatrix4fv(view_projection_location, 1, 0, mvp.data());
+
+  quat view_quaternion = controller_orientation_;
+  view_quaternion.toRotationMatrix();
+
+  vec3 world_pos = last_pose_.GetPosition() + controller_position_;
+
+  controller_translate_ =
+      Eigen::Translation3f(world_pos.x(), world_pos.y(), world_pos.z());
+
+  mat4 transform = controller_translate_ * view_quaternion *
+                   mat4(Eigen::Scaling<float>(1, 1, 3.0));
+  GLint transform_location =
+      glGetUniformLocation(controller_program_->GetProgram(), "uTransform");
+  glUniformMatrix4fv(transform_location, 1, 0, transform.data());
+
+  controller_mesh_->Draw();
+}
+
+bool ShellView::InitializeTouch() {
+  virtual_touchpad_ = VirtualTouchpadClient::Create();
+  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::status_t 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 != OK) {
+    ALOGE("touch failed: %d", status);
+  }
+}
+
+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::status_t status =
+      virtual_touchpad_->ButtonState(touchpad_buttons_);
+  if (status != OK) {
+    ALOGE("touchpad button failed: %d %d", touchpad_buttons_, status);
+  }
+  return true;
+}
+
+void ShellView::UpdateReleaseFence(base::unique_fd fence) {
+  std::lock_guard<std::mutex> guard(pending_frame_mutex_);
+  release_fence_ = std::move(fence);
+}
+
+}  // 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..c477669
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -0,0 +1,137 @@
+#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 <deque>
+
+#include "VirtualTouchpadClient.h"
+#include "application.h"
+#include "reticle.h"
+#include "shell_view_binder_interface.h"
+#include "surface_flinger_view.h"
+
+namespace android {
+namespace dvr {
+
+enum class ViewMode {
+  Hidden,
+  VR,
+  App,
+};
+
+class ShellView : public Application,
+                  public android::dvr::ShellViewBinderInterface,
+                  public HwcCallback::Client {
+ public:
+  ShellView();
+  virtual ~ShellView();
+
+  int Initialize() override;
+
+  int AllocateResources() override;
+  void DeallocateResources() override;
+
+  // ShellViewBinderInterface:
+  void EnableDebug(bool debug) override;
+  void VrMode(bool mode) override;
+  void dumpInternal(String8& result) override;
+
+ 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);
+
+  void AdvanceFrame();
+
+  void UpdateReleaseFence(base::unique_fd fence);
+
+  // HwcCallback::Client:
+  base::unique_fd 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<VirtualTouchpad> 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, ViewMode visibility)
+        : frame(std::move(frame)), visibility(visibility) {}
+    PendingFrame(PendingFrame&& r)
+        : frame(std::move(r.frame)), visibility(r.visibility) {}
+
+    void operator=(PendingFrame&& r) {
+      frame.reset(r.frame.release());
+      visibility = r.visibility;
+    }
+
+    std::unique_ptr<HwcCallback::Frame> frame;
+    ViewMode visibility = ViewMode::Hidden;
+  };
+  std::deque<PendingFrame> pending_frames_;
+  std::mutex pending_frame_mutex_;
+  PendingFrame current_frame_;
+
+  mat4 controller_translate_;
+
+  base::unique_fd release_fence_;
+
+  ShellView(const ShellView&) = delete;
+  void operator=(const ShellView&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_SHELL_VIEW_H_
diff --git a/services/vr/vr_window_manager/shell_view_binder_interface.h b/services/vr/vr_window_manager/shell_view_binder_interface.h
new file mode 100644
index 0000000..b58e4bd
--- /dev/null
+++ b/services/vr/vr_window_manager/shell_view_binder_interface.h
@@ -0,0 +1,20 @@
+#ifndef VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
+#define VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
+
+namespace android {
+namespace dvr {
+
+class ShellViewBinderInterface {
+ public:
+  ShellViewBinderInterface() {};
+  virtual ~ShellViewBinderInterface() {};
+
+  virtual void EnableDebug(bool debug) = 0;
+  virtual void VrMode(bool mode) = 0;
+  virtual void dumpInternal(String8& result) = 0;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_SHELL_VIEWBINDER_INTERFACE_H_
diff --git a/services/vr/vr_window_manager/surface_flinger_view.cpp b/services/vr/vr_window_manager/surface_flinger_view.cpp
new file mode 100644
index 0000000..63bc143
--- /dev/null
+++ b/services/vr/vr_window_manager/surface_flinger_view.cpp
@@ -0,0 +1,88 @@
+#include "surface_flinger_view.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 vr_hwcomposer_name[] = "vr_hwcomposer";
+  vr_hwcomposer_ = HIDL_FETCH_IComposer(vr_hwcomposer_name);
+  if (!vr_hwcomposer_.get()) {
+    ALOGE("Failed to get vr_hwcomposer");
+    return false;
+  }
+
+  if (vr_hwcomposer_->isRemote()) {
+    ALOGE("vr_hwcomposer service is remote");
+    return false;
+  }
+
+  const android::status_t vr_hwcomposer_status =
+      vr_hwcomposer_->registerAsService(vr_hwcomposer_name);
+  if (vr_hwcomposer_status != OK) {
+    ALOGE("Failed to register vr_hwcomposer service");
+    return false;
+  }
+
+  vr_composer_view_ =
+      std::make_unique<VrComposerView>(std::make_unique<HwcCallback>(client));
+  vr_composer_view_->Initialize(GetComposerViewFromIComposer(
+      vr_hwcomposer_.get()));
+
+  // TODO(dnicoara): Query this from the composer service.
+  width_ = 1920;
+  height_ = 1080;
+  return true;
+}
+
+bool SurfaceFlingerView::GetTextures(const HwcCallback::Frame& frame,
+                                     std::vector<TextureLayer>* texture_layers,
+                                     TextureLayer* ime_layer,
+                                     bool debug, bool skip_first_layer) const {
+  auto& layers = frame.layers();
+  texture_layers->clear();
+
+  size_t start = 0;
+  // Skip the second layer if it is from the VR app.
+  if (!debug && skip_first_layer) {
+    start = 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..7370299
--- /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 <memory>
+
+#include <impl/vr_composer_view.h>
+
+#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,
+                   bool skip_first_layer) const;
+
+ private:
+  sp<IComposer> vr_hwcomposer_;
+  std::unique_ptr<VrComposerView> vr_composer_view_;
+  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..2229efa
--- /dev/null
+++ b/services/vr/vr_window_manager/texture.cpp
@@ -0,0 +1,41 @@
+#include "texture.h"
+
+#include <GLES/glext.h>
+#include <log/log.h>
+#include <system/window.h>
+
+namespace android {
+namespace dvr {
+
+Texture::Texture() {}
+
+Texture::~Texture() {
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  if (id_)
+    glDeleteTextures(1, &id_);
+  if (image_)
+    eglDestroyImageKHR(display, image_);
+}
+
+bool Texture::Initialize(ANativeWindowBuffer* buffer) {
+  width_ = buffer->width;
+  height_ = buffer->height;
+
+  EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+  image_ = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+                             EGL_NATIVE_BUFFER_ANDROID, buffer, nullptr);
+  if (!image_) {
+    ALOGE("Failed to create eglImage");
+    return false;
+  }
+
+  glGenTextures(1, &id_);
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_2D, id_);
+  glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image_);
+
+  return true;
+}
+
+}  // namespace android
+}  // namespace dvr
diff --git a/services/vr/vr_window_manager/texture.h b/services/vr/vr_window_manager/texture.h
new file mode 100644
index 0000000..9840f19
--- /dev/null
+++ b/services/vr/vr_window_manager/texture.h
@@ -0,0 +1,37 @@
+#ifndef VR_WINDOW_MANAGER_TEXTURE_H_
+#define VR_WINDOW_MANAGER_TEXTURE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace dvr {
+
+class Texture {
+ public:
+  explicit Texture();
+  ~Texture();
+
+  bool Initialize(ANativeWindowBuffer* buffer);
+
+  GLuint id() const { return id_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+
+ private:
+  EGLImageKHR image_ = nullptr;
+  GLuint id_ = 0;
+  int width_ = 0;
+  int height_ = 0;
+
+  Texture(const Texture&) = delete;
+  void operator=(const Texture&) = delete;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_TEXTURE_H_
diff --git a/services/vr/vr_window_manager/vr_window_manager.cpp b/services/vr/vr_window_manager/vr_window_manager.cpp
new file mode 100644
index 0000000..6636dc5
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager.cpp
@@ -0,0 +1,48 @@
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <hwbinder/IPCThreadState.h>
+#include <impl/vr_composer_view.h>
+#include <impl/vr_hwc.h>
+
+#include "shell_view.h"
+#include "vr_window_manager_binder.h"
+
+using namespace android;
+using namespace android::dvr;
+
+int main(int /* argc */, char** /* argv */) {
+  android::ProcessState::self()->startThreadPool();
+
+  // ShellView needs to be created after vr_hwcomposer.
+  android::dvr::ShellView app;
+  const int app_status = app.Initialize();
+  LOG_ALWAYS_FATAL_IF(app_status != 0, "failed to initialize: %d", app_status);
+
+  // Create vr_wm_binder.
+  android::service::vr::VrWindowManagerBinder vr_wm_binder(app);
+  const int status = vr_wm_binder.Initialize();
+  LOG_ALWAYS_FATAL_IF(status != 0, "initialization failed: %d", status);
+
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+  const android::status_t vr_wm_binder_status =
+      sm->addService(
+          android::service::vr::VrWindowManagerBinder::SERVICE_NAME(),
+          &vr_wm_binder, false /*allowIsolated*/);
+  LOG_ALWAYS_FATAL_IF(vr_wm_binder_status != android::OK,
+                      "vr_wm_binder service not added: %d",
+                      static_cast<int>(vr_wm_binder_status));
+
+  app.SetControllerDataProvider(&vr_wm_binder);
+
+  android::hardware::ProcessState::self()->startThreadPool();
+
+  while (true) {
+    app.DrawFrame();
+  }
+
+  android::hardware::IPCThreadState::self()->joinThreadPool();
+  android::IPCThreadState::self()->joinThreadPool();
+
+  return 0;
+}
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.cpp b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
new file mode 100644
index 0000000..c2138b7
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
@@ -0,0 +1,156 @@
+#include "vr_window_manager_binder.h"
+
+#include <inttypes.h>
+#include <sys/mman.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Errors.h>
+
+namespace android {
+namespace service {
+namespace vr {
+
+namespace {
+const String16 kDumpPermission("android.permission.DUMP");
+const String16 kSendMeControllerInputPermission("TODO");  // TODO(kpschoedel)
+}  // anonymous namespace
+
+constexpr size_t AshmemControllerDataProvider::kRegionLength;
+
+status_t AshmemControllerDataProvider::Connect(const int in_fd) {
+  if (in_fd < 0) {
+    return BAD_VALUE;
+  }
+  if (fd_.get() >= 0) {
+    // The VrCore is dead. Long live the VrCore.
+    Disconnect();
+  }
+  void* const shared_region =
+      ::mmap(nullptr, kRegionLength, PROT_READ, MAP_SHARED, in_fd, 0);
+  if (shared_region == MAP_FAILED) {
+    shared_region_ = nullptr;
+    return NO_MEMORY;
+  }
+
+  errno = 0;
+  const int fd = ::fcntl(in_fd, F_DUPFD_CLOEXEC, 0);
+  if (fd < 0) {
+    ::munmap(shared_region, kRegionLength);
+    return -errno;
+  }
+  fd_.reset(fd);
+  ALOGI("controller connected %d -> %d @ %p", in_fd, fd, shared_region);
+
+  std::lock_guard<std::mutex> guard(mutex_);
+  shared_region_ = shared_region;
+  return OK;
+}
+
+status_t AshmemControllerDataProvider::Disconnect() {
+  if (shared_region_ == nullptr || fd_.get() < 0) {
+    return INVALID_OPERATION;
+  }
+  std::lock_guard<std::mutex> guard(mutex_);
+  ::munmap(shared_region_, kRegionLength);
+  shared_region_ = nullptr;
+  fd_.reset();
+  ALOGI("controller disconnected");
+  return OK;
+}
+
+const void* AshmemControllerDataProvider::LockControllerData() {
+  mutex_.lock();
+  if (!shared_region_) {
+    mutex_.unlock();
+    return nullptr;
+  }
+  return shared_region_;
+}
+
+void AshmemControllerDataProvider::UnlockControllerData() { mutex_.unlock(); }
+
+void AshmemControllerDataProvider::dumpInternal(String8& result) {
+  result.appendFormat("[controller]\nfd = %d\n", fd_.get());
+  if (shared_region_) {
+    int32_t* p = reinterpret_cast<int32_t*>(shared_region_);
+    result.appendFormat("header = ");
+    for (int i = 0; i < 8; ++i) {
+      result.appendFormat("%c 0x%08" PRIX32, i ? ',' : '[', p[i]);
+    }
+    result.appendFormat(" ]\n\n");
+  }
+}
+
+int VrWindowManagerBinder::Initialize() { return 0; }
+
+binder::Status VrWindowManagerBinder::connectController(
+    const ::android::base::unique_fd& in_fd) {
+  // TODO(kpschoedel): check permission
+#if 0
+  int32_t pid, uid;
+  if (!PermissionCache::checkCallingPermission(kSendMeControllerInputPermission,
+                                               &pid, &uid)) {
+    ALOGE("permission denied to pid=%" PRId32 " uid=%" PRId32, pid, uid);
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+#endif
+  return binder::Status::fromStatusT(Connect(in_fd.get()));
+}
+
+binder::Status VrWindowManagerBinder::disconnectController() {
+  // TODO(kpschoedel): check permission
+#if 0
+  int32_t pid, uid;
+  if (!PermissionCache::checkCallingPermission(kSendMeControllerInputPermission,
+                                               &pid, &uid)) {
+    ALOGE("permission denied to pid=%" PRId32 " uid=%" PRId32, pid, uid);
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+#endif
+  return binder::Status::fromStatusT(Disconnect());
+}
+
+binder::Status VrWindowManagerBinder::enterVrMode() {
+  // TODO(kpschoedel): check permission
+  app_.VrMode(true);
+  return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::exitVrMode() {
+  // TODO(kpschoedel): check permission
+  app_.VrMode(false);
+  return binder::Status::ok();
+}
+
+binder::Status VrWindowManagerBinder::setDebugMode(int32_t mode) {
+  // TODO(kpschoedel): check permission
+  app_.EnableDebug(static_cast<bool>(mode));
+  return binder::Status::ok();
+}
+
+status_t VrWindowManagerBinder::dump(
+    int fd, const Vector<String16>& args [[gnu::unused]]) {
+  String8 result;
+  const android::IPCThreadState* ipc = android::IPCThreadState::self();
+  const int pid = ipc->getCallingPid();
+  const int uid = ipc->getCallingUid();
+  if ((uid != AID_SHELL) &&
+      !PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
+    result.appendFormat("Permission denial: can't dump " LOG_TAG
+                        " from pid=%d, uid=%d\n",
+                        pid, uid);
+  } else {
+    app_.dumpInternal(result);
+    AshmemControllerDataProvider::dumpInternal(result);
+  }
+  write(fd, result.string(), result.size());
+  return OK;
+}
+
+}  // namespace vr
+}  // namespace service
+}  // namespace android
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.h b/services/vr/vr_window_manager/vr_window_manager_binder.h
new file mode 100644
index 0000000..99ca27a
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager_binder.h
@@ -0,0 +1,77 @@
+#ifndef VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
+#define VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
+
+#include <android/service/vr/BnVrWindowManager.h>
+
+#include <mutex>
+
+#include "controller_data_provider.h"
+#include "shell_view_binder_interface.h"
+
+namespace android {
+namespace service {
+namespace vr {
+
+class AshmemControllerDataProvider : public dvr::ControllerDataProvider {
+ public:
+  AshmemControllerDataProvider() {}
+  virtual ~AshmemControllerDataProvider() {}
+
+  status_t Connect(int fd);
+  status_t Disconnect();
+
+  // ControllerDataProvider:
+  const void* LockControllerData() override;
+  void UnlockControllerData() override;
+
+ protected:
+  void dumpInternal(String8& result);
+
+ private:
+  static constexpr size_t kRegionLength = 8192;  // TODO(kpschoedel)
+  ::android::base::unique_fd fd_;
+
+  // Mutex for guarding shared_region_.
+  std::mutex mutex_;
+  void* shared_region_ = nullptr;
+
+  AshmemControllerDataProvider(const AshmemControllerDataProvider&) = delete;
+  void operator=(const AshmemControllerDataProvider&) = delete;
+};
+
+class VrWindowManagerBinder : public BnVrWindowManager,
+                              public AshmemControllerDataProvider {
+ public:
+  VrWindowManagerBinder(android::dvr::ShellViewBinderInterface& app)
+      : app_(app) {}
+  virtual ~VrWindowManagerBinder() {}
+
+  // Must be called before clients can connect.
+  // Returns 0 if initialization is successful.
+  int Initialize();
+  static char const* getServiceName() { return "vr_window_manager"; }
+
+ protected:
+  // Implements IVrWindowManagerBinder.
+  ::android::binder::Status connectController(
+      const ::android::base::unique_fd& fd) override;
+  ::android::binder::Status disconnectController() override;
+  ::android::binder::Status enterVrMode() override;
+  ::android::binder::Status exitVrMode() override;
+  ::android::binder::Status setDebugMode(int32_t mode) override;
+
+  // Implements BBinder::dump().
+  status_t dump(int fd, const Vector<String16>& args) override;
+
+ private:
+  android::dvr::ShellViewBinderInterface& app_;
+
+  VrWindowManagerBinder(const VrWindowManagerBinder&) = delete;
+  void operator=(const VrWindowManagerBinder&) = delete;
+};
+
+}  // namespace vr
+}  // namespace service
+}  // namespace android
+
+#endif  // VR_WINDOW_MANAGER_VR_WINDOW_MANAGER_BINDER_H_
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder_test.cpp b/services/vr/vr_window_manager/vr_window_manager_binder_test.cpp
new file mode 100644
index 0000000..f43e803
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_window_manager_binder_test.cpp
@@ -0,0 +1,29 @@
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cutils/log.h>
+
+#include "vr_window_manager_binder.h"
+
+int main() {
+  ALOGI("Starting");
+  android::service::vr::VrWindowManagerBinder service;
+  const int status = service.Initialize();
+  LOG_ALWAYS_FATAL_IF(status != 0, "initialization failed: %d", status);
+
+  signal(SIGPIPE, SIG_IGN);
+  android::sp<android::ProcessState> ps(android::ProcessState::self());
+  ps->setThreadPoolMaxThreadCount(4);
+  ps->startThreadPool();
+  ps->giveThreadPoolName();
+
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+  const android::status_t service_status = sm->addService(
+      android::service::vr::VrWindowManagerBinder::SERVICE_NAME(), &service,
+      false /*allowIsolated*/);
+  LOG_ALWAYS_FATAL_IF(service_status != android::OK, "service not added: %d",
+                      static_cast<int>(service_status));
+
+  android::IPCThreadState::self()->joinThreadPool();
+  return 0;
+}
diff --git a/services/vr/vr_window_manager/vr_wm.rc b/services/vr/vr_window_manager/vr_wm.rc
new file mode 100644
index 0000000..951515b
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_wm.rc
@@ -0,0 +1,5 @@
+service vr_wm /system/bin/vr_wm
+  class core
+  user system
+  group system graphics input
+  cpuset /system
diff --git a/services/vr/vr_window_manager/vr_wm_ctl.cpp b/services/vr/vr_window_manager/vr_wm_ctl.cpp
new file mode 100644
index 0000000..c67b2eb
--- /dev/null
+++ b/services/vr/vr_window_manager/vr_wm_ctl.cpp
@@ -0,0 +1,48 @@
+#include <android/service/vr/BpVrWindowManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <inttypes.h>
+
+void usage() { fprintf(stderr, "usage: vr_wm_ctl [enter|exit|debug N]\n"); }
+
+int report(const android::binder::Status& status) {
+  if (status.isOk()) {
+    fprintf(stderr, "ok\n");
+    return 0;
+  }
+  fprintf(stderr, "failed (%" PRId32 ") %s\n", status.exceptionCode(),
+          status.exceptionMessage().string());
+  return (int)status.exceptionCode();
+}
+
+int main(int argc, char* argv[]) {
+  android::sp<android::IServiceManager> sm(android::defaultServiceManager());
+  if (sm == nullptr) {
+    fprintf(stderr, "service manager not found\n");
+    exit(1);
+  }
+
+  android::sp<android::service::vr::IVrWindowManager> vrwm =
+      android::interface_cast<android::service::vr::IVrWindowManager>(
+          sm->getService(
+              android::service::vr::IVrWindowManager::SERVICE_NAME()));
+  if (vrwm == nullptr) {
+    fprintf(stderr, "service not found\n");
+    exit(1);
+  }
+
+  android::binder::Status status;
+  if ((argc == 2) && (strcmp(argv[1], "enter") == 0)) {
+    exit(report(vrwm->enterVrMode()));
+  } else if ((argc == 2) && (strcmp(argv[1], "exit") == 0)) {
+    exit(report(vrwm->exitVrMode()));
+  } else if ((argc == 3) && (strcmp(argv[1], "debug") == 0)) {
+    exit(report(vrwm->setDebugMode(atoi(argv[2]))));
+  } else {
+    usage();
+    exit(2);
+  }
+
+  return 0;
+}
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 754c441..cfeeeef 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -86,7 +86,7 @@
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME             "VK_KHR_win32_surface"
 
 // 11
-@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     5
+@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     7
 @extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME             "VK_ANDROID_native_buffer"
 
 // 12
@@ -165,14 +165,33 @@
 @extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1
 @extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
 
+// 60
+@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
+@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
+
 // 62
 @extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
 @extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
 
+// 85
+@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"
+
 // 87
 @extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
 @extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
 
+// 93
+@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"
+
+// 106
+@extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_SPEC_VERSION 1
+@extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata"
+
+// 112
+@extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1
+@extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image"
 
 /////////////
 //  Types  //
@@ -242,6 +261,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 {
@@ -757,8 +779,16 @@
     //@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,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID = 1000010002,
+
+    //@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,
@@ -787,9 +817,23 @@
     //@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,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR          = 1000059001,
+    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR                   = 1000059002,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR             = 1000059003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR   = 1000059004,
+    VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR             = 1000059005,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR   = 1000059006,
+    VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR      = 1000059007,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
+
     //@extension("VK_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,
     VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX  = 1000086001,
@@ -873,11 +917,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")
@@ -1493,6 +1553,13 @@
 //bitfield VkWin32SurfaceCreateFlagBitsKHR {
 //}
 
+@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_EXT_debug_report")
 type VkFlags VkDebugReportFlagsEXT
 @extension("VK_EXT_debug_report")
@@ -1541,7 +1608,6 @@
     VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX                   = 0x00000002,
 }
 
-
 //////////////////
 //  Structures  //
 //////////////////
@@ -2826,14 +2892,63 @@
     platform.HWND                               hwnd
 }
 
+@internal class Gralloc1Usage {
+    u64                                         consumer
+    u64                                         producer
+}
+
 @extension("VK_ANDROID_native_buffer")
 class VkNativeBufferANDROID {
     VkStructureType                             sType
     const void*                                 pNext
     platform.buffer_handle_t                    handle
-    int                                         stride
-    int                                         format
-    int                                         usage
+    s32                                         stride
+    s32                                         format
+    s32                                         usage
+    Gralloc1Usage                               usage2
+}
+
+@extension("VK_ANDROID_native_buffer")
+class VkSwapchainImageCreateInfoANDROID {
+    VkStructureType                             sType
+    const void*                                 pNext
+    VkSwapchainImageUsageFlagsANDROID           flags
+}
+
+@extension("VK_ANDROID_native_buffer")
+class VkPhysicalDevicePresentationPropertiesANDROID {
+    VkStructureType                             sType
+    void*                                       pNext
+    VkBool32                                    sharedImage
+}
+
+@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")
@@ -2953,6 +3068,77 @@
     const u64*                                  pReleaseKeys
 }
 
+@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_EXT_validation_flags")
 class VkValidationFlagsEXT {
     VkStructureType                             sType
@@ -3087,6 +3273,44 @@
     VkShaderStageFlags                          stageFlags
 }
 
+@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_EXT_hdr_metadata")
+class VkXYColorEXT {
+    f32    x
+    f32    y
+}
+
+@extension("VK_EXT_hdr_metadata")
+class VkHdrMetadataEXT {
+    VkXYColorEXT    displayPrimaryRed
+    VkXYColorEXT    displayPrimaryGreen
+    VkXYColorEXT    displayPrimaryBlue
+    VkXYColorEXT    whitePoint
+    f32             maxLuminance
+    f32             minLuminance
+    f32             maxContentLightLevel
+    f32             maxFrameAverageLightLevel
+}
 
 ////////////////
 //  Commands  //
@@ -5622,11 +5846,24 @@
 }
 
 @extension("VK_ANDROID_native_buffer")
+@optional
 cmd VkResult vkGetSwapchainGrallocUsageANDROID(
         VkDevice                                device,
         VkFormat                                format,
         VkImageUsageFlags                       imageUsage,
-        int*                                    grallocUsage) {
+        s32*                                    grallocUsage) {
+    return ?
+}
+
+@extension("VK_ANDROID_native_buffer")
+@optional
+cmd VkResult vkGetSwapchainGrallocUsage2ANDROID(
+        VkDevice                                device,
+        VkFormat                                format,
+        VkImageUsageFlags                       imageUsage,
+        VkSwapchainImageUsageFlagsANDROID       swapchainImageUsage,
+        u64*                                    grallocConsumerUsage,
+        u64*                                    grallocProducerUsage) {
     return ?
 }
 
@@ -5650,6 +5887,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")
@@ -5768,6 +6028,54 @@
     return ?
 }
 
+@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_NVX_device_generated_commands")
 cmd void vkCmdProcessCommandsNVX(
         VkCommandBuffer                             commandBuffer,
@@ -5839,6 +6147,21 @@
         VkDeviceGeneratedCommandsLimitsNVX*         pLimits) {
 }
 
+@extension("VK_EXT_hdr_metadata")
+cmd void vkSetHdrMetadataEXT(
+    VkDevice                                        device,
+    u32                                             swapchainCount,
+    const VkSwapchainKHR*                           pSwapchains,
+    const VkHdrMetadataEXT*                         pMetadata) {
+}
+
+@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..24af950 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.adoc
+++ b/vulkan/doc/implementors_guide/implementors_guide.adoc
@@ -47,9 +47,33 @@
 
 The +vk_wsi_swapchin+ and +vk_wsi_device_swapchain+ extensions are primarily be implemented by the platform and live in +libvulkan.so+. The +VkSwapchain+ object and all interaction with +ANativeWindow+ will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver's +vkGetDeviceProcAddr+ functions, after passing through any enabled layers.
 
+Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on +format+ and +imageUsage+, but also on the intended usage of the swapchain. The swapchain usage bits are defined as
+[source,c]
+----
+typedef enum VkSwapchainImageUsageFlagBitsANDROID {
+    VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSwapchainImageUsageFlagBitsANDROID;
+typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
+----
+
 Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling
 [source,c]
 ----
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+    VkDevice            device,
+    VkFormat            format,
+    VkImageUsageFlags   imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    uint64_t*           grallocConsumerUsage,
+    uint64_t*           grallocProducerUsage,
+);
+----
+The +format+ and +imageUsage+ parameters are taken from the +VkSwapchainCreateInfoKHR+ structure. The driver should fill +*grallocConsumerUsage+ and +*grallocProducerUsage+ with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.
+
+An older version of this function is deprecated but still supported for backwards compatibility; it will be used if +vkGetSwapchainGrallocUsage2ANDROID+ is not supported:
+[source,c]
+----
 VkResult VKAPI vkGetSwapchainGrallocUsageANDROID(
     VkDevice            device,
     VkFormat            format,
@@ -57,7 +81,6 @@
     int*                grallocUsage
 );
 ----
-The +format+ and +imageUsage+ parameters are taken from the +VkSwapchainCreateInfoKHR+ structure. The driver should fill +*grallocUsage+ with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.
 
 +VkNativeBufferANDROID+ is a +vkCreateImage+ extension structure for creating an image backed by a gralloc buffer. This structure is provided to +vkCreateImage+ in the +VkImageCreateInfo+ structure chain. Calls to +vkCreateImage+ with this structure will happen during the first call to +vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)+. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a +VkImage+ for each one.
 
@@ -73,11 +96,16 @@
 
     // Gralloc format and usage requested when the buffer was allocated.
     int                         format;
-    int                         usage;
+    int                         usage; // deprecated
+    struct {
+        uint64_t                consumer;
+        uint64_t                producer;
+    } usage2;
 } VkNativeBufferANDROID;
 ----
 
 When creating a gralloc-backed image, the +VkImageCreateInfo+ will have:
+[source,txt]
 ----
   .imageType           = VK_IMAGE_TYPE_2D
   .format              = a VkFormat matching the format requested for the gralloc buffer
@@ -93,6 +121,17 @@
   .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices
 ----
 
+Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a +VkSwapchainImageCreateInfoANDROID+ extension structure in the +VkImageCreateInfo+ chain provided to +vkCreateImage+, containing the swapchain image usage flags:
+[source,c]
+----
+typedef struct {
+    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+    const void*                            pNext;
+
+    VkSwapchainImageUsageFlagsANDROID      usage;
+} VkSwapchainImageCreateInfoANDROID;
+----
+
 +vkAcquireImageANDROID+ acquires ownership of a swapchain image and imports an
 externally-signalled native fence into both an existing VkSemaphore object
 and an existing VkFence object:
@@ -139,6 +178,8 @@
 
 This will be called during +vkQueuePresentWSI+ on the provided queue. Effects are similar to +vkQueueSignalSemaphore+, except with a native fence instead of a semaphore. The native fence must: not signal until the +waitSemaphoreCount+ semaphores in +pWaitSemaphores+ have signaled. Unlike +vkQueueSignalSemaphore+, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set +*pNativeFenceFd+ to -1. The file descriptor returned in +*pNativeFenceFd+ is owned and will be closed by the caller. Many drivers will be able to ignore the +image+ parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to +VK_IMAGE_LAYOUT_PRESENT_SRC_KHR+.
 
+If +image+ was created with +VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID+, then the driver must tolerate +vkQueueSignalReleaseImageANDROID+ being called repeatedly without intervening calls to +vkAcquireImageANDROID+.
+
 == History ==
 
 . *2015-07-08* Initial version
@@ -158,4 +199,11 @@
 . *2016-01-08*
    * Added waitSemaphoreCount and pWaitSemaphores parameters to vkQueueSignalReleaseImageANDROID.
 . *2016-06-17*
-   * Updates to reflect final behavior, closed some TBDs now that they've BDed.
\ No newline at end of file
+   * Updates to reflect final behavior, closed some TBDs now that they've BDed.
+. *2017-01-06*
+   * Extension version 6
+   * Added VkSwapchainImageUsageFlagBitsANDROID
+   * Added vkGetSwapchainGrallocUsage2ANDROID
+   * Added VkSwapchainImageCreateInfoANDROID
+. *2017-02-09*
+   * Extended vkGetSwapchainGrallocUsage2ANDROID and VkNativeBufferANDROID to use gralloc1-style usage bitfields.
\ No newline at end of file
diff --git a/vulkan/doc/implementors_guide/implementors_guide.html b/vulkan/doc/implementors_guide/implementors_guide.html
index 0bfeb81..9fecce5 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.html
+++ b/vulkan/doc/implementors_guide/implementors_guide.html
@@ -793,9 +793,35 @@
 <h2 id="_window_system_integration">2. Window System Integration</h2>
 <div class="sectionbody">
 <div class="paragraph"><p>The <span class="monospaced">vk_wsi_swapchin</span> and <span class="monospaced">vk_wsi_device_swapchain</span> extensions are primarily be implemented by the platform and live in <span class="monospaced">libvulkan.so</span>. The <span class="monospaced">VkSwapchain</span> object and all interaction with <span class="monospaced">ANativeWindow</span> will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver&#8217;s <span class="monospaced">vkGetDeviceProcAddr</span> functions, after passing through any enabled layers.</p></div>
+<div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags that depend not only on <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span>, but also on the intended usage of the swapchain. The swapchain usage bits are defined as</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">enum</span></span> VkSwapchainImageUsageFlagBitsANDROID <span style="color: #FF0000">{</span>
+    VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID <span style="color: #990000">=</span> <span style="color: #993399">0x00000001</span><span style="color: #990000">,</span>
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM <span style="color: #990000">=</span> <span style="color: #993399">0x7FFFFFFF</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageUsageFlagBitsANDROID<span style="color: #990000">;</span>
+<span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="color: #008080">VkFlags</span> VkSwapchainImageUsageFlagsANDROID<span style="color: #990000">;</span></tt></pre></div></div>
 <div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>VKAPI_ATTR <span style="color: #008080">VkResult</span> <span style="color: #008080">VKAPI_CALL</span> <span style="font-weight: bold"><span style="color: #000000">vkGetSwapchainGrallocUsage2ANDROID</span></span><span style="color: #990000">(</span>
+    <span style="color: #008080">VkDevice</span>            device<span style="color: #990000">,</span>
+    <span style="color: #008080">VkFormat</span>            format<span style="color: #990000">,</span>
+    <span style="color: #008080">VkImageUsageFlags</span>   imageUsage<span style="color: #990000">,</span>
+    <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span> swapchainImageUsage<span style="color: #990000">,</span>
+    uint64_t<span style="color: #990000">*</span>           grallocConsumerUsage<span style="color: #990000">,</span>
+    uint64_t<span style="color: #990000">*</span>           grallocProducerUsage<span style="color: #990000">,</span>
+<span style="color: #990000">);</span></tt></pre></div></div>
+<div class="paragraph"><p>The <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span> parameters are taken from the <span class="monospaced">VkSwapchainCreateInfoKHR</span> structure. The driver should fill <span class="monospaced">*grallocConsumerUsage</span> and <span class="monospaced">*grallocProducerUsage</span> with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.</p></div>
+<div class="paragraph"><p>An older version of this function is deprecated but still supported for backwards compatibility; it will be used if <span class="monospaced">vkGetSwapchainGrallocUsage2ANDROID</span> is not supported:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -805,10 +831,9 @@
     <span style="color: #008080">VkImageUsageFlags</span>   imageUsage<span style="color: #990000">,</span>
     <span style="color: #009900">int</span><span style="color: #990000">*</span>                grallocUsage
 <span style="color: #990000">);</span></tt></pre></div></div>
-<div class="paragraph"><p>The <span class="monospaced">format</span> and <span class="monospaced">imageUsage</span> parameters are taken from the <span class="monospaced">VkSwapchainCreateInfoKHR</span> structure. The driver should fill <span class="monospaced">*grallocUsage</span> with the gralloc usage flags it requires for that format and usage. These will be combined with the usage flags requested by the swapchain consumer when allocating buffers.</p></div>
 <div class="paragraph"><p><span class="monospaced">VkNativeBufferANDROID</span> is a <span class="monospaced">vkCreateImage</span> extension structure for creating an image backed by a gralloc buffer. This structure is provided to <span class="monospaced">vkCreateImage</span> in the <span class="monospaced">VkImageCreateInfo</span> structure chain. Calls to <span class="monospaced">vkCreateImage</span> with this structure will happen during the first call to <span class="monospaced">vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)</span>. The WSI implementation will allocate the number of native buffers requested for the swapchain, then create a <span class="monospaced">VkImage</span> for each one.</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -822,12 +847,19 @@
 
     <span style="font-style: italic"><span style="color: #9A1900">// Gralloc format and usage requested when the buffer was allocated.</span></span>
     <span style="color: #009900">int</span>                         format<span style="color: #990000">;</span>
-    <span style="color: #009900">int</span>                         usage<span style="color: #990000">;</span>
+    <span style="color: #009900">int</span>                         usage<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// deprecated</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
+        <span style="color: #008080">uint64_t</span>                consumer<span style="color: #990000">;</span>
+        <span style="color: #008080">uint64_t</span>                producer<span style="color: #990000">;</span>
+    <span style="color: #FF0000">}</span> usage2<span style="color: #990000">;</span>
 <span style="color: #FF0000">}</span> VkNativeBufferANDROID<span style="color: #990000">;</span></tt></pre></div></div>
 <div class="paragraph"><p>When creating a gralloc-backed image, the <span class="monospaced">VkImageCreateInfo</span> will have:</p></div>
 <div class="listingblock">
-<div class="content monospaced">
-<pre>  .imageType           = VK_IMAGE_TYPE_2D
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>  .imageType           = VK_IMAGE_TYPE_2D
   .format              = a VkFormat matching the format requested for the gralloc buffer
   .extent              = the 2D dimensions requested for the gralloc buffer
   .mipLevels           = 1
@@ -838,13 +870,24 @@
   .flags               = 0
   .sharingMode         = VkSwapChainCreateInfoWSI::sharingMode
   .queueFamilyCount    = VkSwapChainCreateInfoWSI::queueFamilyCount
-  .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</pre>
-</div></div>
+  .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices</tt></pre></div></div>
+<div class="paragraph"><p>Additionally, when any swapchain image usage flags are required for the swapchain, the platform will provide a <span class="monospaced">VkSwapchainImageCreateInfoANDROID</span> extension structure in the <span class="monospaced">VkImageCreateInfo</span> chain provided to <span class="monospaced">vkCreateImage</span>, containing the swapchain image usage flags:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">typedef</span></span> <span style="font-weight: bold"><span style="color: #0000FF">struct</span></span> <span style="color: #FF0000">{</span>
+    <span style="color: #008080">VkStructureType</span>                        sType<span style="color: #990000">;</span> <span style="font-style: italic"><span style="color: #9A1900">// must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID</span></span>
+    <span style="font-weight: bold"><span style="color: #0000FF">const</span></span> <span style="color: #009900">void</span><span style="color: #990000">*</span>                            pNext<span style="color: #990000">;</span>
+
+    <span style="color: #008080">VkSwapchainImageUsageFlagsANDROID</span>      usage<span style="color: #990000">;</span>
+<span style="color: #FF0000">}</span> VkSwapchainImageCreateInfoANDROID<span style="color: #990000">;</span></tt></pre></div></div>
 <div class="paragraph"><p><span class="monospaced">vkAcquireImageANDROID</span> acquires ownership of a swapchain image and imports an
 externally-signalled native fence into both an existing VkSemaphore object
 and an existing VkFence object:</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -872,7 +915,7 @@
 is as if the native fence was already signalled.</p></div>
 <div class="paragraph"><p><span class="monospaced">vkQueueSignalReleaseImageANDROID</span> prepares a swapchain image for external use, and creates a native fence and schedules it to be signalled when prior work on the queue has completed.</p></div>
 <div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 3.1.8
+<div class="content"><!-- Generator: GNU source-highlight 3.1.6
 by Lorenzo Bettini
 http://www.lorenzobettini.it
 http://www.gnu.org/software/src-highlite -->
@@ -884,6 +927,7 @@
     <span style="color: #009900">int</span><span style="color: #990000">*</span>                pNativeFenceFd
 <span style="color: #990000">);</span></tt></pre></div></div>
 <div class="paragraph"><p>This will be called during <span class="monospaced">vkQueuePresentWSI</span> on the provided queue. Effects are similar to <span class="monospaced">vkQueueSignalSemaphore</span>, except with a native fence instead of a semaphore. The native fence must: not signal until the <span class="monospaced">waitSemaphoreCount</span> semaphores in <span class="monospaced">pWaitSemaphores</span> have signaled. Unlike <span class="monospaced">vkQueueSignalSemaphore</span>, however, this call creates and returns the synchronization object that will be signalled rather than having it provided as input. If the queue is already idle when this function is called, it is allowed but not required to set <span class="monospaced">*pNativeFenceFd</span> to -1. The file descriptor returned in <span class="monospaced">*pNativeFenceFd</span> is owned and will be closed by the caller. Many drivers will be able to ignore the <span class="monospaced">image</span> parameter, but some may need to prepare CPU-side data structures associated with a gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should have been done asynchronously as part of transitioning the image to <span class="monospaced">VK_IMAGE_LAYOUT_PRESENT_SRC_KHR</span>.</p></div>
+<div class="paragraph"><p>If <span class="monospaced">image</span> was created with <span class="monospaced">VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID</span>, then the driver must tolerate <span class="monospaced">vkQueueSignalReleaseImageANDROID</span> being called repeatedly without intervening calls to <span class="monospaced">vkAcquireImageANDROID</span>.</p></div>
 </div>
 </div>
 <div class="sect1">
@@ -978,6 +1022,45 @@
 </li>
 </ul></div>
 </li>
+<li>
+<p>
+<strong>2017-01-06</strong>
+</p>
+<div class="ulist"><ul>
+<li>
+<p>
+Extension version 6
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageUsageFlagBitsANDROID
+</p>
+</li>
+<li>
+<p>
+Added vkGetSwapchainGrallocUsage2ANDROID
+</p>
+</li>
+<li>
+<p>
+Added VkSwapchainImageCreateInfoANDROID
+</p>
+</li>
+</ul></div>
+</li>
+<li>
+<p>
+<strong>2017-02-09</strong>
+</p>
+<div class="ulist"><ul>
+<li>
+<p>
+Extended vkGetSwapchainGrallocUsage2ANDROID and VkNativeBufferANDROID to use gralloc1-style usage bitfields.
+</p>
+</li>
+</ul></div>
+</li>
 </ol></div>
 </div>
 </div>
@@ -986,7 +1069,7 @@
 <div id="footer">
 <div id="footer-text">
 Version 5<br>
-Last updated 2016-06-17 13:54:25 PDT
+Last updated 2017-02-09 22:40:30 PST
 </div>
 </div>
 </body>
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index d0ebf81..43a9a9c 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -27,11 +27,29 @@
 #define VK_ANDROID_native_buffer 1
 
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     5
+/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6
+ *
+ * This version of the extension transitions from gralloc0 to gralloc1 usage
+ * flags (int -> 2x uint64_t). The WSI implementation will temporarily continue
+ * to fill out deprecated fields in VkNativeBufferANDROID, and will call the
+ * deprecated vkGetSwapchainGrallocUsageANDROID if the new
+ * vkGetSwapchainGrallocUsage2ANDROID is not supported. This transitionary
+ * backwards-compatibility support is temporary, and will likely be removed in
+ * (along with all gralloc0 support) in a future release.
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     7
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME   "VK_ANDROID_native_buffer"
 
 #define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id)    ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id)))
 #define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID   VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0)
+#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
+#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+
+typedef enum VkSwapchainImageUsageFlagBitsANDROID {
+    VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
+    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VkSwapchainImageUsageFlagBitsANDROID;
+typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
 
 typedef struct {
     VkStructureType             sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
@@ -43,20 +61,53 @@
 
     // Gralloc format and usage requested when the buffer was allocated.
     int                         format;
-    int                         usage;
+    int                         usage; // DEPRECATED in SPEC_VERSION 6
+    // -- Added in SPEC_VERSION 6 --
+    struct {
+        uint64_t                consumer;
+        uint64_t                producer;
+    } usage2;
 } VkNativeBufferANDROID;
 
+typedef struct {
+    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
+    const void*                            pNext;
+
+    VkSwapchainImageUsageFlagsANDROID      usage;
+} VkSwapchainImageCreateInfoANDROID;
+
+typedef struct {
+    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
+    const void*                            pNext;
+
+    VkBool32                               sharedImage;
+} VkPhysicalDevicePresentationPropertiesANDROID;
+
+// -- DEPRECATED in SPEC_VERSION 6 --
 typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+// -- ADDED in SPEC_VERSION 6 --
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
 typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
 
 #ifndef VK_NO_PROTOTYPES
+
+// -- DEPRECATED in SPEC_VERSION 6 --
 VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID(
     VkDevice            device,
     VkFormat            format,
     VkImageUsageFlags   imageUsage,
     int*                grallocUsage
 );
+// -- ADDED in SPEC_VERSION 6 --
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
+    VkDevice            device,
+    VkFormat            format,
+    VkImageUsageFlags   imageUsage,
+    VkSwapchainImageUsageFlagsANDROID swapchainImageUsage,
+    uint64_t*           grallocConsumerUsage,
+    uint64_t*           grallocProducerUsage
+);
 VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
     VkDevice            device,
     VkImage             image,
@@ -71,17 +122,6 @@
     VkImage             image,
     int*                pNativeFenceFd
 );
-// -- DEPRECATED --
-VKAPI_ATTR VkResult VKAPI_CALL vkImportNativeFenceANDROID(
-    VkDevice            device,
-    VkSemaphore         semaphore,
-    int                 nativeFenceFd
-);
-VKAPI_ATTR VkResult VKAPI_CALL vkQueueSignalNativeFenceANDROID(
-    VkQueue             queue,
-    int*                pNativeFenceFd
-);
-// ----------------
 #endif
 
 #ifdef __cplusplus
diff --git a/vulkan/include/vulkan/vk_platform.h b/vulkan/include/vulkan/vk_platform.h
index 0fa62ee..2054447 100644
--- a/vulkan/include/vulkan/vk_platform.h
+++ b/vulkan/include/vulkan/vk_platform.h
@@ -94,7 +94,7 @@
 // controls inclusion of the extension interfaces in vulkan.h.
 
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
-#include <android/native_window.h>
+struct ANativeWindow;
 #endif
 
 #ifdef VK_USE_PLATFORM_MIR_KHR
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index f24a0a2..3c8ee1c 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -6,7 +6,7 @@
 #endif
 
 /*
-** Copyright (c) 2015-2016 The Khronos Group Inc.
+** Copyright (c) 2015-2017 The Khronos Group Inc.
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -225,13 +225,24 @@
     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_PRESENT_REGIONS_KHR = 1000084000,
     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),
@@ -519,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),
@@ -3206,6 +3218,19 @@
 
 typedef enum VkColorSpaceKHR {
     VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0,
+    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104001,
+    VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT = 1000104002,
+    VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT = 1000104003,
+    VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = 1000104004,
+    VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104005,
+    VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104006,
+    VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104007,
+    VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104008,
+    VK_COLOR_SPACE_BT2020_170M_EXT = 1000104009,
+    VK_COLOR_SPACE_BT2020_ST2084_EXT = 1000104010,
+    VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011,
+    VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012,
+    VK_COLOR_SPACE_PASS_THROUGH_EXT = 1000104013,
     VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
     VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
     VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1),
@@ -3217,6 +3242,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),
@@ -3676,7 +3703,6 @@
 
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
 #define VK_KHR_android_surface 1
-#include <android/native_window.h>
 
 #define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6
 #define VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "VK_KHR_android_surface"
@@ -3687,7 +3713,7 @@
     VkStructureType                   sType;
     const void*                       pNext;
     VkAndroidSurfaceCreateFlagsKHR    flags;
-    ANativeWindow*                    window;
+    struct ANativeWindow*             window;
 } VkAndroidSurfaceCreateInfoKHR;
 
 
@@ -3741,6 +3767,150 @@
 #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)
 
@@ -3984,7 +4154,6 @@
 } VkDedicatedAllocationMemoryAllocateInfoNV;
 
 
-
 #define VK_AMD_draw_indirect_count 1
 #define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
 #define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
@@ -4393,6 +4562,88 @@
     VkDeviceGeneratedCommandsLimitsNVX*         pLimits);
 #endif
 
+#define VK_GOOGLE_display_timing 1
+#define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
+#define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
+
+typedef struct VkRefreshCycleDurationGOOGLE {
+    uint64_t    refreshDuration;
+} VkRefreshCycleDurationGOOGLE;
+
+typedef struct VkPastPresentationTimingGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+    uint64_t    actualPresentTime;
+    uint64_t    earliestPresentTime;
+    uint64_t    presentMargin;
+} VkPastPresentationTimingGOOGLE;
+
+typedef struct VkPresentTimeGOOGLE {
+    uint32_t    presentID;
+    uint64_t    desiredPresentTime;
+} VkPresentTimeGOOGLE;
+
+typedef struct VkPresentTimesInfoGOOGLE {
+    VkStructureType               sType;
+    const void*                   pNext;
+    uint32_t                      swapchainCount;
+    const VkPresentTimeGOOGLE*    pTimes;
+} VkPresentTimesInfoGOOGLE;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE(
+    VkDevice                                    device,
+    VkSwapchainKHR                              swapchain,
+    uint32_t*                                   pPresentationTimingCount,
+    VkPastPresentationTimingGOOGLE*             pPresentationTimings);
+#endif
+
+#define VK_EXT_swapchain_colorspace 1
+#define VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 1
+#define VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
+
+#define VK_EXT_hdr_metadata 1
+#define VK_EXT_HDR_METADATA_SPEC_VERSION  0
+#define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata"
+
+typedef struct VkXYColorEXT {
+    float    x;
+    float    y;
+} VkXYColorEXT;
+
+typedef struct VkHdrMetadataEXT {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkXYColorEXT       displayPrimaryRed;
+    VkXYColorEXT       displayPrimaryGreen;
+    VkXYColorEXT       displayPrimaryBlue;
+    VkXYColorEXT       whitePoint;
+    float              maxLuminance;
+    float              minLuminance;
+    float              maxContentLightLevel;
+    float              maxFrameAverageLightLevel;
+} VkHdrMetadataEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkSetHdrMetadataEXT)(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkSetHdrMetadataEXT(
+    VkDevice                                    device,
+    uint32_t                                    swapchainCount,
+    const VkSwapchainKHR*                       pSwapchains,
+    const VkHdrMetadataEXT*                     pMetadata);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 70c7e75..524de75 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,14 +67,14 @@
     export_static_lib_headers: ["vulkan_headers"],
     static_libs: [
         "vulkan_headers",
-        "libziparchive",
     ],
     shared_libs: [
-        "libgui",
+        "libziparchive",
         "libhardware",
         "libsync",
         "libbase",
         "liblog",
+        "libui",
         "libutils",
         "libcutils",
         "libz",
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 af70fa1..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;
@@ -455,13 +455,20 @@
         "vkGetInstanceProcAddr",
         "vkGetPhysicalDeviceExternalImageFormatPropertiesNV",
         "vkGetPhysicalDeviceFeatures",
+        "vkGetPhysicalDeviceFeatures2KHR",
         "vkGetPhysicalDeviceFormatProperties",
+        "vkGetPhysicalDeviceFormatProperties2KHR",
         "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX",
         "vkGetPhysicalDeviceImageFormatProperties",
+        "vkGetPhysicalDeviceImageFormatProperties2KHR",
         "vkGetPhysicalDeviceMemoryProperties",
+        "vkGetPhysicalDeviceMemoryProperties2KHR",
         "vkGetPhysicalDeviceProperties",
+        "vkGetPhysicalDeviceProperties2KHR",
         "vkGetPhysicalDeviceQueueFamilyProperties",
+        "vkGetPhysicalDeviceQueueFamilyProperties2KHR",
         "vkGetPhysicalDeviceSparseImageFormatProperties",
+        "vkGetPhysicalDeviceSparseImageFormatProperties2KHR",
         "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
         "vkGetPhysicalDeviceSurfaceFormatsKHR",
         "vkGetPhysicalDeviceSurfacePresentModesKHR",
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index 87ad848..992c5d1 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -319,7 +319,7 @@
 }

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

-  #define INIT_PROC(obj, proc) do {                             \
+  #define INIT_PROC(required, obj, proc) do {                   \
       data.{{$}}.proc = reinterpret_cast<PFN_vk ## proc>(       \
               get_proc(obj, "vk" # proc));                      \
-      if (UNLIKELY(!data.{{$}}.proc)) {                         \
+      if (UNLIKELY(required && !data.{{$}}.proc)) {             \
           ALOGE("missing " # obj " proc: vk" # proc);           \
           success = false;                                      \
       }                                                         \
@@ -419,6 +419,8 @@
     INIT_PROC(§
   {{end}}
 
+  {{if GetAnnotation $ "optional"}}false{{else}}true{{end}}, §
+
   {{if (Macro "IsInstanceDispatched" $)}}
     instance, §
   {{else}}
@@ -494,9 +496,9 @@
 {{define "api.C++.DefineInitProcExtMacro"}}
   // Exported extension functions may be invoked even when their extensions
   // are disabled.  Dispatch to stubs when that happens.
-  #define INIT_PROC_EXT(ext, obj, proc) do {                    \
+  #define INIT_PROC_EXT(ext, required, obj, proc) do {          \
       if (extensions[driver::ProcHook::ext])                    \
-        INIT_PROC(obj, proc);                                   \
+        INIT_PROC(required, obj, proc);                         \
       else                                                      \
         data.dispatch.proc = disabled ## proc;                  \
   } while(0)
@@ -680,7 +682,12 @@
 {{define "driver.InterceptedExtensions"}}
 VK_ANDROID_native_buffer
 VK_EXT_debug_report
+VK_EXT_hdr_metadata
+VK_EXT_swapchain_colorspace
+VK_GOOGLE_display_timing
 VK_KHR_android_surface
+VK_KHR_incremental_present
+VK_KHR_shared_presentable_image
 VK_KHR_surface
 VK_KHR_swapchain
 {{end}}
@@ -688,6 +695,17 @@
 
 {{/*
 ------------------------------------------------------------------------------
+  Emits a list of extensions known to vulkan::driver.
+------------------------------------------------------------------------------
+*/}}
+{{define "driver.KnownExtensions"}}
+{{Macro "driver.InterceptedExtensions"}}
+VK_KHR_get_physical_device_properties2
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
   Emits true if an extension is intercepted by vulkan::driver.
 ------------------------------------------------------------------------------
 */}}
@@ -770,7 +788,7 @@
       };
 
       enum Extension {
-        {{$exts := Strings (Macro "driver.InterceptedExtensions") | SplitOn "\n"}}
+        {{$exts := Strings (Macro "driver.KnownExtensions") | SplitOn "\n"}}
         {{range $e := $exts}}
           {{TrimPrefix "VK_" $e}},
         {{end}}
@@ -796,9 +814,9 @@
 -------------------------------------------------------------------------------
 */}}
 {{define "driver.C++.DefineInitProcExtMacro"}}
-  #define INIT_PROC_EXT(ext, obj, proc) do {                    \
+  #define INIT_PROC_EXT(ext, required, obj, proc) do {          \
       if (extensions[ProcHook::ext])                            \
-        INIT_PROC(obj, proc);                                   \
+        INIT_PROC(required, obj, proc);                         \
   } while(0)
 {{end}}
 
@@ -958,6 +976,8 @@
     {{else if eq $.Name "vkCreateImage"}}true
     {{else if eq $.Name "vkDestroyImage"}}true
 
+    {{else if eq $.Name "vkGetPhysicalDeviceProperties"}}true
+    {{else if eq $.Name "vkGetPhysicalDeviceProperties2KHR"}}true
     {{end}}
 
     {{$ext := GetAnnotation $ "extension"}}
@@ -1122,8 +1142,9 @@
 
 {{/*
 ------------------------------------------------------------------------------
-  Reports whether an extension is implemented entirely by the loader,
-  so drivers should not enumerate it.
+  Reports whether an extension has functions exported by the loader.
+  E.g. applications can directly link to an extension function.
+  Currently only support WSI extensions this way.
 ------------------------------------------------------------------------------
 */}}
 {{define "IsExtensionExported"}}
diff --git a/vulkan/libvulkan/debug_report.cpp b/vulkan/libvulkan/debug_report.cpp
index 0c2f138..40ba1e5 100644
--- a/vulkan/libvulkan/debug_report.cpp
+++ b/vulkan/libvulkan/debug_report.cpp
@@ -46,7 +46,8 @@
         Node* prev = &head_;
         while (prev && prev->next != node)
             prev = prev->next;
-        prev->next = node->next;
+        if (prev)
+            prev->next = node->next;
     }
 
     allocator.pfnFree(allocator.pUserData, node);
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index f9d3de1..b62eec6 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -28,7 +28,8 @@
 
 #include <android/dlext.h>
 #include <cutils/properties.h>
-#include <gui/GraphicsEnv.h>
+#include <ui/GraphicsEnv.h>
+#include <utils/Vector.h>
 
 #include "driver.h"
 #include "stubhal.h"
@@ -446,6 +447,7 @@
         switch (ext_bit) {
             case ProcHook::KHR_android_surface:
             case ProcHook::KHR_surface:
+            case ProcHook::EXT_swapchain_colorspace:
                 hook_extensions_.set(ext_bit);
                 // return now as these extensions do not require HAL support
                 return;
@@ -454,6 +456,7 @@
                 hook_extensions_.set(ext_bit);
                 break;
             case ProcHook::EXTENSION_UNKNOWN:
+            case ProcHook::KHR_get_physical_device_properties2:
                 // HAL's extensions
                 break;
             default:
@@ -467,6 +470,15 @@
                 name = VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
                 ext_bit = ProcHook::ANDROID_native_buffer;
                 break;
+            case ProcHook::KHR_incremental_present:
+            case ProcHook::GOOGLE_display_timing:
+            case ProcHook::KHR_shared_presentable_image:
+                hook_extensions_.set(ext_bit);
+                // return now as these extensions do not require HAL support
+                return;
+            case ProcHook::EXT_hdr_metadata:
+                hook_extensions_.set(ext_bit);
+                break;
             case ProcHook::EXTENSION_UNKNOWN:
                 // HAL's extensions
                 break;
@@ -662,11 +674,13 @@
     const char* pLayerName,
     uint32_t* pPropertyCount,
     VkExtensionProperties* pProperties) {
-    static const std::array<VkExtensionProperties, 2> loader_extensions = {{
+    static const std::array<VkExtensionProperties, 3> loader_extensions = {{
         // WSI extensions
         {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION},
         {VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
          VK_KHR_ANDROID_SURFACE_SPEC_VERSION},
+        {VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
+         VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION},
     }};
     static const VkExtensionProperties loader_debug_report_extension = {
         VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_SPEC_VERSION,
@@ -719,32 +733,98 @@
     return result;
 }
 
+bool QueryPresentationProperties(
+    VkPhysicalDevice physicalDevice,
+    VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties)
+{
+    const InstanceData& data = GetData(physicalDevice);
+
+    // GPDP2 must be present and enabled on the instance.
+    if (!data.driver.GetPhysicalDeviceProperties2KHR)
+        return false;
+
+    // Request the android-specific presentation properties via GPDP2
+    VkPhysicalDeviceProperties2KHR properties = {
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+        presentation_properties,
+        {}
+    };
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+    presentation_properties->sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID;
+#pragma clang diagnostic pop
+    presentation_properties->pNext = nullptr;
+    presentation_properties->sharedImage = VK_FALSE;
+
+    data.driver.GetPhysicalDeviceProperties2KHR(physicalDevice,
+                                                &properties);
+
+    return true;
+}
+
 VkResult EnumerateDeviceExtensionProperties(
     VkPhysicalDevice physicalDevice,
     const char* pLayerName,
     uint32_t* pPropertyCount,
     VkExtensionProperties* pProperties) {
     const InstanceData& data = GetData(physicalDevice);
+    // extensions that are unconditionally exposed by the loader
+    android::Vector<VkExtensionProperties> loader_extensions;
+    loader_extensions.push_back({
+        VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
+        VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION});
+    loader_extensions.push_back({
+        VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME,
+        VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});
+
+    // conditionally add shared_presentable_image if supportable
+    VkPhysicalDevicePresentationPropertiesANDROID presentation_properties;
+    if (QueryPresentationProperties(physicalDevice, &presentation_properties) &&
+        presentation_properties.sharedImage) {
+        loader_extensions.push_back({
+            VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME,
+            VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION});
+    }
+
+    // enumerate our extensions first
+    if (!pLayerName && pProperties) {
+        uint32_t count = std::min(
+            *pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
+
+        std::copy_n(loader_extensions.begin(), count, pProperties);
+
+        if (count < loader_extensions.size()) {
+            *pPropertyCount = count;
+            return VK_INCOMPLETE;
+        }
+
+        pProperties += count;
+        *pPropertyCount -= count;
+    }
 
     VkResult result = data.driver.EnumerateDeviceExtensionProperties(
         physicalDevice, pLayerName, pPropertyCount, pProperties);
-    if (result != VK_SUCCESS && result != VK_INCOMPLETE)
-        return result;
 
-    if (!pProperties)
-        return result;
+    if (pProperties) {
+        // map VK_ANDROID_native_buffer to VK_KHR_swapchain
+        for (uint32_t i = 0; i < *pPropertyCount; i++) {
+            auto& prop = pProperties[i];
 
-    // map VK_ANDROID_native_buffer to VK_KHR_swapchain
-    for (uint32_t i = 0; i < *pPropertyCount; i++) {
-        auto& prop = pProperties[i];
+            if (strcmp(prop.extensionName,
+                       VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
+                continue;
 
-        if (strcmp(prop.extensionName,
-                   VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
-            continue;
+            memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+                   sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
+            prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+        }
+    }
 
-        memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
-               sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
-        prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+    // restore loader extension count
+    if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE)) {
+        *pPropertyCount += loader_extensions.size();
     }
 
     return result;
@@ -862,7 +942,29 @@
 
         return VK_ERROR_INCOMPATIBLE_DRIVER;
     }
+
+    // sanity check ANDROID_native_buffer implementation, whose set of
+    // entrypoints varies according to the spec version.
+    if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
+        !data->driver.GetSwapchainGrallocUsageANDROID &&
+        !data->driver.GetSwapchainGrallocUsage2ANDROID) {
+        ALOGE("Driver's implementation of ANDROID_native_buffer is broken;"
+              " must expose at least one of "
+              "vkGetSwapchainGrallocUsageANDROID or "
+              "vkGetSwapchainGrallocUsage2ANDROID");
+
+        data->driver.DestroyDevice(dev, pAllocator);
+        FreeDeviceData(data, data_allocator);
+
+        return VK_ERROR_INCOMPATIBLE_DRIVER;
+    }
+
+    VkPhysicalDeviceProperties properties;
+    instance_data.driver.GetPhysicalDeviceProperties(physicalDevice,
+                                                     &properties);
+
     data->driver_device = dev;
+    data->driver_version = properties.driverVersion;
 
     *pDevice = dev;
 
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index e058439..7f8ae98 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -102,12 +102,17 @@
 
     VkDevice driver_device;
     DeviceDriverTable driver;
+    uint32_t driver_version;
 };
 
 bool Debuggable();
 bool OpenHAL();
 const VkAllocationCallbacks& GetDefaultAllocator();
 
+bool QueryPresentationProperties(
+    VkPhysicalDevice physicalDevice,
+    VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties);
+
 // clang-format off
 VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName);
 VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName);
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 8cbd398..fc3f87a 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -75,6 +75,41 @@
     }
 }
 
+VKAPI_ATTR VkResult checkedGetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
+    if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
+        return GetRefreshCycleDurationGOOGLE(device, swapchain, pDisplayTimingProperties);
+    } else {
+        Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetRefreshCycleDurationGOOGLE not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+VKAPI_ATTR VkResult checkedGetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) {
+    if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
+        return GetPastPresentationTimingGOOGLE(device, swapchain, pPresentationTimingCount, pPresentationTimings);
+    } else {
+        Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetPastPresentationTimingGOOGLE not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+VKAPI_ATTR void checkedSetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata) {
+    if (GetData(device).hook_extensions[ProcHook::EXT_hdr_metadata]) {
+        SetHdrMetadataEXT(device, swapchainCount, pSwapchains, pMetadata);
+    } else {
+        Logger(device).Err(device, "VK_EXT_hdr_metadata not enabled. vkSetHdrMetadataEXT not executed.");
+    }
+}
+
+VKAPI_ATTR VkResult checkedGetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_shared_presentable_image]) {
+        return GetSwapchainStatusKHR(device, swapchain);
+    } else {
+        Logger(device).Err(device, "VK_KHR_shared_presentable_image not enabled. vkGetSwapchainStatusKHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
 // clang-format on
 
 const ProcHook g_proc_hooks[] = {
@@ -220,6 +255,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 +290,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 +318,13 @@
         reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainImagesKHR),
     },
     {
+        "vkGetSwapchainStatusKHR",
+        ProcHook::DEVICE,
+        ProcHook::KHR_shared_presentable_image,
+        reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainStatusKHR),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainStatusKHR),
+    },
+    {
         "vkQueuePresentKHR",
         ProcHook::DEVICE,
         ProcHook::KHR_swapchain,
@@ -275,6 +338,13 @@
         nullptr,
         nullptr,
     },
+    {
+        "vkSetHdrMetadataEXT",
+        ProcHook::DEVICE,
+        ProcHook::EXT_hdr_metadata,
+        reinterpret_cast<PFN_vkVoidFunction>(SetHdrMetadataEXT),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedSetHdrMetadataEXT),
+    },
     // clang-format on
 };
 
@@ -294,29 +364,35 @@
     // clang-format off
     if (strcmp(name, "VK_ANDROID_native_buffer") == 0) return ProcHook::ANDROID_native_buffer;
     if (strcmp(name, "VK_EXT_debug_report") == 0) return ProcHook::EXT_debug_report;
+    if (strcmp(name, "VK_EXT_hdr_metadata") == 0) return ProcHook::EXT_hdr_metadata;
+    if (strcmp(name, "VK_EXT_swapchain_colorspace") == 0) return ProcHook::EXT_swapchain_colorspace;
+    if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing;
     if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface;
+    if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present;
+    if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image;
     if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
     if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
+    if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
     // clang-format on
     return ProcHook::EXTENSION_UNKNOWN;
 }
 
 #define UNLIKELY(expr) __builtin_expect((expr), 0)
 
-#define INIT_PROC(obj, proc)                                           \
+#define INIT_PROC(required, obj, proc)                                 \
     do {                                                               \
         data.driver.proc =                                             \
             reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \
-        if (UNLIKELY(!data.driver.proc)) {                             \
+        if (UNLIKELY(required && !data.driver.proc)) {                 \
             ALOGE("missing " #obj " proc: vk" #proc);                  \
             success = false;                                           \
         }                                                              \
     } while (0)
 
-#define INIT_PROC_EXT(ext, obj, proc)  \
-    do {                               \
-        if (extensions[ProcHook::ext]) \
-            INIT_PROC(obj, proc);      \
+#define INIT_PROC_EXT(ext, required, obj, proc) \
+    do {                                        \
+        if (extensions[ProcHook::ext])          \
+            INIT_PROC(required, obj, proc);     \
     } while (0)
 
 bool InitDriverTable(VkInstance instance,
@@ -326,14 +402,16 @@
     bool success = true;
 
     // clang-format off
-    INIT_PROC(instance, DestroyInstance);
-    INIT_PROC(instance, EnumeratePhysicalDevices);
-    INIT_PROC(instance, GetInstanceProcAddr);
-    INIT_PROC(instance, CreateDevice);
-    INIT_PROC(instance, EnumerateDeviceExtensionProperties);
-    INIT_PROC_EXT(EXT_debug_report, instance, CreateDebugReportCallbackEXT);
-    INIT_PROC_EXT(EXT_debug_report, instance, DestroyDebugReportCallbackEXT);
-    INIT_PROC_EXT(EXT_debug_report, instance, DebugReportMessageEXT);
+    INIT_PROC(true, instance, DestroyInstance);
+    INIT_PROC(true, instance, EnumeratePhysicalDevices);
+    INIT_PROC(true, instance, GetInstanceProcAddr);
+    INIT_PROC(true, instance, GetPhysicalDeviceProperties);
+    INIT_PROC(true, instance, CreateDevice);
+    INIT_PROC(true, instance, EnumerateDeviceExtensionProperties);
+    INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT);
+    INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT);
+    INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT);
+    INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR);
     // clang-format on
 
     return success;
@@ -346,15 +424,16 @@
     bool success = true;
 
     // clang-format off
-    INIT_PROC(dev, GetDeviceProcAddr);
-    INIT_PROC(dev, DestroyDevice);
-    INIT_PROC(dev, GetDeviceQueue);
-    INIT_PROC(dev, CreateImage);
-    INIT_PROC(dev, DestroyImage);
-    INIT_PROC(dev, AllocateCommandBuffers);
-    INIT_PROC_EXT(ANDROID_native_buffer, dev, GetSwapchainGrallocUsageANDROID);
-    INIT_PROC_EXT(ANDROID_native_buffer, dev, AcquireImageANDROID);
-    INIT_PROC_EXT(ANDROID_native_buffer, dev, QueueSignalReleaseImageANDROID);
+    INIT_PROC(true, dev, GetDeviceProcAddr);
+    INIT_PROC(true, dev, DestroyDevice);
+    INIT_PROC(true, dev, GetDeviceQueue);
+    INIT_PROC(true, dev, CreateImage);
+    INIT_PROC(true, dev, DestroyImage);
+    INIT_PROC(true, dev, AllocateCommandBuffers);
+    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
     // clang-format on
 
     return success;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index a60b2fe..738da5b 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -35,9 +35,15 @@
     enum Extension {
         ANDROID_native_buffer,
         EXT_debug_report,
+        EXT_hdr_metadata,
+        EXT_swapchain_colorspace,
+        GOOGLE_display_timing,
         KHR_android_surface,
+        KHR_incremental_present,
+        KHR_shared_presentable_image,
         KHR_surface,
         KHR_swapchain,
+        KHR_get_physical_device_properties2,
 
         EXTENSION_CORE,  // valid bit
         EXTENSION_COUNT,
@@ -57,11 +63,13 @@
     PFN_vkDestroyInstance DestroyInstance;
     PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
     PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
+    PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties;
     PFN_vkCreateDevice CreateDevice;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
     PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
     PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
+    PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR;
     // clang-format on
 };
 
@@ -74,6 +82,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..a0ae1f3 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/Vector.h>
 
 #include "driver.h"
 
@@ -105,6 +106,54 @@
     }
 }
 
+class TimingInfo {
+   public:
+    TimingInfo() = default;
+    TimingInfo(const VkPresentTimeGOOGLE* qp, uint64_t nativeFrameId)
+        : vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0},
+          native_frame_id_(nativeFrameId) {}
+    bool ready() const {
+        return (timestamp_desired_present_time_ &&
+                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) const {
+        *values = vals_;
+    }
+
+   public:
+    VkPastPresentationTimingGOOGLE vals_ { 0, 0, 0, 0, 0 };
+
+    uint64_t native_frame_id_ { 0 };
+    uint64_t timestamp_desired_present_time_ { 0 };
+    uint64_t timestamp_actual_present_time_ { 0 };
+    uint64_t timestamp_render_complete_time_ { 0 };
+    uint64_t timestamp_composition_latch_time_ { 0 };
+};
+
 // ----------------------------------------------------------------------------
 
 struct Surface {
@@ -120,12 +169,33 @@
     return reinterpret_cast<Surface*>(handle);
 }
 
+// Maximum number of TimingInfo structs to keep per swapchain:
+enum { MAX_TIMING_INFOS = 10 };
+// Minimum number of frames to look for in the past (so we don't cause
+// syncronous requests to Surface Flinger):
+enum { MIN_NUM_FRAMES_AGO = 5 };
+
 struct Swapchain {
-    Swapchain(Surface& surface_, uint32_t num_images_)
-        : surface(surface_), num_images(num_images_) {}
+    Swapchain(Surface& surface_,
+              uint32_t num_images_,
+              VkPresentModeKHR present_mode)
+        : surface(surface_),
+          num_images(num_images_),
+          mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
+          frame_timestamps_enabled(false) {
+        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 mailbox_mode;
+    bool frame_timestamps_enabled;
+    uint64_t refresh_duration;
 
     struct Image {
         Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
@@ -138,6 +208,8 @@
         int dequeue_fence;
         bool dequeued;
     } images[android::BufferQueue::NUM_BUFFER_SLOTS];
+
+    android::Vector<TimingInfo> timing;
 };
 
 VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
@@ -205,6 +277,176 @@
             ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
     }
     swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
+    swapchain->timing.clear();
+}
+
+uint32_t get_num_ready_timings(Swapchain& swapchain) {
+    if (swapchain.timing.size() < MIN_NUM_FRAMES_AGO) {
+        return 0;
+    }
+
+    uint32_t num_ready = 0;
+    const size_t num_timings = swapchain.timing.size() - MIN_NUM_FRAMES_AGO + 1;
+    for (uint32_t i = 0; i < num_timings; i++) {
+        TimingInfo& ti = swapchain.timing.editItemAt(i);
+        if (ti.ready()) {
+            // This TimingInfo is ready to be reported to the user.  Add it
+            // to the num_ready.
+            num_ready++;
+            continue;
+        }
+        // This TimingInfo is not yet ready to be reported to the user,
+        // and so we should look for any available timestamps that
+        // might make it ready.
+        int64_t desired_present_time = 0;
+        int64_t render_complete_time = 0;
+        int64_t composition_latch_time = 0;
+        int64_t actual_present_time = 0;
+        // Obtain timestamps:
+        int ret = native_window_get_frame_timestamps(
+            swapchain.surface.window.get(), ti.native_frame_id_,
+            &desired_present_time, &render_complete_time,
+            &composition_latch_time,
+            NULL,  //&first_composition_start_time,
+            NULL,  //&last_composition_start_time,
+            NULL,  //&composition_finish_time,
+            // TODO(ianelliott): Maybe ask if this one is
+            // supported, at startup time (since it may not be
+            // supported):
+            &actual_present_time,
+            NULL,  //&display_retire_time,
+            NULL,  //&dequeue_ready_time,
+            NULL /*&reads_done_time*/);
+
+        if (ret != android::NO_ERROR) {
+            continue;
+        }
+
+        // Record the timestamp(s) we received, and then see if this TimingInfo
+        // is ready to be reported to the user:
+        ti.timestamp_desired_present_time_ =
+            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++;
+        }
+    }
+    return num_ready;
+}
+
+// TODO(ianelliott): DEAL WITH RETURN VALUE (e.g. VK_INCOMPLETE)!!!
+void copy_ready_timings(Swapchain& swapchain,
+                        uint32_t* count,
+                        VkPastPresentationTimingGOOGLE* timings) {
+    if (swapchain.timing.empty()) {
+        *count = 0;
+        return;
+    }
+
+    size_t last_ready = swapchain.timing.size() - 1;
+    while (!swapchain.timing[last_ready].ready()) {
+        if (last_ready == 0) {
+            *count = 0;
+            return;
+        }
+        last_ready--;
+    }
+
+    uint32_t num_copied = 0;
+    size_t num_to_remove = 0;
+    for (uint32_t i = 0; i <= last_ready && num_copied < *count; i++) {
+        const TimingInfo& ti = swapchain.timing[i];
+        if (ti.ready()) {
+            ti.get_values(&timings[num_copied]);
+            num_copied++;
+        }
+        num_to_remove++;
+    }
+
+    // Discard old frames that aren't ready if newer frames are ready.
+    // We don't expect to get the timing info for those old frames.
+    swapchain.timing.removeItemsAt(0, num_to_remove);
+
+    *count = num_copied;
+}
+
+android_pixel_format GetNativePixelFormat(VkFormat format) {
+    android_pixel_format native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+    switch (format) {
+        case VK_FORMAT_R8G8B8A8_UNORM:
+        case VK_FORMAT_R8G8B8A8_SRGB:
+            native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+            break;
+        case VK_FORMAT_R5G6B5_UNORM_PACK16:
+            native_format = HAL_PIXEL_FORMAT_RGB_565;
+            break;
+        case VK_FORMAT_R16G16B16A16_SFLOAT:
+            native_format = HAL_PIXEL_FORMAT_RGBA_FP16;
+            break;
+        case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+            native_format = HAL_PIXEL_FORMAT_RGBA_1010102;
+            break;
+        default:
+            ALOGV("unsupported swapchain format %d", format);
+            break;
+    }
+    return native_format;
+}
+
+android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) {
+    switch (colorspace) {
+        case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
+            return HAL_DATASPACE_V0_SRGB;
+        case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT:
+            return HAL_DATASPACE_DISPLAY_P3;
+        case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
+            return HAL_DATASPACE_V0_SCRGB_LINEAR;
+        case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT:
+            return HAL_DATASPACE_V0_SCRGB;
+        case VK_COLOR_SPACE_DCI_P3_LINEAR_EXT:
+            return HAL_DATASPACE_DCI_P3_LINEAR;
+        case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT:
+            return HAL_DATASPACE_DCI_P3;
+        case VK_COLOR_SPACE_BT709_LINEAR_EXT:
+            return HAL_DATASPACE_V0_SRGB_LINEAR;
+        case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
+            return HAL_DATASPACE_V0_SRGB;
+        case VK_COLOR_SPACE_BT2020_170M_EXT:
+            return static_cast<android_dataspace>(
+                HAL_DATASPACE_STANDARD_BT2020 |
+                HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_FULL);
+        case VK_COLOR_SPACE_BT2020_ST2084_EXT:
+            return static_cast<android_dataspace>(
+                HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
+                HAL_DATASPACE_RANGE_FULL);
+        case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT:
+            return static_cast<android_dataspace>(
+                HAL_DATASPACE_STANDARD_ADOBE_RGB |
+                HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL);
+        case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT:
+            return HAL_DATASPACE_ADOBE_RGB;
+
+        // Pass through is intended to allow app to provide data that is passed
+        // to the display system without modification.
+        case VK_COLOR_SPACE_PASS_THROUGH_EXT:
+            return HAL_DATASPACE_ARBITRARY;
+
+        default:
+            // This indicates that we don't know about the
+            // dataspace specified and we should indicate that
+            // it's unsupported
+            return HAL_DATASPACE_UNKNOWN;
+    }
 }
 
 }  // anonymous namespace
@@ -237,7 +479,7 @@
               err);
         surface->~Surface();
         allocator->pfnFree(allocator->pUserData, surface);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
     }
 
     *out_surface = HandleFromSurface(surface);
@@ -285,13 +527,13 @@
     if (err != 0) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
     err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
     if (err != 0) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     int transform_hint;
@@ -299,7 +541,7 @@
     if (err != 0) {
         ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     // TODO(jessehall): Figure out what the min/max values should be.
@@ -327,7 +569,6 @@
     // TODO(jessehall): I think these are right, but haven't thought hard about
     // it. Do we need to query the driver for support of any of these?
     // Currently not included:
-    // - VK_IMAGE_USAGE_GENERAL: maybe? does this imply cpu mappable?
     // - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not
     // - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not
     capabilities->supportedUsageFlags =
@@ -340,10 +581,12 @@
 }
 
 VKAPI_ATTR
-VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice /*pdev*/,
-                                            VkSurfaceKHR /*surface*/,
+VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev,
+                                            VkSurfaceKHR surface_handle,
                                             uint32_t* count,
                                             VkSurfaceFormatKHR* formats) {
+    const InstanceData& instance_data = GetData(pdev);
+
     // TODO(jessehall): Fill out the set of supported formats. Longer term, add
     // a new gralloc method to query whether a (format, usage) pair is
     // supported, and check that for each gralloc format that corresponds to a
@@ -356,37 +599,82 @@
         {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
     };
     const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
+    uint32_t total_num_formats = kNumFormats;
+
+    bool wide_color_support = false;
+    Surface& surface = *SurfaceFromHandle(surface_handle);
+    int err = native_window_get_wide_color_support(surface.window.get(),
+                                                   &wide_color_support);
+    if (err) {
+        // Not allowed to return a more sensible error code, so do this
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+    ALOGV("wide_color_support is: %d", wide_color_support);
+    wide_color_support =
+        wide_color_support &&
+        instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
+
+    const VkSurfaceFormatKHR kWideColorFormats[] = {
+        {VK_FORMAT_R16G16B16A16_SFLOAT,
+         VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
+        {VK_FORMAT_A2R10G10B10_UNORM_PACK32,
+         VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
+    };
+    const uint32_t kNumWideColorFormats =
+        sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]);
+    if (wide_color_support) {
+        total_num_formats += kNumWideColorFormats;
+    }
 
     VkResult result = VK_SUCCESS;
     if (formats) {
-        if (*count < kNumFormats)
+        uint32_t out_count = 0;
+        uint32_t transfer_count = 0;
+        if (*count < total_num_formats)
             result = VK_INCOMPLETE;
-        *count = std::min(*count, kNumFormats);
-        std::copy(kFormats, kFormats + *count, formats);
+        transfer_count = std::min(*count, kNumFormats);
+        std::copy(kFormats, kFormats + transfer_count, formats);
+        out_count += transfer_count;
+        if (wide_color_support) {
+            transfer_count = std::min(*count - out_count, kNumWideColorFormats);
+            std::copy(kWideColorFormats, kWideColorFormats + transfer_count,
+                      formats + out_count);
+            out_count += transfer_count;
+        }
+        *count = out_count;
     } else {
-        *count = kNumFormats;
+        *count = total_num_formats;
     }
     return result;
 }
 
 VKAPI_ATTR
-VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice /*pdev*/,
+VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev,
                                                  VkSurfaceKHR /*surface*/,
                                                  uint32_t* count,
                                                  VkPresentModeKHR* modes) {
-    const VkPresentModeKHR kModes[] = {
-        VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
-    };
-    const uint32_t kNumModes = sizeof(kModes) / sizeof(kModes[0]);
+    android::Vector<VkPresentModeKHR> present_modes;
+    present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
+    present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
+
+    VkPhysicalDevicePresentationPropertiesANDROID present_properties;
+    if (QueryPresentationProperties(pdev, &present_properties)) {
+        if (present_properties.sharedImage) {
+            present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR);
+            present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR);
+        }
+    }
+
+    uint32_t num_modes = uint32_t(present_modes.size());
 
     VkResult result = VK_SUCCESS;
     if (modes) {
-        if (*count < kNumModes)
+        if (*count < num_modes)
             result = VK_INCOMPLETE;
-        *count = std::min(*count, kNumModes);
-        std::copy(kModes, kModes + *count, modes);
+        *count = std::min(*count, num_modes);
+        std::copy(present_modes.begin(), present_modes.begin() + int(*count), modes);
     } else {
-        *count = kNumModes;
+        *count = num_modes;
     }
     return result;
 }
@@ -413,17 +701,28 @@
     if (!allocator)
         allocator = &GetData(device).allocator;
 
+    android_pixel_format native_pixel_format =
+        GetNativePixelFormat(create_info->imageFormat);
+    android_dataspace native_dataspace =
+        GetNativeDataspace(create_info->imageColorSpace);
+    if (native_dataspace == HAL_DATASPACE_UNKNOWN) {
+        ALOGE(
+            "CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) "
+            "failed: Unsupported color space",
+            create_info->imageColorSpace);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
     ALOGV_IF(create_info->imageArrayLayers != 1,
              "swapchain imageArrayLayers=%u not supported",
              create_info->imageArrayLayers);
-    ALOGV_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
-             "swapchain imageColorSpace=%u not supported",
-             create_info->imageColorSpace);
     ALOGV_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
              "swapchain preTransform=%#x not supported",
              create_info->preTransform);
     ALOGV_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
-               create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR),
+               create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ||
+               create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+               create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR),
              "swapchain presentMode=%u not supported",
              create_info->presentMode);
 
@@ -465,51 +764,55 @@
     if (err != 0) {
         ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = surface.window->setSwapInterval(surface.window.get(), 1);
+    int swap_interval =
+        create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
+    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    err = native_window_set_shared_buffer_mode(surface.window.get(), false);
+    if (err != 0) {
+        ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    err = native_window_set_auto_refresh(surface.window.get(), false);
+    if (err != 0) {
+        ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     // -- Configure the native window --
 
     const auto& dispatch = GetData(device).driver;
 
-    int native_format = HAL_PIXEL_FORMAT_RGBA_8888;
-    switch (create_info->imageFormat) {
-        case VK_FORMAT_R8G8B8A8_UNORM:
-        case VK_FORMAT_R8G8B8A8_SRGB:
-            native_format = HAL_PIXEL_FORMAT_RGBA_8888;
-            break;
-        case VK_FORMAT_R5G6B5_UNORM_PACK16:
-            native_format = HAL_PIXEL_FORMAT_RGB_565;
-            break;
-        default:
-            ALOGV("unsupported swapchain format %d", create_info->imageFormat);
-            break;
-    }
-    err = native_window_set_buffers_format(surface.window.get(), native_format);
+    err = native_window_set_buffers_format(surface.window.get(),
+                                           native_pixel_format);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
-              native_format, strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+              native_pixel_format, strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
     err = native_window_set_buffers_data_space(surface.window.get(),
-                                               HAL_DATASPACE_SRGB_LINEAR);
+                                               native_dataspace);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
-              HAL_DATASPACE_SRGB_LINEAR, strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+              native_dataspace, strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     err = native_window_set_buffers_dimensions(
@@ -521,7 +824,7 @@
         ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
               create_info->imageExtent.width, create_info->imageExtent.height,
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     // VkSwapchainCreateInfo::preTransform indicates the transformation the app
@@ -541,7 +844,7 @@
         ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
               InvertTransformToNative(create_info->preTransform),
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     err = native_window_set_scaling_mode(
@@ -551,7 +854,7 @@
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     int query_value;
@@ -563,16 +866,9 @@
         // errors and translate them to valid Vulkan result codes?
         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
               query_value);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
     uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
-    // The MIN_UNDEQUEUED_BUFFERS query doesn't know whether we'll be using
-    // async mode or not, and assumes not. But in async mode, the BufferQueue
-    // requires an extra undequeued buffer.
-    // See BufferQueueCore::getMinUndequeuedBufferCountLocked().
-    if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
-        min_undequeued_buffers += 1;
-
     uint32_t num_images =
         (create_info->minImageCount - 1) + min_undequeued_buffers;
     err = native_window_set_buffer_count(surface.window.get(), num_images);
@@ -581,39 +877,76 @@
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
               strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    VkSwapchainImageUsageFlagsANDROID swapchain_image_usage = 0;
+    if (create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+        create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+        swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID;
+
+        err = native_window_set_shared_buffer_mode(surface.window.get(), true);
+        if (err != 0) {
+            ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+    }
+
+    if (create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+        err = native_window_set_auto_refresh(surface.window.get(), true);
+        if (err != 0) {
+            ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
     }
 
     int gralloc_usage = 0;
-    // TODO(jessehall): Remove conditional once all drivers have been updated
-    if (dispatch.GetSwapchainGrallocUsageANDROID) {
+    if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+        uint64_t consumer_usage, producer_usage;
+        uint32_t driver_version = GetData(device).driver_version;
+        if (driver_version == 256587285 || driver_version == 96011958) {
+            // HACK workaround for loader/driver mismatch during transition to
+            // vkGetSwapchainGrallocUsage2ANDROID.
+            typedef VkResult(VKAPI_PTR *
+                             PFN_vkGetSwapchainGrallocUsage2ANDROID_HACK)(
+                VkDevice device, VkFormat format, VkImageUsageFlags imageUsage,
+                uint64_t * grallocConsumerUsage,
+                uint64_t * grallocProducerUsage);
+            auto get_swapchain_gralloc_usage =
+                reinterpret_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID_HACK>(
+                    dispatch.GetSwapchainGrallocUsage2ANDROID);
+            result = get_swapchain_gralloc_usage(
+                device, create_info->imageFormat, create_info->imageUsage,
+                &consumer_usage, &producer_usage);
+        } else {
+            result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+                device, create_info->imageFormat, create_info->imageUsage,
+                swapchain_image_usage, &consumer_usage, &producer_usage);
+        }
+        if (result != VK_SUCCESS) {
+            ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        // TODO: This is the same translation done by Gralloc1On0Adapter.
+        // Remove it once ANativeWindow has been updated to take gralloc1-style
+        // usages.
+        gralloc_usage =
+            static_cast<int>(consumer_usage) | static_cast<int>(producer_usage);
+    } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
         result = dispatch.GetSwapchainGrallocUsageANDROID(
             device, create_info->imageFormat, create_info->imageUsage,
             &gralloc_usage);
         if (result != VK_SUCCESS) {
             ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
-            return VK_ERROR_INITIALIZATION_FAILED;
+            return VK_ERROR_SURFACE_LOST_KHR;
         }
-    } else {
-        gralloc_usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
     }
     err = native_window_set_usage(surface.window.get(), gralloc_usage);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    int swap_interval =
-        create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
-    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
-        ALOGE("native_window->setSwapInterval(%d) failed: %s (%d)",
-              swap_interval, strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     // -- Allocate our Swapchain object --
@@ -624,17 +957,26 @@
                                          VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
     if (!mem)
         return VK_ERROR_OUT_OF_HOST_MEMORY;
-    Swapchain* swapchain = new (mem) Swapchain(surface, num_images);
+    Swapchain* swapchain =
+        new (mem) Swapchain(surface, num_images, create_info->presentMode);
 
     // -- Dequeue all buffers and create a VkImage for each --
     // Any failures during or after this must cancel the dequeued buffers.
 
+    VkSwapchainImageCreateInfoANDROID swapchain_image_create = {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+        .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
+#pragma clang diagnostic pop
+        .pNext = nullptr,
+        .usage = swapchain_image_usage,
+    };
     VkNativeBufferANDROID image_native_buffer = {
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wold-style-cast"
         .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
 #pragma clang diagnostic pop
-        .pNext = nullptr,
+        .pNext = &swapchain_image_create,
     };
     VkImageCreateInfo image_create = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@@ -663,7 +1005,7 @@
             // TODO(jessehall): Improve error reporting. Can we enumerate
             // possible errors and translate them to valid Vulkan result codes?
             ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
-            result = VK_ERROR_INITIALIZATION_FAILED;
+            result = VK_ERROR_SURFACE_LOST_KHR;
             break;
         }
         img.buffer = buffer;
@@ -677,6 +1019,12 @@
         image_native_buffer.stride = img.buffer->stride;
         image_native_buffer.format = img.buffer->format;
         image_native_buffer.usage = img.buffer->usage;
+        // TODO: Adjust once ANativeWindowBuffer supports gralloc1-style usage.
+        // For now, this is the same translation Gralloc1On0Adapter does.
+        image_native_buffer.usage2.consumer =
+            static_cast<uint64_t>(img.buffer->usage);
+        image_native_buffer.usage2.producer =
+            static_cast<uint64_t>(img.buffer->usage);
 
         result =
             dispatch.CreateImage(device, &image_create, nullptr, &img.image);
@@ -728,6 +1076,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)
@@ -790,7 +1141,7 @@
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     uint32_t idx;
@@ -862,17 +1213,56 @@
     ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
              "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
              present_info->sType);
-    ALOGV_IF(present_info->pNext, "VkPresentInfo::pNext != NULL");
 
     VkDevice device = GetData(queue).driver_device;
     const auto& dispatch = GetData(queue).driver;
     VkResult final_result = VK_SUCCESS;
 
+    // Look at the pNext chain for supported extension structs:
+    const VkPresentRegionsKHR* present_regions = nullptr;
+    const VkPresentTimesInfoGOOGLE* present_times = nullptr;
+    const VkPresentRegionsKHR* next =
+        reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext);
+    while (next) {
+        switch (next->sType) {
+            case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
+                present_regions = next;
+                break;
+            case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE:
+                present_times =
+                    reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next);
+                break;
+            default:
+                ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x",
+                      next->sType);
+                break;
+        }
+        next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext);
+    }
+    ALOGV_IF(
+        present_regions &&
+            present_regions->swapchainCount != present_info->swapchainCount,
+        "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount");
+    ALOGV_IF(present_times &&
+                 present_times->swapchainCount != present_info->swapchainCount,
+             "VkPresentTimesInfoGOOGLE::swapchainCount != "
+             "VkPresentInfo::swapchainCount");
+    const VkPresentRegionKHR* regions =
+        (present_regions) ? present_regions->pRegions : nullptr;
+    const VkPresentTimeGOOGLE* times =
+        (present_times) ? present_times->pTimes : nullptr;
+    const VkAllocationCallbacks* allocator = &GetData(device).allocator;
+    android_native_rect_t* rects = nullptr;
+    uint32_t nrects = 0;
+
     for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
         Swapchain& swapchain =
             *SwapchainFromHandle(present_info->pSwapchains[sc]);
         uint32_t image_idx = present_info->pImageIndices[sc];
         Swapchain::Image& img = swapchain.images[image_idx];
+        const VkPresentRegionKHR* region =
+            (regions && !swapchain.mailbox_mode) ? &regions[sc] : nullptr;
+        const VkPresentTimeGOOGLE* time = (times) ? &times[sc] : nullptr;
         VkResult swapchain_result = VK_SUCCESS;
         VkResult result;
         int err;
@@ -890,6 +1280,80 @@
             present_info->pSwapchains[sc]) {
             ANativeWindow* window = swapchain.surface.window.get();
             if (swapchain_result == VK_SUCCESS) {
+                if (region) {
+                    // Process the incremental-present hint for this swapchain:
+                    uint32_t rcount = region->rectangleCount;
+                    if (rcount > nrects) {
+                        android_native_rect_t* new_rects =
+                            static_cast<android_native_rect_t*>(
+                                allocator->pfnReallocation(
+                                    allocator->pUserData, rects,
+                                    sizeof(android_native_rect_t) * rcount,
+                                    alignof(android_native_rect_t),
+                                    VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
+                        if (new_rects) {
+                            rects = new_rects;
+                            nrects = rcount;
+                        } else {
+                            rcount = 0;  // Ignore the hint for this swapchain
+                        }
+                    }
+                    for (uint32_t r = 0; r < rcount; ++r) {
+                        if (region->pRectangles[r].layer > 0) {
+                            ALOGV(
+                                "vkQueuePresentKHR ignoring invalid layer "
+                                "(%u); using layer 0 instead",
+                                region->pRectangles[r].layer);
+                        }
+                        int x = region->pRectangles[r].offset.x;
+                        int y = region->pRectangles[r].offset.y;
+                        int width = static_cast<int>(
+                            region->pRectangles[r].extent.width);
+                        int height = static_cast<int>(
+                            region->pRectangles[r].extent.height);
+                        android_native_rect_t* cur_rect = &rects[r];
+                        cur_rect->left = x;
+                        cur_rect->top = y + height;
+                        cur_rect->right = x + width;
+                        cur_rect->bottom = y;
+                    }
+                    native_window_set_surface_damage(window, rects, rcount);
+                }
+                if (time) {
+                    if (!swapchain.frame_timestamps_enabled) {
+                        ALOGV(
+                            "Calling "
+                            "native_window_enable_frame_timestamps(true)");
+                        native_window_enable_frame_timestamps(window, true);
+                        swapchain.frame_timestamps_enabled = true;
+                    }
+
+                    // Record the nativeFrameId so it can be later correlated to
+                    // this present.
+                    uint64_t nativeFrameId = 0;
+                    err = native_window_get_next_frame_id(
+                            window, &nativeFrameId);
+                    if (err != android::NO_ERROR) {
+                        ALOGE("Failed to get next native frame ID.");
+                    }
+
+                    // Add a new timing record with the user's presentID and
+                    // the nativeFrameId.
+                    swapchain.timing.push_back(TimingInfo(time, nativeFrameId));
+                    while (swapchain.timing.size() > MAX_TIMING_INFOS) {
+                        swapchain.timing.removeAt(0);
+                    }
+                    if (time->desiredPresentTime) {
+                        // Set the desiredPresentTime:
+                        ALOGV(
+                            "Calling "
+                            "native_window_set_buffers_timestamp(%" PRId64 ")",
+                            time->desiredPresentTime);
+                        native_window_set_buffers_timestamp(
+                            window,
+                            static_cast<int64_t>(time->desiredPresentTime));
+                    }
+                }
                 err = window->queueBuffer(window, img.buffer.get(), fence);
                 // queueBuffer always closes fence, even on error
                 if (err != 0) {
@@ -920,9 +1384,80 @@
         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;
+}
+
+VKAPI_ATTR void SetHdrMetadataEXT(
+    VkDevice device,
+    uint32_t swapchainCount,
+    const VkSwapchainKHR* pSwapchains,
+    const VkHdrMetadataEXT* pHdrMetadataEXTs) {
+    // TODO: courtneygo: implement actual function
+    (void)device;
+    (void)swapchainCount;
+    (void)pSwapchains;
+    (void)pHdrMetadataEXTs;
+    return;
+}
+
 }  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h
index 2c60c49..976c995 100644
--- a/vulkan/libvulkan/swapchain.h
+++ b/vulkan/libvulkan/swapchain.h
@@ -27,13 +27,17 @@
 VKAPI_ATTR void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* allocator);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice pdev, uint32_t queue_family, VkSurfaceKHR surface, VkBool32* pSupported);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* capabilities);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, uint32_t* count, VkSurfaceFormatKHR* formats);
+VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface_handle, uint32_t* count, VkSurfaceFormatKHR* formats);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, uint32_t* count, VkPresentModeKHR* modes);
 VKAPI_ATTR VkResult CreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* create_info, const VkAllocationCallbacks* allocator, VkSwapchainKHR* swapchain_handle);
 VKAPI_ATTR void DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain_handle, const VkAllocationCallbacks* allocator);
 VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint32_t* count, VkImage* images);
 VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* image_index);
 VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info);
+VKAPI_ATTR VkResult GetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties);
+VKAPI_ATTR VkResult GetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings);
+VKAPI_ATTR VkResult GetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain);
+VKAPI_ATTR void SetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pHdrMetadataEXTs);
 // clang-format on
 
 }  // namespace driver
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index e966c06..e03ee0a 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,18 @@
     return VK_SUCCESS;
 }
 
+VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice,
+                                          VkFormat,
+                                          VkImageUsageFlags,
+                                          VkSwapchainImageUsageFlagsANDROID,
+                                          uint64_t* grallocConsumerUsage,
+                                          uint64_t* grallocProducerUsage) {
+    // The null driver never reads or writes the gralloc buffer
+    *grallocConsumerUsage = 0;
+    *grallocProducerUsage = 0;
+    return VK_SUCCESS;
+}
+
 VkResult AcquireImageANDROID(VkDevice,
                              VkImage,
                              int fence,
@@ -1073,11 +1100,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 +1168,14 @@
     ALOGV("TODO: vk%s", __FUNCTION__);
 }
 
+void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice,
+                                                      VkPhysicalDeviceSparseImageFormatInfo2KHR const* pInfo,
+                                                      unsigned int* pNumProperties,
+                                                      VkSparseImageFormatProperties2KHR* pProperties) {
+    ALOGV("TODO: vk%s", __FUNCTION__);
+}
+
+
 VkResult QueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence) {
     ALOGV("TODO: vk%s", __FUNCTION__);
     return VK_SUCCESS;
diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl
index 209d61d..ce15517 100644
--- a/vulkan/nulldrv/null_driver.tmpl
+++ b/vulkan/nulldrv/null_driver.tmpl
@@ -205,5 +205,6 @@
   {{$ext := index $.Arguments 0}}
   {{     if eq $ext "VK_ANDROID_native_buffer"}}true
   {{else if eq $ext "VK_EXT_debug_report"}}true
+  {{else if eq $ext "VK_KHR_get_physical_device_properties2"}}true
   {{end}}
 {{end}}
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index b078ad1..7cbbdb1 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -171,15 +171,23 @@
     {"vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSubresourceLayout>(GetImageSubresourceLayout))},
     {"vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr))},
     {"vkGetPhysicalDeviceFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFeatures>(GetPhysicalDeviceFeatures))},
+    {"vkGetPhysicalDeviceFeatures2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(GetPhysicalDeviceFeatures2KHR))},
     {"vkGetPhysicalDeviceFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFormatProperties>(GetPhysicalDeviceFormatProperties))},
+    {"vkGetPhysicalDeviceFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFormatProperties2KHR>(GetPhysicalDeviceFormatProperties2KHR))},
     {"vkGetPhysicalDeviceImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(GetPhysicalDeviceImageFormatProperties))},
+    {"vkGetPhysicalDeviceImageFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceImageFormatProperties2KHR>(GetPhysicalDeviceImageFormatProperties2KHR))},
     {"vkGetPhysicalDeviceMemoryProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(GetPhysicalDeviceMemoryProperties))},
+    {"vkGetPhysicalDeviceMemoryProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceMemoryProperties2KHR>(GetPhysicalDeviceMemoryProperties2KHR))},
     {"vkGetPhysicalDeviceProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceProperties>(GetPhysicalDeviceProperties))},
+    {"vkGetPhysicalDeviceProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(GetPhysicalDeviceProperties2KHR))},
     {"vkGetPhysicalDeviceQueueFamilyProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(GetPhysicalDeviceQueueFamilyProperties))},
+    {"vkGetPhysicalDeviceQueueFamilyProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR>(GetPhysicalDeviceQueueFamilyProperties2KHR))},
     {"vkGetPhysicalDeviceSparseImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(GetPhysicalDeviceSparseImageFormatProperties))},
+    {"vkGetPhysicalDeviceSparseImageFormatProperties2KHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR>(GetPhysicalDeviceSparseImageFormatProperties2KHR))},
     {"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
     {"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
     {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+    {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
     {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
     {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
     {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 4052d26..d73bf14 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -165,12 +165,20 @@
 VKAPI_ATTR void CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents);
 VKAPI_ATTR void CmdEndRenderPass(VkCommandBuffer commandBuffer);
 VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers);
-VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int32_t* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
 VKAPI_ATTR VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
 VKAPI_ATTR void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2KHR(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 801eca8..89bc926 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -176,7 +176,7 @@
         .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
         .queueFamilyIndex = 0,
         .queueCount = 1,
-        queue_priorities
+        .pQueuePriorities = queue_priorities
     };
     // clang-format off
     const char *kValidationLayers[] = {