Merge "Validate the size of all dex paths eagerly" into oc-dr1-dev
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
index 5548699..abf7b06 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -14,6 +14,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 d61cbc9..2d9a98c 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -40,6 +40,7 @@
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
 
+#include <pdx/default_transport/service_utility.h>
 #include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Tokenizer.h>
@@ -50,6 +51,7 @@
 #include <android-base/stringprintf.h>
 
 using namespace android;
+using pdx::default_transport::ServiceUtility;
 
 using std::string;
 
@@ -61,6 +63,7 @@
 const char* k_traceAppsNumberProperty = "debug.atrace.app_number";
 const char* k_traceAppsPropertyTemplate = "debug.atrace.app_%d";
 const char* k_coreServiceCategory = "core_services";
+const char* k_pdxServiceCategory = "pdx";
 const char* k_coreServicesProp = "ro.atrace.core.services";
 
 typedef enum { OPT, REQ } requiredness  ;
@@ -114,6 +117,7 @@
     { "network",    "Network",          ATRACE_TAG_NETWORK, { } },
     { "adb",        "ADB",              ATRACE_TAG_ADB, { } },
     { k_coreServiceCategory, "Core services", 0, { } },
+    { k_pdxServiceCategory, "PDX services", 0, { } },
     { "sched",      "CPU Scheduling",   0, {
         { REQ,      "events/sched/sched_switch/enable" },
         { REQ,      "events/sched/sched_wakeup/enable" },
@@ -209,6 +213,7 @@
 static const char* g_outputFile = nullptr;
 
 /* Global state */
+static bool g_tracePdx = false;
 static bool g_traceAborted = false;
 static bool g_categoryEnables[arraysize(k_categories)] = {};
 static std::string g_traceFolder;
@@ -368,6 +373,10 @@
         return !android::base::GetProperty(k_coreServicesProp, "").empty();
     }
 
+    if (strcmp(category.name, k_pdxServiceCategory) == 0) {
+        return true;
+    }
+
     bool ok = category.tags != 0;
     for (int i = 0; i < MAX_SYS_FILES; i++) {
         const char* path = category.sysfiles[i].path;
@@ -769,7 +778,8 @@
     ok &= setCategoriesEnableFromFile(g_categoriesFile);
     ok &= setTraceOverwriteEnable(g_traceOverwrite);
     ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
-    ok &= setCmdlineSize();
+    // TODO: Re-enable after stabilization
+    //ok &= setCmdlineSize();
     ok &= setClock();
     ok &= setPrintTgidEnableIfPresent(true);
     ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
@@ -789,6 +799,11 @@
         if (strcmp(k_categories[i].name, k_coreServiceCategory) == 0) {
             coreServicesTagEnabled = g_categoryEnables[i];
         }
+
+        // Set whether to poke PDX services in this session.
+        if (strcmp(k_categories[i].name, k_pdxServiceCategory) == 0) {
+            g_tracePdx = g_categoryEnables[i];
+        }
     }
 
     std::string packageList(g_debugAppCmdLine);
@@ -802,6 +817,10 @@
     ok &= pokeBinderServices();
     pokeHalServices();
 
+    if (g_tracePdx) {
+        ok &= ServiceUtility::PokeServices();
+    }
+
     // Disable all the sysfs enables.  This is done as a separate loop from
     // the enables to allow the same enable to exist in multiple categories.
     ok &= disableKernelTraceEvents();
@@ -839,6 +858,10 @@
     clearAppProperties();
     pokeBinderServices();
 
+    if (g_tracePdx) {
+        ServiceUtility::PokeServices();
+    }
+
     // Set the options back to their defaults.
     setTraceOverwriteEnable(true);
     setTraceBufferSizeKB(1);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 95dc8dd..4161bd7 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -123,7 +123,14 @@
 static const std::string ZIP_ROOT_DIR = "FS";
 
 // Must be hardcoded because dumpstate HAL implementation need SELinux access to it
-static const std::string kDumpstateBoardPath = "/bugreports/dumpstate_board.txt";
+static const std::string kDumpstateBoardPath = "/bugreports/";
+static const std::string kDumpstateBoardFiles[] = {
+    "dumpstate_board.txt",
+    // TODO: rename to dumpstate_board.bin once vendors can handle it
+    "modem_log_all.tar"
+};
+static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles);
+
 static const std::string kLsHalDebugPath = "/bugreports/dumpstate_lshal.txt";
 
 static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
@@ -445,88 +452,6 @@
     }
 }
 
-/**
- * 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 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());
-        return "";
-    }
-
-    // Find the newest file matching the file_prefix in dir
-    struct dirent *de;
-    time_t last_modified_time = 0;
-    std::string last_modified_file = "";
-    struct stat s;
-
-    while ((de = readdir(d.get()))) {
-        std::string file = std::string(de->d_name);
-        if (!file_prefix.empty()) {
-            if (!android::base::StartsWith(file, file_prefix.c_str())) continue;
-        }
-        file = dir + "/" + file;
-        int ret = stat(file.c_str(), &s);
-
-        if ((ret == 0) && (s.st_mtime > last_modified_time)) {
-            last_modified_file = file;
-            last_modified_time = s.st_mtime;
-        }
-    }
-
-    return last_modified_file;
-}
-
-static void DumpModemLogs() {
-    DurationReporter durationReporter("DUMP MODEM LOGS");
-    if (PropertiesHelper::IsUserBuild()) {
-        return;
-    }
-
-    if (!ds.IsZipping()) {
-        MYLOGD("Not dumping modem logs. dumpstate is not generating a zipping bugreport\n");
-        return;
-    }
-
-    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;
-    }
-
-    // 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());
-    }
-
-    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) {
-        MYLOGD("Modem log %s does not exist\n", modem_log_file.c_str());
-        return;
-    }
-
-    std::string filename = basename(modem_log_file.c_str());
-    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());
-        if (remove(modem_log_file.c_str())) {
-            MYLOGE("Error removing modem log %s\n", modem_log_file.c_str());
-        }
-    }
-}
-
 static bool skip_not_stat(const char *path) {
     static const char stat[] = "/stat";
     size_t len = strlen(path);
@@ -918,7 +843,7 @@
                         "-d", "*:v"});
 }
 
-static void DumpIpTables() {
+static void DumpIpTablesAsRoot() {
     RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
     RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
     RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
@@ -1088,6 +1013,24 @@
     }
      return;
 }
+
+static void DumpPacketStats() {
+    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");
+}
+
+static void DumpIpAddrAndRules() {
+    /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
+    RunCommand("NETWORK INTERFACES", {"ip", "link"});
+    RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
+    RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
+    RunCommand("IP RULES", {"ip", "rule", "show"});
+    RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
+}
+
 static void dumpstate() {
     DurationReporter duration_reporter("DUMPSTATE");
 
@@ -1165,23 +1108,11 @@
         printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR.c_str());
     }
 
-    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");
+    DumpPacketStats();
 
     DoKmsg();
 
-    /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
-
-    RunCommand("NETWORK INTERFACES", {"ip", "link"});
-
-    RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
-    RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
-
-    RunCommand("IP RULES", {"ip", "rule", "show"});
-    RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
+    DumpIpAddrAndRules();
 
     dump_route_tables();
 
@@ -1282,11 +1213,6 @@
     RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"});
     RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"});
 
-    // DumpModemLogs adds the modem logs if available to the bugreport.
-    // Do this at the end to allow for sufficient time for the modem logs to be
-    // collected.
-    DumpModemLogs();
-
     printf("========================================================\n");
     printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
            ds.progress_->GetMax(), ds.progress_->GetInitialMax());
@@ -1295,6 +1221,46 @@
     printf("========================================================\n");
 }
 
+// This method collects dumpsys for telephony debugging only
+static void DumpstateTelephonyOnly() {
+    DurationReporter duration_reporter("DUMPSTATE");
+
+    DumpIpTablesAsRoot();
+
+    if (!DropRootUser()) {
+        return;
+    }
+
+    do_dmesg();
+    DoLogcat();
+    DumpPacketStats();
+    DoKmsg();
+    DumpIpAddrAndRules();
+    dump_route_tables();
+
+    RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
+               CommandOptions::WithTimeout(10).Build());
+
+    RunCommand("SYSTEM PROPERTIES", {"getprop"});
+
+    printf("========================================================\n");
+    printf("== Android Framework Services\n");
+    printf("========================================================\n");
+
+    RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), 10);
+    RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), 10);
+
+    printf("========================================================\n");
+    printf("== Running Application Services\n");
+    printf("========================================================\n");
+
+    RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"});
+
+    printf("========================================================\n");
+    printf("== dumpstate: done (id %d)\n", ds.id_);
+    printf("========================================================\n");
+}
+
 void Dumpstate::DumpstateBoard() {
     DurationReporter duration_reporter("dumpstate_board()");
     printf("========================================================\n");
@@ -1312,23 +1278,35 @@
         return;
     }
 
-    std::string path = kDumpstateBoardPath;
-    MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path.c_str());
+    std::string path[NUM_OF_DUMPS];
+    android::base::unique_fd fd[NUM_OF_DUMPS];
+    int numFds = 0;
 
-    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;
+    for (int i = 0; i < NUM_OF_DUMPS; i++) {
+        path[i] = kDumpstateBoardPath + kDumpstateBoardFiles[i];
+        MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path[i].c_str());
+
+        fd[i] = android::base::unique_fd(
+            TEMP_FAILURE_RETRY(open(path[i].c_str(),
+            O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+        if (fd[i] < 0) {
+            MYLOGE("Could not open file %s: %s\n", path[i].c_str(), strerror(errno));
+            return;
+        } else {
+            numFds++;
+        }
     }
 
-    native_handle_t* handle = native_handle_create(1, 0);
+    native_handle_t *handle = native_handle_create(numFds, 0);
     if (handle == nullptr) {
         MYLOGE("Could not create native_handle\n");
         return;
     }
-    handle->data[0] = fd;
+
+    for (int i = 0; i < numFds; i++) {
+        handle->data[i] = fd[i].release();
+    }
 
     // TODO: need a timeout mechanism so dumpstate does not hang on device implementation call.
     android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle);
@@ -1339,14 +1317,26 @@
         return;
     }
 
-    AddZipEntry("dumpstate-board.txt", path);
+    for (int i = 0; i < numFds; i++) {
+        struct stat s;
+        if (fstat(handle->data[i], &s) == -1) {
+            MYLOGE("Failed to fstat %s: %d\n", kDumpstateBoardFiles[i].c_str(), errno);
+        } else if (s.st_size > 0) {
+            AddZipEntry(kDumpstateBoardFiles[i], path[i]);
+        } else {
+            MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str());
+        }
+    }
+
     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));
+    for (int i = 0; i < numFds; i++) {
+        if (remove(path[i].c_str()) != 0) {
+            MYLOGE("Could not remove(%s): %s\n", path[i].c_str(), strerror(errno));
+        }
     }
 }
 
@@ -1808,15 +1798,8 @@
     ds.PrintHeader();
 
     if (telephony_only) {
-        DumpIpTables();
-        if (!DropRootUser()) {
-            return -1;
-        }
-        do_dmesg();
-        DoLogcat();
-        DoKmsg();
+        DumpstateTelephonyOnly();
         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
@@ -1850,7 +1833,7 @@
             ds.AddDir(PROFILE_DATA_DIR_REF, true);
         }
         add_mountinfo();
-        DumpIpTables();
+        DumpIpTablesAsRoot();
 
         // Capture any IPSec policies in play.  No keys are exposed here.
         RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 87b9104..7c6cfd9 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -244,7 +244,18 @@
     mOut << std::endl;
 }
 
+static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
+    for (vintf::Version& v : hal->versions) {
+        if (v.majorVer == version.majorVer) {
+            v.minorVer = std::max(v.minorVer, version.minorVer);
+            return true;
+        }
+    }
+    return false;
+}
+
 void ListCommand::dumpVintf() const {
+    using vintf::operator|=;
     mOut << "<!-- " << std::endl
          << "    This is a skeleton device manifest. Notes: " << std::endl
          << "    1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
@@ -252,7 +263,9 @@
          << "       only hwbinder is shown." << std::endl
          << "    3. It is likely that HALs in passthrough transport does not have" << std::endl
          << "       <interface> declared; users will have to write them by hand." << std::endl
-         << "    4. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
+         << "    4. A HAL with lower minor version can be overridden by a HAL with" << std::endl
+         << "       higher minor version if they have the same name and major version." << std::endl
+         << "    5. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
          << "       is removed from the manifest file and written by assemble_vintf" << std::endl
          << "       at build time." << std::endl
          << "-->" << std::endl;
@@ -316,17 +329,19 @@
             for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
                 if (hal->transport() != transport) {
                     if (transport != vintf::Transport::PASSTHROUGH) {
-                        mErr << "Fatal: should not reach here. Generated result may be wrong."
+                        mErr << "Fatal: should not reach here. Generated result may be wrong for '"
+                             << hal->name << "'."
                              << std::endl;
                     }
                     done = true;
                     break;
                 }
-                if (hal->hasVersion(version)) {
+                if (findAndBumpVersion(hal, version)) {
                     if (&table != &mImplementationsTable) {
                         hal->interfaces[interfaceName].name = interfaceName;
                         hal->interfaces[interfaceName].instances.insert(instanceName);
                     }
+                    hal->transportArch.arch |= arch;
                     done = true;
                     break;
                 }
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 45bb1d0..d5cfcaf 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -287,7 +287,11 @@
     }
 
     if (sehandle && selinux_status_updated() > 0) {
+#ifdef VENDORSERVICEMANAGER
+        struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle();
+#else
         struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
+#endif
         if (tmp_sehandle) {
             selabel_close(sehandle);
             sehandle = tmp_sehandle;
diff --git a/include/gui b/include/gui
new file mode 120000
index 0000000..3b796f3
--- /dev/null
+++ b/include/gui
@@ -0,0 +1 @@
+../libs/gui/include/gui
\ No newline at end of file
diff --git a/include/private/gui b/include/private/gui
new file mode 120000
index 0000000..99de2dc
--- /dev/null
+++ b/include/private/gui
@@ -0,0 +1 @@
+../../libs/gui/include/private/gui
\ No newline at end of file
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 204fdb5..3a353c2 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -15,6 +15,7 @@
 cc_library_headers {
     name: "libbinder_headers",
     export_include_dirs: ["include"],
+    vendor_available: true,
 }
 
 cc_library {
@@ -74,13 +75,18 @@
         "libcutils",
         "libutils",
     ],
+
+    header_libs: [
+        "libbinder_headers",
+    ],
+
     export_shared_lib_headers: [
         "libbase",
         "libutils",
     ],
 
-    export_include_dirs: [
-        "include",
+    export_header_lib_headers: [
+        "libbinder_headers",
     ],
 
     clang: true,
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index c0ae3d7..4558fe8 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -11,9 +11,15 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+cc_library_headers {
+    name: "libgui_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
 
 cc_library_shared {
     name: "libgui",
+    vendor_available: true,
 
     clang: true,
     cppflags: [
@@ -57,7 +63,7 @@
         brillo: {
             cflags: ["-DHAVE_NO_SURFACE_FLINGER"],
         },
-        debuggable: {
+        eng: {
             cppflags: [
                 "-UDEBUG_ONLY_CODE",
                 "-DDEBUG_ONLY_CODE=1",
@@ -119,16 +125,22 @@
 
     header_libs: [
         "libnativebase_headers",
+        "libgui_headers",
     ],
 
     export_shared_lib_headers: [
         "libbinder",
-        "libui",
+        "libEGL",
         "libnativewindow",
+        "libui",
         "android.hidl.token@1.0-utils",
         "android.hardware.graphics.bufferqueue@1.0",
     ],
 
+    export_header_lib_headers: [
+        "libgui_headers",
+    ],
+
     export_include_dirs: [
         "include",
     ],
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index c2b10a9..3d36376 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -106,7 +106,7 @@
 
     sp<FrameAvailableListener> listener;
     { // scope for the lock
-        Mutex::Autolock lock(mMutex);
+        Mutex::Autolock lock(mFrameAvailableMutex);
         listener = mFrameAvailableListener.promote();
     }
 
@@ -121,7 +121,7 @@
 
     sp<FrameAvailableListener> listener;
     {
-        Mutex::Autolock lock(mMutex);
+        Mutex::Autolock lock(mFrameAvailableMutex);
         listener = mFrameAvailableListener.promote();
     }
 
@@ -185,7 +185,7 @@
 void ConsumerBase::setFrameAvailableListener(
         const wp<FrameAvailableListener>& listener) {
     CB_LOGV("setFrameAvailableListener");
-    Mutex::Autolock lock(mMutex);
+    Mutex::Autolock lock(mFrameAvailableMutex);
     mFrameAvailableListener = listener;
 }
 
diff --git a/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h
similarity index 100%
rename from include/gui/BufferItem.h
rename to libs/gui/include/gui/BufferItem.h
diff --git a/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h
similarity index 100%
rename from include/gui/BufferItemConsumer.h
rename to libs/gui/include/gui/BufferItemConsumer.h
diff --git a/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
similarity index 100%
rename from include/gui/BufferQueue.h
rename to libs/gui/include/gui/BufferQueue.h
diff --git a/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
similarity index 100%
rename from include/gui/BufferQueueConsumer.h
rename to libs/gui/include/gui/BufferQueueConsumer.h
diff --git a/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
similarity index 100%
rename from include/gui/BufferQueueCore.h
rename to libs/gui/include/gui/BufferQueueCore.h
diff --git a/include/gui/BufferQueueDefs.h b/libs/gui/include/gui/BufferQueueDefs.h
similarity index 100%
rename from include/gui/BufferQueueDefs.h
rename to libs/gui/include/gui/BufferQueueDefs.h
diff --git a/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
similarity index 100%
rename from include/gui/BufferQueueProducer.h
rename to libs/gui/include/gui/BufferQueueProducer.h
diff --git a/include/gui/BufferSlot.h b/libs/gui/include/gui/BufferSlot.h
similarity index 100%
rename from include/gui/BufferSlot.h
rename to libs/gui/include/gui/BufferSlot.h
diff --git a/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
similarity index 98%
rename from include/gui/ConsumerBase.h
rename to libs/gui/include/gui/ConsumerBase.h
index d1a9b04..e9fc8fd 100644
--- a/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -241,7 +241,9 @@
 
     // mFrameAvailableListener is the listener object that will be called when a
     // new frame becomes available. If it is not NULL it will be called from
-    // queueBuffer.
+    // queueBuffer. The listener object is protected by mFrameAvailableMutex
+    // (not mMutex).
+    Mutex mFrameAvailableMutex;
     wp<FrameAvailableListener> mFrameAvailableListener;
 
     // The ConsumerBase has-a BufferQueue and is responsible for creating this object
diff --git a/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
similarity index 100%
rename from include/gui/CpuConsumer.h
rename to libs/gui/include/gui/CpuConsumer.h
diff --git a/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
similarity index 100%
rename from include/gui/DisplayEventReceiver.h
rename to libs/gui/include/gui/DisplayEventReceiver.h
diff --git a/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
similarity index 100%
rename from include/gui/FrameTimestamps.h
rename to libs/gui/include/gui/FrameTimestamps.h
diff --git a/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
similarity index 100%
rename from include/gui/GLConsumer.h
rename to libs/gui/include/gui/GLConsumer.h
diff --git a/include/gui/GuiConfig.h b/libs/gui/include/gui/GuiConfig.h
similarity index 100%
rename from include/gui/GuiConfig.h
rename to libs/gui/include/gui/GuiConfig.h
diff --git a/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
similarity index 100%
rename from include/gui/IConsumerListener.h
rename to libs/gui/include/gui/IConsumerListener.h
diff --git a/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
similarity index 100%
rename from include/gui/IDisplayEventConnection.h
rename to libs/gui/include/gui/IDisplayEventConnection.h
diff --git a/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
similarity index 100%
rename from include/gui/IGraphicBufferConsumer.h
rename to libs/gui/include/gui/IGraphicBufferConsumer.h
diff --git a/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
similarity index 100%
rename from include/gui/IGraphicBufferProducer.h
rename to libs/gui/include/gui/IGraphicBufferProducer.h
diff --git a/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
similarity index 100%
rename from include/gui/IProducerListener.h
rename to libs/gui/include/gui/IProducerListener.h
diff --git a/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
similarity index 100%
rename from include/gui/ISurfaceComposer.h
rename to libs/gui/include/gui/ISurfaceComposer.h
diff --git a/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
similarity index 100%
rename from include/gui/ISurfaceComposerClient.h
rename to libs/gui/include/gui/ISurfaceComposerClient.h
diff --git a/include/gui/OccupancyTracker.h b/libs/gui/include/gui/OccupancyTracker.h
similarity index 100%
rename from include/gui/OccupancyTracker.h
rename to libs/gui/include/gui/OccupancyTracker.h
diff --git a/include/gui/StreamSplitter.h b/libs/gui/include/gui/StreamSplitter.h
similarity index 100%
rename from include/gui/StreamSplitter.h
rename to libs/gui/include/gui/StreamSplitter.h
diff --git a/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
similarity index 100%
rename from include/gui/Surface.h
rename to libs/gui/include/gui/Surface.h
diff --git a/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
similarity index 100%
rename from include/gui/SurfaceComposerClient.h
rename to libs/gui/include/gui/SurfaceComposerClient.h
diff --git a/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
similarity index 100%
rename from include/gui/SurfaceControl.h
rename to libs/gui/include/gui/SurfaceControl.h
diff --git a/include/gui/bufferqueue/1.0/B2HProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/B2HProducerListener.h
similarity index 100%
rename from include/gui/bufferqueue/1.0/B2HProducerListener.h
rename to libs/gui/include/gui/bufferqueue/1.0/B2HProducerListener.h
diff --git a/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
similarity index 100%
rename from include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
rename to libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
diff --git a/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
similarity index 100%
rename from include/gui/view/Surface.h
rename to libs/gui/include/gui/view/Surface.h
diff --git a/include/private/gui/ComposerService.h b/libs/gui/include/private/gui/ComposerService.h
similarity index 100%
rename from include/private/gui/ComposerService.h
rename to libs/gui/include/private/gui/ComposerService.h
diff --git a/include/private/gui/LayerState.h b/libs/gui/include/private/gui/LayerState.h
similarity index 100%
rename from include/private/gui/LayerState.h
rename to libs/gui/include/private/gui/LayerState.h
diff --git a/include/private/gui/SyncFeatures.h b/libs/gui/include/private/gui/SyncFeatures.h
similarity index 100%
rename from include/private/gui/SyncFeatures.h
rename to libs/gui/include/private/gui/SyncFeatures.h
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d9cfed7..6630d90 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -89,6 +89,7 @@
 
     header_libs: [
         "libnativebase_headers",
+        "libhardware_headers",
     ],
 
     export_include_dirs: ["include"],
@@ -100,6 +101,7 @@
 
     export_header_lib_headers: [
         "libnativebase_headers",
+        "libhardware_headers",
     ],
 }
 
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index e149803..87dbaf4 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -16,7 +16,6 @@
 
 #define LOG_TAG "Gralloc2"
 
-#include <hidl/ServiceManagement.h>
 #include <hwbinder/IPCThreadState.h>
 #include <ui/Gralloc2.h>
 
@@ -251,17 +250,4 @@
 
 } // namespace Gralloc2
 
-namespace {
-// Load the IMapper implementation library when this shared library is loaded, rather than when
-// we (lazily) create the Gralloc2::Mapper instance. Since these libraries will all be needed by
-// nearly all apps, this allows us to load them in Zygote rather than on each app launch.
-class PreloadMapperImpl {
-public:
-    PreloadMapperImpl() {
-        android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>();
-    }
-};
-static PreloadMapperImpl preloadMapperImpl;
-} // namespace
-
 } // namespace android
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 4041d6b..7fe9825 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -31,6 +31,7 @@
     "dvr_configuration_data.cpp",
     "dvr_display_manager.cpp",
     "dvr_hardware_composer_client.cpp",
+    "dvr_performance.cpp",
     "dvr_surface.cpp",
     "dvr_vsync.cpp",
 ]
@@ -45,6 +46,7 @@
     "libvr_hwc-impl",
     "libvr_hwc-binder",
     "libgrallocusage",
+    "libperformance",
     "libpdx_default_transport",
 ]
 
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
index dc31917..7d4e2d1 100644
--- a/libs/vr/libdvr/dvr_api.cpp
+++ b/libs/vr/libdvr/dvr_api.cpp
@@ -10,6 +10,7 @@
 #include <dvr/dvr_buffer_queue.h>
 #include <dvr/dvr_configuration_data.h>
 #include <dvr/dvr_display_manager.h>
+#include <dvr/dvr_performance.h>
 #include <dvr/dvr_surface.h>
 #include <dvr/dvr_vsync.h>
 
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
index aa2ed94..2e1655f 100644
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -313,7 +313,10 @@
 int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
                               DvrReadBuffer* read_buffer, int* out_fence_fd,
                               void* out_meta, size_t meta_size_bytes) {
-  if (!read_queue || !read_buffer || !out_fence_fd || !out_meta)
+  if (!read_queue || !read_buffer || !out_fence_fd)
+    return -EINVAL;
+
+  if (meta_size_bytes != 0 && !out_meta)
     return -EINVAL;
 
   return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta,
diff --git a/libs/vr/libdvr/dvr_hardware_composer_client.cpp b/libs/vr/libdvr/dvr_hardware_composer_client.cpp
index d3ae299..46d72ca 100644
--- a/libs/vr/libdvr/dvr_hardware_composer_client.cpp
+++ b/libs/vr/libdvr/dvr_hardware_composer_client.cpp
@@ -6,7 +6,9 @@
 #include <binder/IServiceManager.h>
 #include <private/android/AHardwareBufferHelpers.h>
 
+#include <functional>
 #include <memory>
+#include <mutex>
 
 struct DvrHwcFrame {
   android::dvr::ComposerView::Frame frame;
@@ -16,10 +18,15 @@
 
 class HwcCallback : public android::dvr::BnVrComposerCallback {
  public:
-  explicit HwcCallback(DvrHwcOnFrameCallback callback,
-                       void* client_state);
+  using CallbackFunction = std::function<int(DvrHwcFrame*)>;
+
+  explicit HwcCallback(const CallbackFunction& callback);
   ~HwcCallback() override;
 
+  // Reset the callback. This needs to be done early to avoid use after free
+  // accesses from binder thread callbacks.
+  void Shutdown();
+
   std::unique_ptr<DvrHwcFrame> DequeueFrame();
 
  private:
@@ -28,26 +35,41 @@
       const android::dvr::ParcelableComposerFrame& frame,
       android::dvr::ParcelableUniqueFd* fence) override;
 
-  DvrHwcOnFrameCallback callback_;
-  void* client_state_;
+  // Protects the |callback_| from uses from multiple threads. During shutdown
+  // there may be in-flight frame update events. In those cases the callback
+  // access needs to be protected otherwise binder threads may access an invalid
+  // callback.
+  std::mutex mutex_;
+  CallbackFunction callback_;
 
   HwcCallback(const HwcCallback&) = delete;
   void operator=(const HwcCallback&) = delete;
 };
 
-HwcCallback::HwcCallback(DvrHwcOnFrameCallback callback, void* client_state)
-    : callback_(callback), client_state_(client_state) {}
+HwcCallback::HwcCallback(const CallbackFunction& callback)
+    : callback_(callback) {}
 
 HwcCallback::~HwcCallback() {}
 
+void HwcCallback::Shutdown() {
+  std::lock_guard<std::mutex> guard(mutex_);
+  callback_ = nullptr;
+}
+
 android::binder::Status HwcCallback::onNewFrame(
     const android::dvr::ParcelableComposerFrame& frame,
     android::dvr::ParcelableUniqueFd* fence) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  if (!callback_) {
+    fence->set_fence(android::base::unique_fd());
+    return android::binder::Status::ok();
+  }
+
   std::unique_ptr<DvrHwcFrame> dvr_frame(new DvrHwcFrame());
   dvr_frame->frame = frame.frame();
 
-  fence->set_fence(android::base::unique_fd(callback_(client_state_,
-                                                      dvr_frame.release())));
+  fence->set_fence(android::base::unique_fd(callback_(dvr_frame.release())));
   return android::binder::Status::ok();
 }
 
@@ -67,7 +89,8 @@
   if (!client->composer.get())
     return nullptr;
 
-  client->callback = new HwcCallback(callback, data);
+  client->callback = new HwcCallback(std::bind(callback, data,
+                                               std::placeholders::_1));
   android::binder::Status status = client->composer->registerObserver(
       client->callback);
   if (!status.isOk())
@@ -77,6 +100,10 @@
 }
 
 void dvrHwcClientDestroy(DvrHwcClient* client) {
+  // NOTE: Deleting DvrHwcClient* isn't enough since DvrHwcClient::callback is a
+  // shared pointer that could be referenced from a binder thread. But the
+  // client callback isn't valid past this calls so that needs to be reset.
+  client->callback->Shutdown();
   delete client;
 }
 
diff --git a/libs/vr/libdvr/dvr_performance.cpp b/libs/vr/libdvr/dvr_performance.cpp
new file mode 100644
index 0000000..599101f
--- /dev/null
+++ b/libs/vr/libdvr/dvr_performance.cpp
@@ -0,0 +1,18 @@
+#include "include/dvr/dvr_performance.h"
+
+#include <private/dvr/performance_client.h>
+
+using android::dvr::PerformanceClient;
+
+extern "C" {
+
+int dvrPerformanceSetSchedulerPolicy(pid_t task_id,
+                                     const char* scheduler_policy) {
+  int error;
+  if (auto client = PerformanceClient::Create(&error))
+    return client->SetSchedulerPolicy(task_id, scheduler_policy);
+  else
+    return error;
+}
+
+}  // extern "C"
diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
index 8602206..a3a47f1 100644
--- a/libs/vr/libdvr/dvr_surface.cpp
+++ b/libs/vr/libdvr/dvr_surface.cpp
@@ -2,6 +2,7 @@
 
 #include <inttypes.h>
 
+#include <pdx/rpc/variant.h>
 #include <private/android/AHardwareBufferHelpers.h>
 #include <private/dvr/display_client.h>
 
@@ -14,9 +15,20 @@
 using android::dvr::display::SurfaceAttributes;
 using android::dvr::display::SurfaceAttributeValue;
 using android::dvr::CreateDvrReadBufferFromBufferConsumer;
+using android::pdx::rpc::EmptyVariant;
 
 namespace {
 
+// Sets the Variant |destination| to the target std::array type and copies the C
+// array into it. Unsupported std::array configurations will fail to compile.
+template <typename T, std::size_t N>
+void ArrayCopy(SurfaceAttributeValue* destination, const T (&source)[N]) {
+  using ArrayType = std::array<T, N>;
+  *destination = ArrayType{};
+  std::copy(std::begin(source), std::end(source),
+            std::get<ArrayType>(*destination).begin());
+}
+
 bool ConvertSurfaceAttributes(const DvrSurfaceAttribute* attributes,
                               size_t attribute_count,
                               SurfaceAttributes* surface_attributes,
@@ -31,25 +43,31 @@
         value = attributes[i].value.int64_value;
         break;
       case DVR_SURFACE_ATTRIBUTE_TYPE_BOOL:
-        value = attributes[i].value.bool_value;
+        // bool_value is defined in an extern "C" block, which makes it look
+        // like an int to C++. Use a cast to assign the correct type to the
+        // Variant type SurfaceAttributeValue.
+        value = static_cast<bool>(attributes[i].value.bool_value);
         break;
       case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT:
         value = attributes[i].value.float_value;
         break;
       case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2:
-        value = attributes[i].value.float2_value;
+        ArrayCopy(&value, attributes[i].value.float2_value);
         break;
       case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3:
-        value = attributes[i].value.float3_value;
+        ArrayCopy(&value, attributes[i].value.float3_value);
         break;
       case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4:
-        value = attributes[i].value.float4_value;
+        ArrayCopy(&value, attributes[i].value.float4_value);
         break;
       case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8:
-        value = attributes[i].value.float8_value;
+        ArrayCopy(&value, attributes[i].value.float8_value);
         break;
       case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16:
-        value = attributes[i].value.float16_value;
+        ArrayCopy(&value, attributes[i].value.float16_value);
+        break;
+      case DVR_SURFACE_ATTRIBUTE_TYPE_NONE:
+        value = EmptyVariant{};
         break;
       default:
         *error_index = i;
@@ -223,4 +241,38 @@
   return 0;
 }
 
+int dvrGetNativeDisplayMetrics(size_t sizeof_metrics,
+                               DvrNativeDisplayMetrics* metrics) {
+  ALOGE_IF(sizeof_metrics != sizeof(DvrNativeDisplayMetrics),
+           "dvrGetNativeDisplayMetrics: metrics struct mismatch, your dvr api "
+           "header is out of date.");
+
+  auto client = DisplayClient::Create();
+  if (!client) {
+    ALOGE("dvrGetNativeDisplayMetrics: Failed to create display client!");
+    return -ECOMM;
+  }
+
+  if (metrics == nullptr) {
+    ALOGE("dvrGetNativeDisplayMetrics: output metrics buffer must be non-null");
+    return -EINVAL;
+  }
+
+  auto status = client->GetDisplayMetrics();
+
+  if (!status) {
+    return -status.error();
+  }
+
+  if (sizeof_metrics >= 20) {
+    metrics->display_width = status.get().display_width;
+    metrics->display_height = status.get().display_height;
+    metrics->display_x_dpi = status.get().display_x_dpi;
+    metrics->display_y_dpi = status.get().display_y_dpi;
+    metrics->vsync_period_ns = status.get().vsync_period_ns;
+  }
+
+  return 0;
+}
+
 }  // extern "C"
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index 5d5e2f0..ceb6cf2 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -4,6 +4,7 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <unistd.h>
 #include <cstdio>
 
 #include <dvr/dvr_display_types.h>
@@ -41,6 +42,19 @@
 typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue;
 typedef struct DvrSurfaceAttribute DvrSurfaceAttribute;
 
+// Note: To avoid breaking others during active development, only modify this
+// struct by appending elements to the end.
+// If you do feel we should to re-arrange or remove elements, please make a
+// note of it, and wait until we're about to finalize for an API release to do
+// so.
+typedef struct DvrNativeDisplayMetrics {
+  uint32_t display_width;
+  uint32_t display_height;
+  uint32_t display_x_dpi;
+  uint32_t display_y_dpi;
+  uint32_t vsync_period_ns;
+} DvrNativeDisplayMetrics;
+
 // native_handle contains the fds for the underlying ION allocations inside
 // the gralloc buffer. This is needed temporarily while GPU vendors work on
 // better support for AHardwareBuffer via glBindSharedBuffer APIs. See
@@ -200,6 +214,8 @@
     DvrSurface* surface, uint32_t width, uint32_t height, uint32_t format,
     uint32_t layer_count, uint64_t usage, size_t capacity, size_t metadata_size,
     DvrWriteBufferQueue** queue_out);
+typedef int (*DvrGetNativeDisplayMetricsPtr)(size_t sizeof_metrics,
+                                             DvrNativeDisplayMetrics* metrics);
 
 // dvr_vsync.h
 typedef int (*DvrVSyncClientCreatePtr)(DvrVSyncClient** client_out);
@@ -219,6 +235,8 @@
                                              int32_t controller_id,
                                              uint32_t vsync_count,
                                              DvrPoseAsync* out_pose);
+typedef int (*DvrPoseClientSensorsEnablePtr)(DvrPoseClient* client,
+                                             bool enabled);
 
 // services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
 
@@ -305,6 +323,10 @@
                                                            size_t layer_index,
                                                            size_t index);
 
+// dvr_performance.h
+typedef int (*DvrPerformanceSetSchedulerPolicyPtr)(
+    pid_t task_id, const char* scheduler_policy);
+
 // The buffer metadata that an Android Surface (a.k.a. ANativeWindow)
 // will populate. A DvrWriteBufferQueue must be created with this metadata iff
 // ANativeWindow access is needed. Please do not remove, modify, or reorder
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index a30a6c3..914901e 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -148,3 +148,12 @@
 
 // Virtual touchpad client
 DVR_V1_API_ENTRY(VirtualTouchpadScroll);
+
+// Read the native display metrics from the hardware composer
+DVR_V1_API_ENTRY(GetNativeDisplayMetrics);
+
+// Performance
+DVR_V1_API_ENTRY(PerformanceSetSchedulerPolicy);
+
+// Pose client
+DVR_V1_API_ENTRY(PoseClientSensorsEnable);
diff --git a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
index caf208d..95c04f1 100644
--- a/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
+++ b/libs/vr/libdvr/include/dvr/dvr_buffer_queue.h
@@ -124,6 +124,8 @@
 //     signals the release of underlying buffer. The consumer should wait until
 //     this fence clears before reading data from it.
 // @param out_meta The memory area where a metadata object will be filled.
+//     Can be nullptr iff |meta_size_bytes| is zero (i.e., there is no
+//     metadata).
 // @param meta_size_bytes Size of the metadata object caller expects. If it
 //     doesn't match the size of actually metadata transported by the buffer
 //     queue, the method returns -EINVAL.
diff --git a/libs/vr/libdvr/include/dvr/dvr_performance.h b/libs/vr/libdvr/include/dvr/dvr_performance.h
new file mode 100644
index 0000000..5df35ad
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_performance.h
@@ -0,0 +1,26 @@
+#ifndef ANDROID_DVR_PERFORMANCE_H_
+#define ANDROID_DVR_PERFORMANCE_H_
+
+#include <stddef.h>
+#include <unistd.h>
+
+__BEGIN_DECLS
+
+/// Sets the scheduler policy for a task.
+///
+/// Sets the scheduler policy for a task to the class described by a semantic
+/// string.
+///
+/// Supported policies are device-specific.
+///
+/// @param task_id The task id of task to set the policy for. When task_id is 0
+/// the current task id is substituted.
+/// @param scheduler_policy NULL-terminated ASCII string containing the desired
+/// scheduler policy.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrPerformanceSetSchedulerPolicy(pid_t task_id, const char* scheduler_policy);
+
+__END_DECLS
+
+#endif  // ANDROID_DVR_PERFORMANCE_H_
+
diff --git a/libs/vr/libdvr/include/dvr/dvr_pose.h b/libs/vr/libdvr/include/dvr/dvr_pose.h
index 4256cf9..b3df028 100644
--- a/libs/vr/libdvr/include/dvr/dvr_pose.h
+++ b/libs/vr/libdvr/include/dvr/dvr_pose.h
@@ -43,16 +43,24 @@
 } DvrPoseAsync;
 
 enum {
-  DVR_POSE_FLAG_INVALID = (1UL << 0),       // This pose is invalid.
-  DVR_POSE_FLAG_INITIALIZING = (1UL << 1),  // The pose delivered during
-                                            // initialization and it may not be
-                                            // correct.
+  DVR_POSE_FLAG_INVALID = (1ULL << 0),       // This pose is invalid.
+  DVR_POSE_FLAG_INITIALIZING = (1ULL << 1),  // The pose delivered during
+                                             // initialization and it may not be
+                                             // correct.
   DVR_POSE_FLAG_3DOF =
-      (1UL << 2),  // This pose is derived from 3Dof sensors. If
-                   // this is not set, pose is derived using
-                   // 3Dof and 6Dof sensors.
+      (1ULL << 2),  // This pose is derived from 3Dof sensors. If
+                    // this is not set, pose is derived using
+                    // 3Dof and 6Dof sensors.
   DVR_POSE_FLAG_FLOOR_HEIGHT_INVALID =
-      (1UL << 3),  // If set the floor height is invalid.
+      (1ULL << 3),  // If set the floor height is invalid.
+
+  // Bits that indicate the tracking system state.
+  DVR_POSE_FLAG_SERVICE_EXCEPTION = (1ULL << 32),
+  DVR_POSE_FLAG_FISHEYE_OVER_EXPOSED = (1ULL << 33),
+  DVR_POSE_FLAG_FISHEYE_UNDER_EXPOSED = (1ULL << 34),
+  DVR_POSE_FLAG_COLOR_OVER_EXPOSED = (1ULL << 35),
+  DVR_POSE_FLAG_COLOR_UNDER_EXPOSED = (1ULL << 36),
+  DVR_POSE_FLAG_TOO_FEW_FEATURES_TRACKED = (1ULL << 37)
 };
 
 // Represents a sensor pose sample.
diff --git a/libs/vr/libdvr/include/dvr/dvr_surface.h b/libs/vr/libdvr/include/dvr/dvr_surface.h
index 7b8dfa8..74a68a1 100644
--- a/libs/vr/libdvr/include/dvr/dvr_surface.h
+++ b/libs/vr/libdvr/include/dvr/dvr_surface.h
@@ -97,6 +97,17 @@
 // @return 0 on success. Otherwise returns a negative error value.
 int dvrGetGlobalBuffer(DvrGlobalBufferKey key, DvrBuffer** out_buffer);
 
+// Read the native device display metrics as reported by the hardware composer.
+// This is useful as otherwise the device metrics are only reported as
+// relative to the current device orientation.
+// @param sizeof_metrics the size of the passed in metrics struct. This is used
+//   to ensure we don't break each other during active development.
+// @param metrics on success holds the retrieved device metrics.
+// @return 0 on success. Otherwise returns a negative error value (typically
+//   this means the display service is not available).
+int dvrGetNativeDisplayMetrics(size_t metrics_struct_size,
+                               DvrNativeDisplayMetrics* metrics);
+
 __END_DECLS
 
 #endif  // ANDROID_DVR_SURFACE_H_
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
index 5158612..497b1cb 100644
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -37,15 +37,11 @@
 
  protected:
   void SetUp() override {
-    auto config = ProducerQueueConfigBuilder()
-                      .SetDefaultWidth(kBufferWidth)
-                      .SetDefaultHeight(kBufferHeight)
-                      .SetDefaultFormat(kBufferFormat)
-                      .SetMetadata<TestMeta>()
-                      .Build();
-    write_queue_ =
-        new DvrWriteBufferQueue(ProducerQueue::Create(config, UsagePolicy{}));
-    ASSERT_NE(nullptr, write_queue_);
+    config_builder_ = ProducerQueueConfigBuilder()
+                          .SetDefaultWidth(kBufferWidth)
+                          .SetDefaultHeight(kBufferHeight)
+                          .SetDefaultFormat(kBufferFormat)
+                          .SetMetadata<TestMeta>();
   }
 
   void TearDown() override {
@@ -55,6 +51,12 @@
     }
   }
 
+  void CreateWriteBufferQueue() {
+    write_queue_ = new DvrWriteBufferQueue(
+        ProducerQueue::Create(config_builder_.Build(), UsagePolicy{}));
+    ASSERT_NE(nullptr, write_queue_);
+  }
+
   void AllocateBuffers(size_t buffer_count) {
     auto status = write_queue_->producer_queue()->AllocateBuffers(
         kBufferWidth, kBufferHeight, kLayerCount, kBufferFormat, kBufferUsage,
@@ -73,18 +75,23 @@
              buffer_removed_count_);
   }
 
+  ProducerQueueConfigBuilder config_builder_;
   DvrWriteBufferQueue* write_queue_{nullptr};
   int buffer_available_count_{0};
   int buffer_removed_count_{0};
 };
 
-TEST_F(DvrBufferQueueTest, TestWrite_QueueDestroy) {
+TEST_F(DvrBufferQueueTest, TestWrite_QueueCreateDestroy) {
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+
   dvrWriteBufferQueueDestroy(write_queue_);
   write_queue_ = nullptr;
 }
 
 TEST_F(DvrBufferQueueTest, TestWrite_QueueGetCapacity) {
-  AllocateBuffers(kQueueCapacity);
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity));
+
   size_t capacity = dvrWriteBufferQueueGetCapacity(write_queue_);
 
   ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity);
@@ -92,6 +99,8 @@
 }
 
 TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromWriteQueue) {
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+
   DvrReadBufferQueue* read_queue = nullptr;
   int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
 
@@ -102,6 +111,8 @@
 }
 
 TEST_F(DvrBufferQueueTest, TestCreateReadQueueFromReadQueue) {
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+
   DvrReadBufferQueue* read_queue1 = nullptr;
   DvrReadBufferQueue* read_queue2 = nullptr;
   int ret = dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue1);
@@ -119,7 +130,8 @@
 }
 
 TEST_F(DvrBufferQueueTest, CreateEmptyBuffer) {
-  AllocateBuffers(3);
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(3));
 
   DvrReadBuffer* read_buffer = nullptr;
   DvrWriteBuffer* write_buffer = nullptr;
@@ -152,6 +164,9 @@
 }
 
 TEST_F(DvrBufferQueueTest, TestDequeuePostDequeueRelease) {
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity));
+
   static constexpr int kTimeout = 0;
   DvrReadBufferQueue* read_queue = nullptr;
   DvrReadBuffer* rb = nullptr;
@@ -172,8 +187,6 @@
   dvrReadBufferCreateEmpty(&rb);
   ASSERT_NE(nullptr, rb);
 
-  AllocateBuffers(kQueueCapacity);
-
   // Gain buffer for writing.
   ret = dvrWriteBufferQueueDequeue(write_queue_, kTimeout, wb, &fence_fd);
   ASSERT_EQ(0, ret);
@@ -221,6 +234,8 @@
 }
 
 TEST_F(DvrBufferQueueTest, TestGetExternalSurface) {
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+
   ANativeWindow* window = nullptr;
 
   // The |write_queue_| doesn't have proper metadata (must be
@@ -251,6 +266,9 @@
 // Before each dequeue operation, we resize the buffer queue and expect the
 // queue always return buffer with desired dimension.
 TEST_F(DvrBufferQueueTest, TestResizeBuffer) {
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(kQueueCapacity));
+
   static constexpr int kTimeout = 0;
   int fence_fd = -1;
 
@@ -278,8 +296,6 @@
   dvrWriteBufferCreateEmpty(&wb3);
   ASSERT_NE(nullptr, wb3);
 
-  AllocateBuffers(kQueueCapacity);
-
   // Handle all pending events on the read queue.
   ret = dvrReadBufferQueueHandleEvents(read_queue);
   ASSERT_EQ(0, ret);
@@ -369,6 +385,71 @@
   dvrReadBufferQueueDestroy(read_queue);
 }
 
+TEST_F(DvrBufferQueueTest, DequeueEmptyMetadata) {
+  // Overrides default queue parameters: Empty metadata.
+  config_builder_.SetMetadata<void>();
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(1));
+
+  DvrReadBuffer* rb = nullptr;
+  DvrWriteBuffer* wb = nullptr;
+  dvrReadBufferCreateEmpty(&rb);
+  dvrWriteBufferCreateEmpty(&wb);
+
+  DvrReadBufferQueue* read_queue = nullptr;
+  EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
+
+  const int kTimeoutMs = 0;
+  int fence_fd = -1;
+  EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd));
+
+  EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, nullptr, 0));
+  EXPECT_EQ(0, dvrWriteBufferClear(wb));
+  dvrWriteBufferDestroy(wb);
+  wb = nullptr;
+
+  // When acquire buffer, it's legit to pass nullptr as out_meta iff metadata
+  // size is Zero.
+  EXPECT_EQ(0, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd,
+                                         nullptr, 0));
+  EXPECT_TRUE(dvrReadBufferIsValid(rb));
+}
+
+TEST_F(DvrBufferQueueTest, DequeueMismatchMetadata) {
+  ASSERT_NO_FATAL_FAILURE(CreateWriteBufferQueue());
+  ASSERT_NO_FATAL_FAILURE(AllocateBuffers(1));
+
+  DvrReadBuffer* rb = nullptr;
+  DvrWriteBuffer* wb = nullptr;
+  dvrReadBufferCreateEmpty(&rb);
+  dvrWriteBufferCreateEmpty(&wb);
+
+  DvrReadBufferQueue* read_queue = nullptr;
+  EXPECT_EQ(0, dvrWriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
+
+  const int kTimeoutMs = 0;
+  int fence_fd = -1;
+  EXPECT_EQ(0, dvrWriteBufferQueueDequeue(write_queue_, 0, wb, &fence_fd));
+
+  TestMeta seq = 42U;
+  EXPECT_EQ(0, dvrWriteBufferPost(wb, /*fence=*/-1, &seq, sizeof(seq)));
+  EXPECT_EQ(0, dvrWriteBufferClear(wb));
+  dvrWriteBufferDestroy(wb);
+  wb = nullptr;
+
+  // Dequeue with wrong metadata will cause EINVAL.
+  int8_t wrong_metadata;
+  EXPECT_EQ(-EINVAL,
+            dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb, &fence_fd,
+                                      &wrong_metadata, sizeof(wrong_metadata)));
+  EXPECT_FALSE(dvrReadBufferIsValid(rb));
+
+  // Dequeue with empty metadata will cause EINVAL.
+  EXPECT_EQ(-EINVAL, dvrReadBufferQueueDequeue(read_queue, kTimeoutMs, rb,
+                                               &fence_fd, nullptr, 0));
+  EXPECT_FALSE(dvrReadBufferIsValid(rb));
+}
+
 }  // namespace
 
 }  // namespace dvr
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
index 726949d..f83b26e 100644
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -1,11 +1,13 @@
 #include <android-base/properties.h>
 #include <base/logging.h>
 #include <gtest/gtest.h>
+#include <log/log.h>
 #include <poll.h>
 
 #include <android/hardware_buffer.h>
 
 #include <algorithm>
+#include <array>
 #include <set>
 #include <thread>
 #include <vector>
@@ -25,7 +27,30 @@
 
 namespace {
 
-DvrSurfaceAttribute GetAttribute(DvrSurfaceAttributeKey key, bool value) {
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
+  return attribute;
+}
+
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int32_t value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
+  attribute.value.int32_value = value;
+  return attribute;
+}
+
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int64_t value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64;
+  attribute.value.int64_value = value;
+  return attribute;
+}
+
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, bool value) {
   DvrSurfaceAttribute attribute;
   attribute.key = key;
   attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
@@ -33,11 +58,56 @@
   return attribute;
 }
 
-DvrSurfaceAttribute GetAttribute(DvrSurfaceAttributeKey key, int32_t value) {
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, float value) {
   DvrSurfaceAttribute attribute;
   attribute.key = key;
-  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
-  attribute.value.bool_value = value;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT;
+  attribute.value.float_value = value;
+  return attribute;
+}
+
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
+                                  const std::array<float, 2>& value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2;
+  std::copy(value.begin(), value.end(), attribute.value.float2_value);
+  return attribute;
+}
+
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
+                                  const std::array<float, 3>& value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3;
+  std::copy(value.begin(), value.end(), attribute.value.float3_value);
+  return attribute;
+}
+
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
+                                  const std::array<float, 4>& value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4;
+  std::copy(value.begin(), value.end(), attribute.value.float4_value);
+  return attribute;
+}
+
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
+                                  const std::array<float, 8>& value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8;
+  std::copy(value.begin(), value.end(), attribute.value.float8_value);
+  return attribute;
+}
+
+DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
+                                  const std::array<float, 16>& value) {
+  DvrSurfaceAttribute attribute;
+  attribute.key = key;
+  attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16;
+  std::copy(value.begin(), value.end(), attribute.value.float16_value);
   return attribute;
 }
 
@@ -45,8 +115,8 @@
                                                   int32_t z_order = 0) {
   DvrSurface* surface = nullptr;
   DvrSurfaceAttribute attributes[] = {
-      GetAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order),
-      GetAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)};
+      MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order),
+      MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)};
 
   const int ret = dvrSurfaceCreate(
       attributes, std::extent<decltype(attributes)>::value, &surface);
@@ -101,13 +171,14 @@
       return {};
   }
 
-  Status<void> WaitForUpdate() {
+  enum : int { kTimeoutMs = 10000 };  // 10s
+
+  Status<void> WaitForUpdate(int timeout_ms = kTimeoutMs) {
     if (display_manager_event_fd_ < 0)
       return ErrorStatus(-display_manager_event_fd_);
 
-    const int kTimeoutMs = 10000;  // 10s
     pollfd pfd = {display_manager_event_fd_, POLLIN, 0};
-    const int count = poll(&pfd, 1, kTimeoutMs);
+    const int count = poll(&pfd, 1, timeout_ms);
     if (count < 0)
       return ErrorStatus(errno);
     else if (count == 0)
@@ -471,20 +542,218 @@
   auto attributes = attribute_status.take();
   EXPECT_GE(attributes.size(), 2u);
 
-  const std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
-                                           DVR_SURFACE_ATTRIBUTE_VISIBLE};
+  std::set<int32_t> actual_keys;
+  std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
+                                     DVR_SURFACE_ATTRIBUTE_VISIBLE};
 
   // Collect all the keys in attributes that match the expected keys.
-  std::set<int32_t> actual_keys;
-  std::for_each(attributes.begin(), attributes.end(),
-                [&expected_keys, &actual_keys](const auto& attribute) {
-                  if (expected_keys.find(attribute.key) != expected_keys.end())
-                    actual_keys.emplace(attribute.key);
-                });
+  auto compare_keys = [](const auto& attributes, const auto& expected_keys) {
+    std::set<int32_t> keys;
+    for (const auto& attribute : attributes) {
+      if (expected_keys.find(attribute.key) != expected_keys.end())
+        keys.emplace(attribute.key);
+    }
+    return keys;
+  };
 
   // If the sets match then attributes contained at least the expected keys,
   // even if other keys were also present.
+  actual_keys = compare_keys(attributes, expected_keys);
   EXPECT_EQ(expected_keys, actual_keys);
+
+  std::vector<DvrSurfaceAttribute> attributes_to_set = {
+      MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, 0)};
+
+  // Test invalid args.
+  EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(nullptr, attributes_to_set.data(),
+                                             attributes_to_set.size()));
+  EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(surface.get(), nullptr,
+                                             attributes_to_set.size()));
+
+  // Test attribute change events.
+  ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
+                                       attributes_to_set.size()));
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+
+  // Verify the attributes changed flag is set.
+  auto check_flags = [](const auto& value) {
+    return value & DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED;
+  };
+  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+  attribute_status = manager_->GetAttributes(0);
+  ASSERT_STATUS_OK(attribute_status);
+  attributes = attribute_status.take();
+  EXPECT_GE(attributes.size(), 2u);
+
+  expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
+                   DVR_SURFACE_ATTRIBUTE_VISIBLE};
+
+  actual_keys.clear();
+  actual_keys = compare_keys(attributes, expected_keys);
+  EXPECT_EQ(expected_keys, actual_keys);
+
+  // Test setting and then deleting an attribute.
+  const DvrSurfaceAttributeKey kUserKey = 1;
+  attributes_to_set = {MakeAttribute(kUserKey, 1024)};
+
+  ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
+                                       attributes_to_set.size()));
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+  attribute_status = manager_->GetAttributes(0);
+  ASSERT_STATUS_OK(attribute_status);
+  attributes = attribute_status.take();
+  EXPECT_GE(attributes.size(), 2u);
+
+  expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE,
+                   kUserKey};
+
+  actual_keys.clear();
+  actual_keys = compare_keys(attributes, expected_keys);
+  EXPECT_EQ(expected_keys, actual_keys);
+
+  // Delete the attribute.
+  attributes_to_set = {MakeAttribute(kUserKey, nullptr)};
+
+  ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
+                                       attributes_to_set.size()));
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
+
+  attribute_status = manager_->GetAttributes(0);
+  ASSERT_STATUS_OK(attribute_status);
+  attributes = attribute_status.take();
+  EXPECT_GE(attributes.size(), 2u);
+
+  expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE,
+                   kUserKey};
+
+  actual_keys.clear();
+  actual_keys = compare_keys(attributes, expected_keys);
+  EXPECT_NE(expected_keys, actual_keys);
+
+  // Test deleting a reserved attribute.
+  attributes_to_set = {MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, nullptr)};
+
+  EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
+                                       attributes_to_set.size()));
+
+  // Failed attribute operations should not trigger update events.
+  const int kTimeoutMs = 100;  // 0.1s
+  EXPECT_STATUS_ERROR_VALUE(ETIMEDOUT, manager_->WaitForUpdate(kTimeoutMs));
+
+  attribute_status = manager_->GetAttributes(0);
+  ASSERT_STATUS_OK(attribute_status);
+  attributes = attribute_status.take();
+  EXPECT_GE(attributes.size(), 2u);
+
+  expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
+                   DVR_SURFACE_ATTRIBUTE_VISIBLE};
+
+  actual_keys.clear();
+  actual_keys = compare_keys(attributes, expected_keys);
+  EXPECT_EQ(expected_keys, actual_keys);
+}
+
+TEST_F(DvrDisplayManagerTest, SurfaceAttributeTypes) {
+  // Create an application surface.
+  auto surface_status = CreateApplicationSurface();
+  ASSERT_STATUS_OK(surface_status);
+  UniqueDvrSurface surface = surface_status.take();
+  ASSERT_NE(nullptr, surface.get());
+
+  enum : std::int32_t {
+    kInt32Key = 1,
+    kInt64Key,
+    kBoolKey,
+    kFloatKey,
+    kFloat2Key,
+    kFloat3Key,
+    kFloat4Key,
+    kFloat8Key,
+    kFloat16Key,
+  };
+
+  const std::vector<DvrSurfaceAttribute> attributes_to_set = {
+      MakeAttribute(kInt32Key, int32_t{0}),
+      MakeAttribute(kInt64Key, int64_t{0}),
+      MakeAttribute(kBoolKey, false),
+      MakeAttribute(kFloatKey, 0.0f),
+      MakeAttribute(kFloat2Key, std::array<float, 2>{{1.0f, 2.0f}}),
+      MakeAttribute(kFloat3Key, std::array<float, 3>{{3.0f, 4.0f, 5.0f}}),
+      MakeAttribute(kFloat4Key, std::array<float, 4>{{6.0f, 7.0f, 8.0f, 9.0f}}),
+      MakeAttribute(kFloat8Key,
+                    std::array<float, 8>{{10.0f, 11.0f, 12.0f, 13.0f, 14.0f,
+                                          15.0f, 16.0f, 17.0f}}),
+      MakeAttribute(kFloat16Key, std::array<float, 16>{
+                                     {18.0f, 19.0f, 20.0f, 21.0f, 22.0f, 23.0f,
+                                      24.0f, 25.0f, 26.0f, 27.0f, 28.0f, 29.0f,
+                                      30.0f, 31.0f, 32.0f, 33.0f}})};
+
+  EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
+                                       attributes_to_set.size()));
+
+  ASSERT_STATUS_OK(manager_->WaitForUpdate());
+  auto attribute_status = manager_->GetAttributes(0);
+  ASSERT_STATUS_OK(attribute_status);
+  auto attributes = attribute_status.take();
+  EXPECT_GE(attributes.size(), attributes_to_set.size());
+
+  auto HasAttribute = [](const auto& attributes,
+                         DvrSurfaceAttributeKey key) -> bool {
+    for (const auto& attribute : attributes) {
+      if (attribute.key == key)
+        return true;
+    }
+    return false;
+  };
+  auto AttributeType =
+      [](const auto& attributes,
+         DvrSurfaceAttributeKey key) -> DvrSurfaceAttributeType {
+    for (const auto& attribute : attributes) {
+      if (attribute.key == key)
+        return attribute.value.type;
+    }
+    return DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
+  };
+
+  ASSERT_TRUE(HasAttribute(attributes, kInt32Key));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
+            AttributeType(attributes, kInt32Key));
+
+  ASSERT_TRUE(HasAttribute(attributes, kInt64Key));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT64,
+            AttributeType(attributes, kInt64Key));
+
+  ASSERT_TRUE(HasAttribute(attributes, kBoolKey));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
+            AttributeType(attributes, kBoolKey));
+
+  ASSERT_TRUE(HasAttribute(attributes, kFloatKey));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT,
+            AttributeType(attributes, kFloatKey));
+
+  ASSERT_TRUE(HasAttribute(attributes, kFloat2Key));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2,
+            AttributeType(attributes, kFloat2Key));
+
+  ASSERT_TRUE(HasAttribute(attributes, kFloat3Key));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3,
+            AttributeType(attributes, kFloat3Key));
+
+  ASSERT_TRUE(HasAttribute(attributes, kFloat4Key));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4,
+            AttributeType(attributes, kFloat4Key));
+
+  ASSERT_TRUE(HasAttribute(attributes, kFloat8Key));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8,
+            AttributeType(attributes, kFloat8Key));
+
+  ASSERT_TRUE(HasAttribute(attributes, kFloat16Key));
+  EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16,
+            AttributeType(attributes, kFloat16Key));
 }
 
 TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
@@ -500,7 +769,8 @@
   ASSERT_STATUS_OK(manager_->WaitForUpdate());
   ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
 
-  // Verify there are no queues for the surface recorded in the state snapshot.
+  // Verify there are no queues for the surface recorded in the state
+  // snapshot.
   EXPECT_STATUS_EQ(std::vector<int>{}, manager_->GetQueueIds(0));
 
   // Create a new queue in the surface.
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
index dc321fa..80b1769 100644
--- a/libs/vr/libpdx/private/pdx/rpc/variant.h
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -26,14 +26,35 @@
 template <std::size_t I, typename... Types>
 using TypeTagForIndex = TypeTag<TypeForIndex<I, Types...>>;
 
+// Similar to std::is_constructible except that it evaluates to false for bool
+// construction from pointer types: this helps prevent subtle to bugs caused by
+// assigning values that decay to pointers to Variants with bool elements.
+//
+// Here is an example of the problematic situation this trait avoids:
+//
+//  Variant<int, bool> v;
+//  const int array[3] = {1, 2, 3};
+//  v = array; // This is allowed by regular std::is_constructible.
+//
+template <typename...>
+struct IsConstructible;
+template <typename T, typename U>
+struct IsConstructible<T, U>
+    : std::integral_constant<bool,
+                             std::is_constructible<T, U>::value &&
+                                 !(std::is_same<std::decay_t<T>, bool>::value &&
+                                   std::is_pointer<std::decay_t<U>>::value)> {};
+template <typename T, typename... Args>
+struct IsConstructible<T, Args...> : std::is_constructible<T, Args...> {};
+
 // 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;
+    typename std::enable_if<IsConstructible<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;
+    typename std::enable_if<!IsConstructible<T, Args...>::value, R>::type;
 
 // Determines whether T is an element of Types...;
 template <typename... Types>
@@ -65,12 +86,11 @@
 struct ConstructibleCount;
 template <typename From, typename To>
 struct ConstructibleCount<From, To>
-    : std::integral_constant<std::size_t,
-                             std::is_constructible<To, From>::value> {};
+    : std::integral_constant<std::size_t, IsConstructible<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 +
+                             IsConstructible<First, From>::value +
                                  ConstructibleCount<From, Rest...>::value> {};
 
 // Enable if T is an element of Types...
@@ -126,6 +146,18 @@
       : first_(std::forward<T>(value)) {
     *index_out = index;
   }
+  Union(const Union& other, std::int32_t index) {
+    if (index == 0)
+      new (&first_) Type(other.first_);
+  }
+  Union(Union&& other, std::int32_t index) {
+    if (index == 0)
+      new (&first_) Type(std::move(other.first_));
+  }
+  Union(const Union&) = delete;
+  Union(Union&&) = delete;
+  void operator=(const Union&) = delete;
+  void operator=(Union&&) = delete;
 
   Type& get(TypeTag<Type>) { return first_; }
   const Type& get(TypeTag<Type>) const { return first_; }
@@ -217,6 +249,22 @@
   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)) {}
+  Union(const Union& other, std::int32_t index) {
+    if (index == 0)
+      new (&first_) First(other.first_);
+    else
+      new (&rest_) Union<Rest...>(other.rest_, index - 1);
+  }
+  Union(Union&& other, std::int32_t index) {
+    if (index == 0)
+      new (&first_) First(std::move(other.first_));
+    else
+      new (&rest_) Union<Rest...>(std::move(other.rest_), index - 1);
+  }
+  Union(const Union&) = delete;
+  Union(Union&&) = delete;
+  void operator=(const Union&) = delete;
+  void operator=(Union&&) = delete;
 
   struct FirstType {};
   struct RestType {};
@@ -351,6 +399,10 @@
 
 }  // namespace detail
 
+// Variant is a type safe union that can store values of any of its element
+// types. A Variant is different than std::tuple in that it only stores one type
+// at a time or a special empty type. Variants are always default constructible
+// to empty, even when none of the element types are default constructible.
 template <typename... Types>
 class Variant {
  private:
@@ -393,6 +445,11 @@
   explicit Variant(EmptyVariant) {}
   ~Variant() { Destruct(); }
 
+  Variant(const Variant& other)
+      : index_{other.index_}, value_{other.value_, other.index_} {}
+  Variant(Variant&& other)
+      : index_{other.index_}, value_{std::move(other.value_), other.index_} {}
+
   // Copy and move construction from Variant types. Each element of OtherTypes
   // must be convertible to an element of Types.
   template <typename... OtherTypes>
@@ -404,6 +461,15 @@
     other.Visit([this](auto&& value) { Construct(std::move(value)); });
   }
 
+  Variant& operator=(const Variant& other) {
+    other.Visit([this](const auto& value) { *this = value; });
+    return *this;
+  }
+  Variant& operator=(Variant&& other) {
+    other.Visit([this](auto&& value) { *this = std::move(value); });
+    return *this;
+  }
+
   // Construction from non-Variant types.
   template <typename T, typename = EnableIfAssignable<void, T>>
   explicit Variant(T&& value)
diff --git a/libs/vr/libpdx/variant_tests.cpp b/libs/vr/libpdx/variant_tests.cpp
index b1ebc9b..e3520f5 100644
--- a/libs/vr/libpdx/variant_tests.cpp
+++ b/libs/vr/libpdx/variant_tests.cpp
@@ -1,3 +1,4 @@
+#include <array>
 #include <cstdint>
 #include <functional>
 #include <memory>
@@ -1097,6 +1098,29 @@
   EXPECT_FALSE((detail::HasType<char&, int, float, bool>::value));
 }
 
+TEST(Variant, IsConstructible) {
+  using ArrayType = const float[3];
+  struct ImplicitBool {
+    operator bool() const { return true; }
+  };
+  struct ExplicitBool {
+    explicit operator bool() const { return true; }
+  };
+  struct NonBool {};
+  struct TwoArgs {
+    TwoArgs(int, bool) {}
+  };
+
+  EXPECT_FALSE((detail::IsConstructible<bool, ArrayType>::value));
+  EXPECT_TRUE((detail::IsConstructible<bool, int>::value));
+  EXPECT_TRUE((detail::IsConstructible<bool, ImplicitBool>::value));
+  EXPECT_TRUE((detail::IsConstructible<bool, ExplicitBool>::value));
+  EXPECT_FALSE((detail::IsConstructible<bool, NonBool>::value));
+  EXPECT_TRUE((detail::IsConstructible<TwoArgs, int, bool>::value));
+  EXPECT_FALSE((detail::IsConstructible<TwoArgs, int, std::string>::value));
+  EXPECT_FALSE((detail::IsConstructible<TwoArgs, int>::value));
+}
+
 TEST(Variant, Set) {
   EXPECT_TRUE((detail::Set<int, bool, float>::template IsSubset<int, bool,
                                                                 float>::value));
diff --git a/libs/vr/libpdx_uds/channel_event_set.cpp b/libs/vr/libpdx_uds/channel_event_set.cpp
index ac4dea9..ebe7cea 100644
--- a/libs/vr/libpdx_uds/channel_event_set.cpp
+++ b/libs/vr/libpdx_uds/channel_event_set.cpp
@@ -100,6 +100,9 @@
     ALOGE("ChannelEventReceiver::GetPendingEvents: Failed to get events: %s",
           status.GetErrorMessage().c_str());
     return status;
+  } else if (count == 0) {
+    status.SetError(ETIMEDOUT);
+    return status;
   }
 
   const int mask_out = event.data.u32;
diff --git a/libs/vr/libperformance/include/dvr/performance_client_api.h b/libs/vr/libperformance/include/dvr/performance_client_api.h
index 2216e38..9d617cb 100644
--- a/libs/vr/libperformance/include/dvr/performance_client_api.h
+++ b/libs/vr/libperformance/include/dvr/performance_client_api.h
@@ -8,6 +8,20 @@
 extern "C" {
 #endif
 
+/// Sets the scheduler policy for a task.
+///
+/// Sets the scheduler policy for a task to the class described by a semantic
+/// string.
+///
+/// Supported policies are device-specific.
+///
+/// @param task_id The task id of task to set the policy for. When task_id is 0
+/// the current task id is substituted.
+/// @param scheduler_policy NULL-terminated ASCII string containing the desired
+/// scheduler policy.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrSetSchedulerPolicy(pid_t task_id, const char* scheduler_policy);
+
 /// Sets the CPU partition for a task.
 ///
 /// Sets the CPU partition for a task to the partition described by a CPU
diff --git a/libs/vr/libperformance/include/private/dvr/performance_client.h b/libs/vr/libperformance/include/private/dvr/performance_client.h
index a61c6b2..3bd90dc 100644
--- a/libs/vr/libperformance/include/private/dvr/performance_client.h
+++ b/libs/vr/libperformance/include/private/dvr/performance_client.h
@@ -14,6 +14,10 @@
 
 class PerformanceClient : public pdx::ClientBase<PerformanceClient> {
  public:
+  int SetSchedulerPolicy(pid_t task_id, const std::string& scheduler_policy);
+  int SetSchedulerPolicy(pid_t task_id, const char* scheduler_policy);
+
+  // TODO(eieio): Consider deprecating this API.
   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);
diff --git a/libs/vr/libperformance/include/private/dvr/performance_rpc.h b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
index 73bdaa7..d57bbe8 100644
--- a/libs/vr/libperformance/include/private/dvr/performance_rpc.h
+++ b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
@@ -21,14 +21,17 @@
     kOpSetCpuPartition = 0,
     kOpSetSchedulerClass,
     kOpGetCpuPartition,
+    kOpSetSchedulerPolicy,
   };
 
   // Methods.
   PDX_REMOTE_METHOD(SetCpuPartition, kOpSetCpuPartition,
-                    int(pid_t, const std::string&));
+                    void(pid_t, const std::string&));
   PDX_REMOTE_METHOD(SetSchedulerClass, kOpSetSchedulerClass,
-                    int(pid_t, const std::string&));
+                    void(pid_t, const std::string&));
   PDX_REMOTE_METHOD(GetCpuPartition, kOpGetCpuPartition, std::string(pid_t));
+  PDX_REMOTE_METHOD(SetSchedulerPolicy, kOpSetSchedulerPolicy,
+                    void(pid_t, const std::string&));
 };
 
 }  // namespace dvr
diff --git a/libs/vr/libperformance/performance_client.cpp b/libs/vr/libperformance/performance_client.cpp
index 2124162..bf3726e 100644
--- a/libs/vr/libperformance/performance_client.cpp
+++ b/libs/vr/libperformance/performance_client.cpp
@@ -37,6 +37,26 @@
           task_id, WrapString(partition)));
 }
 
+int PerformanceClient::SetSchedulerPolicy(pid_t task_id,
+                                          const std::string& scheduler_policy) {
+  if (task_id == 0)
+    task_id = gettid();
+
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<PerformanceRPC::SetSchedulerPolicy>(task_id,
+                                                             scheduler_policy));
+}
+
+int PerformanceClient::SetSchedulerPolicy(pid_t task_id,
+                                          const char* scheduler_policy) {
+  if (task_id == 0)
+    task_id = gettid();
+
+  return ReturnStatusOrError(
+      InvokeRemoteMethod<PerformanceRPC::SetSchedulerPolicy>(
+          task_id, WrapString(scheduler_policy)));
+}
+
 int PerformanceClient::SetSchedulerClass(pid_t task_id,
                                          const std::string& scheduler_class) {
   if (task_id == 0)
@@ -101,6 +121,15 @@
     return error;
 }
 
+extern "C" int dvrSetSchedulerPolicy(pid_t task_id,
+                                     const char* scheduler_policy) {
+  int error;
+  if (auto client = android::dvr::PerformanceClient::Create(&error))
+    return client->SetSchedulerPolicy(task_id, scheduler_policy);
+  else
+    return error;
+}
+
 extern "C" int dvrSetSchedulerClass(pid_t task_id,
                                     const char* scheduler_class) {
   int error;
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
index 330b455..6e18781 100644
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ b/libs/vr/libvrflinger/display_surface.cpp
@@ -68,7 +68,7 @@
   display::SurfaceUpdateFlags update_flags;
 
   for (const auto& attribute : attributes) {
-    const auto& key = attribute.first;
+    const auto key = attribute.first;
     const auto* variant = &attribute.second;
     bool invalid_value = false;
     bool visibility_changed = false;
@@ -95,14 +95,23 @@
         break;
     }
 
+    // Only update the attribute map with valid values. This check also has the
+    // effect of preventing special attributes handled above from being deleted
+    // by an empty value.
     if (invalid_value) {
       ALOGW(
           "DisplaySurface::OnClientSetAttributes: Failed to set display "
           "surface attribute '%d' because of incompatible type: %d",
           key, variant->index());
     } else {
-      // Only update the attribute map with valid values.
-      attributes_[attribute.first] = attribute.second;
+      // An empty value indicates the attribute should be deleted.
+      if (variant->empty()) {
+        auto search = attributes_.find(key);
+        if (search != attributes_.end())
+          attributes_.erase(search);
+      } else {
+        attributes_[key] = *variant;
+      }
 
       // All attribute changes generate a notification, even if the value
       // doesn't change. Visibility attributes set a flag only if the value
diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
index 145852e..f41da87 100644
--- a/libs/vr/libvrflinger/include/dvr/vr_flinger.h
+++ b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
@@ -32,6 +32,9 @@
   // Called on a binder thread.
   void OnHardwareComposerRefresh();
 
+  // dump all vr flinger state.
+  std::string Dump();
+
  private:
   VrFlinger();
   bool Init(Hwc2::Composer* hidl,
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index b2dc1d8..3a0ca4a 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -139,6 +139,11 @@
   display_service_->OnHardwareComposerRefresh();
 }
 
+std::string VrFlinger::Dump() {
+  // TODO(karthikrs): Add more state information here.
+  return display_service_->DumpState(0/*unused*/);
+}
+
 void VrFlinger::PersistentVrStateCallback::onPersistentVrStateChanged(
     bool enabled) {
   ALOGV("Notified persistent vr mode is %s", enabled ? "on" : "off");
@@ -146,6 +151,5 @@
   // Persistent VR mode is not enough.
   // request_display_callback_(enabled);
 }
-
 }  // namespace dvr
 }  // namespace android
diff --git a/opengl/Android.bp b/opengl/Android.bp
index c520bda..aec5a95 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -52,6 +52,12 @@
     license: "include/KHR/NOTICE",
 }
 
+cc_library_headers {
+    name: "gl_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
+
 subdirs = [
     "*",
 ]
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 4e275db..a344636 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -64,6 +64,16 @@
         "liblog",
         "libdl",
     ],
+    static_libs: [
+        "libarect",
+    ],
+    header_libs: [
+        "gl_headers",
+        "libsystem_headers",
+        "libhardware_headers",
+        "libnativebase_headers",
+    ],
+    export_header_lib_headers: ["gl_headers"],
 
     // we need to access the private Bionic header <bionic_tls.h>
     include_dirs: ["bionic/libc/private"],
@@ -75,6 +85,7 @@
 cc_defaults {
     name: "egl_libs_defaults",
     defaults: ["gl_libs_defaults"],
+    vendor_available: true,
     cflags: [
         "-DLOG_TAG=\"libEGL\"",
     ],
@@ -85,6 +96,11 @@
         "libnativewindow",
         "libbacktrace",
     ],
+    target: {
+        vendor: {
+            exclude_shared_libs: ["libgraphicsenv"],
+        },
+    },
 }
 
 cc_library_static {
@@ -129,6 +145,7 @@
 cc_defaults {
     name: "gles_libs_defaults",
     defaults: ["gl_libs_defaults"],
+    vendor_available: true,
     arch: {
         arm: {
             instruction_set: "arm",
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 6e5c510..32f8caa 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -28,7 +28,9 @@
 #include <cutils/properties.h>
 #include <log/log.h>
 
+#ifndef __ANDROID_VNDK__
 #include <graphicsenv/GraphicsEnv.h>
+#endif
 #include <vndksupport/linker.h>
 
 #include "egl_trace.h"
@@ -477,10 +479,12 @@
     ATRACE_CALL();
 
     void* dso = nullptr;
+#ifndef __ANDROID_VNDK__
     android_namespace_t* ns = android_getDriverNamespace();
     if (ns) {
         dso = load_updated_driver(kind, ns);
     }
+#endif
     if (!dso) {
         dso = load_system_driver(kind);
         if (!dso)
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 835e72b..c5feb89 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -457,8 +457,8 @@
 // EGL_GL_COLORSPACE_SRGB_KHR, or turn sRGB formats into corresponding linear
 // formats when colorspace is EGL_GL_COLORSPACE_LINEAR_KHR. In any cases where
 // the modification isn't possible, the original dataSpace is returned.
-static android_dataspace modifyBufferDataspace( android_dataspace dataSpace,
-                                                EGLint colorspace) {
+static android_dataspace modifyBufferDataspace(android_dataspace dataSpace,
+                                               EGLint colorspace) {
     if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
         return HAL_DATASPACE_SRGB_LINEAR;
     } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
@@ -473,6 +473,59 @@
     return dataSpace;
 }
 
+// Return true if we stripped any EGL_GL_COLORSPACE_KHR attributes.
+static EGLBoolean stripColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
+                                           EGLint format,
+                                           std::vector<EGLint>& stripped_attrib_list) {
+    std::vector<EGLint> allowedColorSpaces;
+    switch (format) {
+        case HAL_PIXEL_FORMAT_RGBA_8888:
+        case HAL_PIXEL_FORMAT_RGB_565:
+            // driver okay with linear & sRGB for 8888, but can't handle
+            // Display-P3 or other spaces.
+            allowedColorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+            allowedColorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
+            break;
+
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBA_1010102:
+        default:
+            // driver does not want to see colorspace attributes for 1010102 or fp16.
+            // Future: if driver supports XXXX extension, we can pass down that colorspace
+            break;
+    }
+
+    bool stripped = false;
+    if (attrib_list && dp->haveExtension("EGL_KHR_gl_colorspace")) {
+        for (const EGLint* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) {
+            if (attr[0] == EGL_GL_COLORSPACE_KHR) {
+                EGLint colorSpace = attr[1];
+                bool found = false;
+                // Verify that color space is allowed
+                for (auto it : allowedColorSpaces) {
+                    if (colorSpace == it) {
+                        found = true;
+                    }
+                }
+                if (!found) {
+                    stripped = true;
+                } else {
+                    stripped_attrib_list.push_back(attr[0]);
+                    stripped_attrib_list.push_back(attr[1]);
+                }
+            } else {
+                stripped_attrib_list.push_back(attr[0]);
+                stripped_attrib_list.push_back(attr[1]);
+            }
+        }
+    }
+    if (stripped) {
+        stripped_attrib_list.push_back(EGL_NONE);
+        stripped_attrib_list.push_back(EGL_NONE);
+    }
+    return stripped;
+}
+
 static EGLBoolean getColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
                                          EGLint& colorSpace, android_dataspace& dataSpace) {
     colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
@@ -514,6 +567,65 @@
     return true;
 }
 
+void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, EGLint& format) {
+    // Set the native window's buffers format to match what this config requests.
+    // Whether to use sRGB gamma is not part of the EGLconfig, but is part
+    // of our native format. So if sRGB gamma is requested, we have to
+    // modify the EGLconfig's format before setting the native window's
+    // format.
+
+    EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
+
+    EGLint a = 0;
+    EGLint r, g, b;
+    r = g = b = 0;
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
+    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
+    EGLint colorDepth = r + g + b;
+
+    // Today, the driver only understands sRGB and linear on 888X
+    // formats. Strip other colorspaces from the attribute list and
+    // only use them to set the dataspace via
+    // native_window_set_buffers_dataspace
+    // if pixel format is RGBX 8888
+    //    TBD: Can test for future extensions that indicate that driver
+    //    handles requested color space and we can let it through.
+    //    allow SRGB and LINEAR. All others need to be stripped.
+    // else if 565, 4444
+    //    TBD: Can we assume these are supported if 8888 is?
+    // else if FP16 or 1010102
+    //    strip colorspace from attribs.
+    // endif
+    if (a == 0) {
+        if (colorDepth <= 16) {
+            format = HAL_PIXEL_FORMAT_RGB_565;
+        } else {
+            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;
+        }
+    }
+}
+
 EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                     NativeWindowType window,
                                     const EGLint *attrib_list)
@@ -543,60 +655,24 @@
             return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
         }
 
-        // Set the native window's buffers format to match what this config requests.
-        // Whether to use sRGB gamma is not part of the EGLconfig, but is part
-        // of our native format. So if sRGB gamma is requested, we have to
-        // modify the EGLconfig's format before setting the native window's
-        // format.
-
-        EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
-        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_COLOR_COMPONENT_TYPE_EXT,
-                                    &componentType);
-
         EGLint format;
+        getNativePixelFormat(iDpy, cnx, config, format);
+
+        // now select correct colorspace and dataspace based on user's attribute list
         EGLint colorSpace;
         android_dataspace dataSpace;
-        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);
-        EGLint colorDepth = r + g + b;
-
-        if (a == 0) {
-            if (colorDepth <= 16) {
-                format = HAL_PIXEL_FORMAT_RGB_565;
-            } else {
-                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;
-            }
-        }
-
-        // now select a corresponding sRGB format if needed
         if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
             ALOGE("error invalid colorspace: %d", colorSpace);
             return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
         }
 
+        std::vector<EGLint> strippedAttribList;
+        if (stripColorSpaceAttribute(dp, attrib_list, format, strippedAttribList)) {
+            // Had to modify the attribute list due to use of color space.
+            // Use modified list from here on.
+            attrib_list = strippedAttribList.data();
+        }
+
         if (format != 0) {
             int err = native_window_set_buffers_format(window, format);
             if (err != 0) {
@@ -671,15 +747,29 @@
 
     egl_connection_t* cnx = NULL;
     egl_display_ptr dp = validate_display_connection(dpy, cnx);
-    EGLint colorSpace;
-    android_dataspace dataSpace;
     if (dp) {
-        // now select a corresponding sRGB format if needed
+        EGLDisplay iDpy = dp->disp.dpy;
+        EGLint format;
+        getNativePixelFormat(iDpy, cnx, config, format);
+
+        // now select correct colorspace and dataspace based on user's attribute list
+        EGLint colorSpace;
+        android_dataspace dataSpace;
         if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
             ALOGE("error invalid colorspace: %d", colorSpace);
             return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
         }
 
+        // Pbuffers are not displayed so we don't need to store the
+        // colorspace. We do need to filter out color spaces the
+        // driver doesn't know how to process.
+        std::vector<EGLint> strippedAttribList;
+        if (stripColorSpaceAttribute(dp, attrib_list, format, strippedAttribList)) {
+            // Had to modify the attribute list due to use of color space.
+            // Use modified list from here on.
+            attrib_list = strippedAttribList.data();
+        }
+
         EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
                 dp->disp.dpy, config, attrib_list);
         if (surface != EGL_NO_SURFACE) {
@@ -1963,8 +2053,15 @@
 
 EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
     clearError();
+    // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
+    // this function cannot be implemented when this libEGL is built for
+    // vendors.
+#ifndef __ANDROID_VNDK__
     if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
     return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
+#else
+    return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
+#endif
 }
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index dc1a4af..579e422 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -26,6 +26,7 @@
 #include <inttypes.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
 #include <thread>
 
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
index 5e82d75..b54752b 100644
--- a/services/inputflinger/InputWindow.cpp
+++ b/services/inputflinger/InputWindow.cpp
@@ -46,8 +46,10 @@
             || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY
             || layoutParamsType == TYPE_STATUS_BAR
             || layoutParamsType == TYPE_NAVIGATION_BAR
+            || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL
             || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY
-            || layoutParamsType == TYPE_DOCK_DIVIDER;
+            || layoutParamsType == TYPE_DOCK_DIVIDER
+            || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY;
 }
 
 bool InputWindowInfo::supportsSplitTouch() const {
diff --git a/services/inputflinger/InputWindow.h b/services/inputflinger/InputWindow.h
index 0ac868b..610290b 100644
--- a/services/inputflinger/InputWindow.h
+++ b/services/inputflinger/InputWindow.h
@@ -101,7 +101,9 @@
         TYPE_NAVIGATION_BAR     = FIRST_SYSTEM_WINDOW+19,
         TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20,
         TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21,
+        TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24,
         TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27,
+        TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32,
         TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34,
         LAST_SYSTEM_WINDOW      = 2999,
     };
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index fad046c..bfe4c09 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -477,7 +477,14 @@
     // separately before the next batch of events.
     for (int j = 0; j < numEventsDropped; ++j) {
         if (scratch[j].type == SENSOR_TYPE_META_DATA) {
-            FlushInfo& flushInfo = mSensorInfo.editValueFor(scratch[j].meta_data.sensor);
+            ssize_t index = mSensorInfo.indexOfKey(scratch[j].meta_data.sensor);
+            if (index < 0) {
+                ALOGW("%s: sensor 0x%x is not found in connection",
+                      __func__, scratch[j].meta_data.sensor);
+                continue;
+            }
+
+            FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
             flushInfo.mPendingFlushEventsToSend++;
             ALOGD_IF(DEBUG_CONNECTIONS, "increment pendingFlushCount %d",
                      flushInfo.mPendingFlushEventsToSend);
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
index 25a3dc5..cf5ab05 100644
--- a/services/sensorservice/hidl/SensorManager.cpp
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -24,7 +24,6 @@
 
 #include <sched.h>
 
-#include <thread>
 
 #include "EventQueue.h"
 #include "DirectReportChannel.h"
@@ -40,20 +39,24 @@
 using ::android::hardware::sensors::V1_0::SensorsEventFormatOffset;
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Void;
-using ::android::sp;
 
 static const char* POLL_THREAD_NAME = "hidl_ssvc_poll";
 
 SensorManager::SensorManager(JavaVM* vm)
-        : mJavaVm(vm) {
+        : mLooper(new Looper(false /*allowNonCallbacks*/)), mStopThread(true), mJavaVm(vm) {
 }
 
 SensorManager::~SensorManager() {
     // Stops pollAll inside the thread.
-    std::unique_lock<std::mutex> lock(mLooperMutex);
+    std::lock_guard<std::mutex> lock(mThreadMutex);
+
+    mStopThread = true;
     if (mLooper != nullptr) {
         mLooper->wake();
     }
+    if (mPollThread.joinable()) {
+        mPollThread.join();
+    }
 }
 
 // Methods from ::android::frameworks::sensorservice::V1_0::ISensorManager follow.
@@ -128,12 +131,13 @@
 }
 
 /* One global looper for all event queues created from this SensorManager. */
-sp<::android::Looper> SensorManager::getLooper() {
-    std::unique_lock<std::mutex> lock(mLooperMutex);
-    if (mLooper == nullptr) {
-        std::condition_variable looperSet;
+sp<Looper> SensorManager::getLooper() {
+    std::lock_guard<std::mutex> lock(mThreadMutex);
 
-        std::thread{[&mutex = mLooperMutex, &looper = mLooper, &looperSet, javaVm = mJavaVm] {
+    if (!mPollThread.joinable()) {
+        // if thread not initialized, start thread
+        mStopThread = false;
+        std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] {
 
             struct sched_param p = {0};
             p.sched_priority = 10;
@@ -142,16 +146,11 @@
                         << strerror(errno);
             }
 
-            std::unique_lock<std::mutex> lock(mutex);
-            if (looper != nullptr) {
-                LOG(INFO) << "Another thread has already set the looper, exiting this one.";
-                return;
-            }
-            looper = Looper::prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS /* opts */);
-            lock.unlock();
+            // set looper
+            Looper::setForThread(looper);
 
-            // Attach the thread to JavaVM so that pollAll do not crash if the event
-            // is from Java.
+            // Attach the thread to JavaVM so that pollAll do not crash if the thread
+            // eventually calls into Java.
             JavaVMAttachArgs args{
                 .version = JNI_VERSION_1_2,
                 .name = POLL_THREAD_NAME,
@@ -162,19 +161,30 @@
                 LOG(FATAL) << "Cannot attach SensorManager looper thread to Java VM.";
             }
 
-            looperSet.notify_one();
-            int pollResult = looper->pollAll(-1 /* timeout */);
-            if (pollResult != ALOOPER_POLL_WAKE) {
-                LOG(ERROR) << "Looper::pollAll returns unexpected " << pollResult;
+            LOG(INFO) << POLL_THREAD_NAME << " started.";
+            for (;;) {
+                int pollResult = looper->pollAll(-1 /* timeout */);
+                if (pollResult == Looper::POLL_WAKE) {
+                    if (stopThread == true) {
+                        LOG(INFO) << POLL_THREAD_NAME << ": requested to stop";
+                        break;
+                    } else {
+                        LOG(INFO) << POLL_THREAD_NAME << ": spurious wake up, back to work";
+                    }
+                } else {
+                    LOG(ERROR) << POLL_THREAD_NAME << ": Looper::pollAll returns unexpected "
+                               << pollResult;
+                    break;
+                }
             }
 
             if (javaVm->DetachCurrentThread() != JNI_OK) {
                 LOG(ERROR) << "Cannot detach SensorManager looper thread from Java VM.";
             }
 
-            LOG(INFO) << "Looper thread is terminated.";
-        }}.detach();
-        looperSet.wait(lock, [this]{ return this->mLooper != nullptr; });
+            LOG(INFO) << POLL_THREAD_NAME << " is terminated.";
+        }};
+        mPollThread = std::move(pollThread);
     }
     return mLooper;
 }
@@ -196,6 +206,12 @@
     }
 
     sp<::android::Looper> looper = getLooper();
+    if (looper == nullptr) {
+        LOG(ERROR) << "::android::SensorManager::createEventQueue cannot initialize looper";
+        _hidl_cb(nullptr, Result::UNKNOWN_ERROR);
+        return Void();
+    }
+
     sp<::android::SensorEventQueue> internalQueue = getInternalManager().createEventQueue();
     if (internalQueue == nullptr) {
         LOG(WARNING) << "::android::SensorManager::createEventQueue returns nullptr.";
diff --git a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
index e66c8e5..ddcee28 100644
--- a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
+++ b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
@@ -20,6 +20,7 @@
 #include <jni.h>
 
 #include <mutex>
+#include <thread>
 
 #include <android/frameworks/sensorservice/1.0/ISensorManager.h>
 #include <android/frameworks/sensorservice/1.0/types.h>
@@ -38,6 +39,7 @@
 using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_memory;
 using ::android::hardware::Return;
+using ::android::sp;
 
 struct SensorManager final : public ISensorManager {
 
@@ -54,13 +56,15 @@
 private:
     // Block until ::android::SensorManager is initialized.
     ::android::SensorManager& getInternalManager();
-    sp<::android::Looper> getLooper();
+    sp<Looper> getLooper();
 
     std::mutex mInternalManagerMutex;
     ::android::SensorManager* mInternalManager = nullptr; // does not own
+    sp<Looper> mLooper;
 
-    std::mutex mLooperMutex;
-    sp<::android::Looper> mLooper;
+    volatile bool mStopThread;
+    std::mutex mThreadMutex; //protects mPollThread
+    std::thread mPollThread;
 
     JavaVM* mJavaVm;
 };
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index ae628e1..e34fa16 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -553,6 +553,29 @@
     return Error::NONE;
 }
 
+Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
+                               uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
+   mWriter.selectDisplay(display);
+   mWriter.presentOrvalidateDisplay();
+
+   Error error = execute();
+   if (error != Error::NONE) {
+       return error;
+   }
+
+   mReader.takePresentOrValidateStage(display, state);
+
+   if (*state == 1) { // Present succeeded
+       mReader.takePresentFence(display, outPresentFence);
+   }
+
+   if (*state == 0) { // Validate succeeded.
+       mReader.hasChanges(display, outNumTypes, outNumRequests);
+   }
+
+   return Error::NONE;
+}
+
 Error Composer::setCursorPosition(Display display, Layer layer,
         int32_t x, int32_t y)
 {
@@ -770,7 +793,8 @@
             auto command = mWriter.getCommand(cmdErr.location);
 
             if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
-                command == IComposerClient::Command::PRESENT_DISPLAY) {
+                command == IComposerClient::Command::PRESENT_DISPLAY ||
+                command == IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY) {
                 error = cmdErr.error;
             } else {
                 ALOGW("command 0x%x generated error %d",
@@ -821,6 +845,9 @@
         case IComposerClient::Command::SET_RELEASE_FENCES:
             parsed = parseSetReleaseFences(length);
             break;
+        case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
+            parsed = parseSetPresentOrValidateDisplayResult(length);
+            break;
         default:
             parsed = false;
             break;
@@ -949,6 +976,15 @@
     return true;
 }
 
+bool CommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length)
+{
+    if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) {
+        return false;
+    }
+    mCurrentReturnData->presentOrValidateState = read();
+    return true;
+}
+
 void CommandReader::resetData()
 {
     mErrors.clear();
@@ -1058,6 +1094,16 @@
     data.presentFence = -1;
 }
 
+void CommandReader::takePresentOrValidateStage(Display display, uint32_t* state) {
+    auto found = mReturnData.find(display);
+    if (found == mReturnData.end()) {
+        *state= -1;
+        return;
+    }
+    ReturnData& data = found->second;
+    *state = data.presentOrValidateState;
+}
+
 } // namespace Hwc2
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 68d6e6f..96dd833 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -93,6 +93,9 @@
     // Get and clear saved present fence.
     void takePresentFence(Display display, int* outPresentFence);
 
+    // Get what stage succeeded during PresentOrValidate: Present or Validate
+    void takePresentOrValidateStage(Display display, uint32_t * state);
+
 private:
     void resetData();
 
@@ -102,6 +105,7 @@
     bool parseSetDisplayRequests(uint16_t length);
     bool parseSetPresentFence(uint16_t length);
     bool parseSetReleaseFences(uint16_t length);
+    bool parseSetPresentOrValidateDisplayResult(uint16_t length);
 
     struct ReturnData {
         uint32_t displayRequests = 0;
@@ -116,6 +120,8 @@
 
         std::vector<Layer> releasedLayers;
         std::vector<int> releaseFences;
+
+        uint32_t presentOrValidateState;
     };
 
     std::vector<CommandError> mErrors;
@@ -202,6 +208,11 @@
     Error validateDisplay(Display display, uint32_t* outNumTypes,
             uint32_t* outNumRequests);
 
+    Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
+                                   uint32_t* outNumRequests,
+                                   int* outPresentFence,
+                                   uint32_t* state);
+
     Error setCursorPosition(Display display, Layer layer,
             int32_t x, int32_t y);
     /* see setClientTarget for the purpose of slot */
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 263ff00..270a732 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -695,6 +695,34 @@
     return error;
 }
 
+Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                 sp<android::Fence>* outPresentFence, uint32_t* state) {
+
+    uint32_t numTypes = 0;
+    uint32_t numRequests = 0;
+    int32_t presentFenceFd = -1;
+    auto intError = mDevice.mComposer->presentOrValidateDisplay(mId, &numTypes, &numRequests, &presentFenceFd, state);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None && error != Error::HasChanges) {
+        return error;
+    }
+
+    if (*state == 1) {
+        *outPresentFence = new Fence(presentFenceFd);
+    }
+
+    if (*state == 0) {
+        *outNumTypes = numTypes;
+        *outNumRequests = numRequests;
+    }
+    return error;
+}
+
+void Display::discardCommands()
+{
+    mDevice.mComposer->resetCommands();
+}
+
 // For use by Device
 
 int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index b7376d0..404bb28 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -255,6 +255,15 @@
     [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled);
     [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes,
             uint32_t* outNumRequests);
+    [[clang::warn_unused_result]] Error presentOrValidate(uint32_t* outNumTypes,
+                                                 uint32_t* outNumRequests,
+                                                          android::sp<android::Fence>* outPresentFence, uint32_t* state);
+
+    // Most methods in this class write a command to a command buffer.  The
+    // command buffer is implicitly submitted in validate, present, and
+    // presentOrValidate.  This method provides a way to discard the commands,
+    // which can be used to discard stale commands.
+    void discardCommands();
 
     // Other Display methods
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 42be935..ac2dde2 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -455,7 +455,34 @@
 
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
-    auto error = hwcDisplay->validate(&numTypes, &numRequests);
+
+    HWC2::Error error = HWC2::Error::None;
+
+    // First try to skip validate altogether if the HWC supports it.
+    displayData.validateWasSkipped = false;
+    if (hasCapability(HWC2::Capability::SkipValidate) &&
+            !displayData.hasClientComposition) {
+        sp<android::Fence> outPresentFence;
+        uint32_t state = UINT32_MAX;
+        error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
+        if (error != HWC2::Error::None && error != HWC2::Error::HasChanges) {
+            ALOGV("skipValidate: Failed to Present or Validate");
+            return UNKNOWN_ERROR;
+        }
+        if (state == 1) { //Present Succeeded.
+            std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences;
+            error = hwcDisplay->getReleaseFences(&releaseFences);
+            displayData.releaseFences = std::move(releaseFences);
+            displayData.lastPresentFence = outPresentFence;
+            displayData.validateWasSkipped = true;
+            displayData.presentError = error;
+            return NO_ERROR;
+        }
+        // Present failed but Validate ran.
+    } else {
+        error = hwcDisplay->validate(&numTypes, &numRequests);
+    }
+    ALOGV("SkipValidate failed, Falling back to SLOW validate/present");
     if (error != HWC2::Error::None && error != HWC2::Error::HasChanges) {
         ALOGE("prepare: validate failed for display %d: %s (%d)", displayId,
                 to_string(error).c_str(), static_cast<int32_t>(error));
@@ -592,6 +619,18 @@
 
     auto& displayData = mDisplayData[displayId];
     auto& hwcDisplay = displayData.hwcDisplay;
+
+    if (displayData.validateWasSkipped) {
+        hwcDisplay->discardCommands();
+        auto error = displayData.presentError;
+        if (error != HWC2::Error::None) {
+            ALOGE("skipValidate: failed for display %d: %s (%d)",
+                  displayId, to_string(error).c_str(), static_cast<int32_t>(error));
+            return UNKNOWN_ERROR;
+        }
+        return NO_ERROR;
+    }
+
     auto error = hwcDisplay->present(&displayData.lastPresentFence);
     if (error != HWC2::Error::None) {
         ALOGE("presentAndGetReleaseFences: failed for display %d: %s (%d)",
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 3eb968d..7463362 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -202,6 +202,9 @@
 
         // protected by mVsyncLock
         HWC2::Vsync vsyncEnabled;
+
+        bool validateWasSkipped;
+        HWC2::Error presentError;
     };
 
     std::unique_ptr<HWC2::Device>   mHwcDevice;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5aed896..273f194 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1952,7 +1952,6 @@
     mCurrentState.barrierLayer = nullptr;
     mCurrentState.frameNumber = 0;
     mCurrentState.modified = false;
-    ALOGE("Deferred transaction");
 }
 
 void Layer::deferTransactionUntil(const sp<IBinder>& barrierHandle,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 6ed372c..392479f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -408,6 +408,7 @@
      * to figure out if the content or size of a surface has changed.
      */
     Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime);
+    bool isBufferLatched() const { return mRefreshPending; }
 
     bool isPotentialCursor() const { return mPotentialCursor;}
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c6ce82c..c10493b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2457,7 +2457,7 @@
         const Region dirty(layer->latchBuffer(visibleRegions, latchTime));
         layer->useSurfaceDamage();
         invalidateLayerStack(layer->getLayerStack(), dirty);
-        if (!dirty.isEmpty()) {
+        if (layer->isBufferLatched()) {
             newDataLatched = true;
         }
     }
@@ -2467,7 +2467,7 @@
     // If we will need to wake up at some time in the future to deal with a
     // queued frame that shouldn't be displayed during this vsync period, wake
     // up during the next vsync period to check again.
-    if (frameQueued && mLayersWithQueuedFrames.empty()) {
+    if (frameQueued && (mLayersWithQueuedFrames.empty() || !newDataLatched)) {
         signalLayerUpdate();
     }
 
@@ -2730,8 +2730,6 @@
             return NO_ERROR;
         }
 
-        index = p->removeChild(layer);
-
         sp<Layer> ancestor = p;
         while (ancestor->getParent() != nullptr) {
             ancestor = ancestor->getParent();
@@ -2740,6 +2738,8 @@
             ALOGE("removeLayer called with a layer whose parent has been removed");
             return NAME_NOT_FOUND;
         }
+
+        index = p->removeChild(layer);
     } else {
         index = mCurrentState.layersSortedByZ.remove(layer);
     }
@@ -3770,6 +3770,15 @@
      */
     const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
     alloc.dump(result);
+
+    /*
+     * Dump VrFlinger state if in use.
+     */
+    if (mVrFlingerRequestsDisplay && mVrFlinger) {
+        result.append("VrFlinger state:\n");
+        result.append(mVrFlinger->Dump().c_str());
+        result.append("\n");
+    }
 }
 
 const Vector< sp<Layer> >&
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 9babeef..abc8fde 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -240,10 +240,15 @@
 }
 
 void SurfaceFlingerConsumer::onSidebandStreamChanged() {
+    FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
+    {
+        Mutex::Autolock lock(mFrameAvailableMutex);
+        unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
+    }
     sp<ContentsChangedListener> listener;
     {   // scope for the lock
         Mutex::Autolock lock(mMutex);
-        ALOG_ASSERT(mFrameAvailableListener.unsafe_get() == mContentsChangedListener.unsafe_get());
+        ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
         listener = mContentsChangedListener.promote();
     }
 
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 7856114..f8d1bb0 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -2327,8 +2327,13 @@
         if (parent == nullptr) {
             mCurrentState.layersSortedByZ.add(lbc);
         } else {
+            if (mCurrentState.layersSortedByZ.indexOf(parent) < 0) {
+                ALOGE("addClientLayer called with a removed parent");
+                return NAME_NOT_FOUND;
+            }
             parent->addChild(lbc);
         }
+
         mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
         mLayersAdded = true;
         mNumLayers++;
@@ -2350,6 +2355,15 @@
             return NO_ERROR;
         }
 
+        sp<Layer> ancestor = p;
+        while (ancestor->getParent() != nullptr) {
+            ancestor = ancestor->getParent();
+        }
+        if (mCurrentState.layersSortedByZ.indexOf(ancestor) < 0) {
+            ALOGE("removeLayer called with a layer whose parent has been removed");
+            return NAME_NOT_FOUND;
+        }
+
         index = p->removeChild(layer);
     } else {
         index = mCurrentState.layersSortedByZ.remove(layer);
@@ -2371,7 +2385,7 @@
 
     mLayersPendingRemoval.add(layer);
     mLayersRemoved = true;
-    mNumLayers--;
+    mNumLayers -= 1 + layer->getChildrenCount();
     setTransactionFlags(eTransactionNeeded);
     return NO_ERROR;
 }
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 9bbb7f3..861114d 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -15,6 +15,7 @@
  */
 #include "impl/vr_hwc.h"
 
+#include <cutils/properties.h>
 #include <private/dvr/display_client.h>
 #include <ui/Fence.h>
 
@@ -352,7 +353,14 @@
       break;
     case IComposerClient::Attribute::DPI_X:
     case IComposerClient::Attribute::DPI_Y:
-      *outValue = 300 * 1000;  // 300dpi
+      {
+        constexpr int32_t kDefaultDPI = 300;
+        int32_t dpi = property_get_int32("ro.vr.hwc.dpi", kDefaultDPI);
+        if (dpi <= 0) {
+          dpi = kDefaultDPI;
+        }
+        *outValue = 1000 * dpi;
+      }
       break;
     default:
       return Error::BAD_PARAMETER;
diff --git a/services/vr/performanced/Android.mk b/services/vr/performanced/Android.mk
index 6256e90..dbc66f1 100644
--- a/services/vr/performanced/Android.mk
+++ b/services/vr/performanced/Android.mk
@@ -23,8 +23,10 @@
 staticLibraries := \
 	libperformance \
 	libpdx_default_transport \
+	libvr_manager
 
 sharedLibraries := \
+	libbinder \
 	libbase \
 	libcutils \
 	liblog \
diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp
index 1a3723f..1a7264c 100644
--- a/services/vr/performanced/cpu_set.cpp
+++ b/services/vr/performanced/cpu_set.cpp
@@ -15,6 +15,9 @@
 #include "task.h"
 #include "unique_file.h"
 
+using android::pdx::ErrorStatus;
+using android::pdx::Status;
+
 namespace {
 
 constexpr int kDirectoryFlags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
@@ -176,11 +179,11 @@
                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,
+      auto status = target->AttachTask(task_id);
+      ALOGW_IF(!status && status.error() != EINVAL,
                "CpuSetManager::MoveUnboundTasks: Failed to attach task_id=%d "
                "to cpuset=%s: %s",
-               task_id, target_set.c_str(), strerror(-ret));
+               task_id, target_set.c_str(), status.GetErrorMessage().c_str());
     } else {
       ALOGD_IF(TRACE,
                "CpuSet::MoveUnboundTasks: Skipping task_id=%d name=%s cpus=%s.",
@@ -233,7 +236,7 @@
   return fp;
 }
 
-int CpuSet::AttachTask(pid_t task_id) const {
+Status<void> CpuSet::AttachTask(pid_t task_id) const {
   auto file = OpenFile("tasks", O_RDWR);
   if (file.get() >= 0) {
     std::ostringstream stream;
@@ -241,11 +244,15 @@
     std::string value = stream.str();
 
     const bool ret = base::WriteStringToFd(value, file.get());
-    return !ret ? -errno : 0;
+    if (!ret)
+      return ErrorStatus(errno);
+    else
+      return {};
   } else {
+    const int error = errno;
     ALOGE("CpuSet::AttachTask: Failed to open %s/tasks: %s", path_.c_str(),
-          strerror(errno));
-    return -errno;
+          strerror(error));
+    return ErrorStatus(error);
   }
 }
 
diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h
index fcf280a..6879272 100644
--- a/services/vr/performanced/cpu_set.h
+++ b/services/vr/performanced/cpu_set.h
@@ -11,6 +11,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include <pdx/status.h>
+
 #include "unique_file.h"
 
 namespace android {
@@ -28,7 +30,7 @@
 
   std::string GetCpuList() const;
 
-  int AttachTask(pid_t task_id) const;
+  pdx::Status<void> AttachTask(pid_t task_id) const;
   std::vector<pid_t> GetTasks() const;
 
  private:
diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp
index 955c661..3f7009a 100644
--- a/services/vr/performanced/performance_service.cpp
+++ b/services/vr/performanced/performance_service.cpp
@@ -8,14 +8,20 @@
 #include <pdx/rpc/argument_encoder.h>
 #include <pdx/rpc/message_buffer.h>
 #include <pdx/rpc/remote_method.h>
+#include <private/android_filesystem_config.h>
 #include <private/dvr/performance_rpc.h>
+#include <private/dvr/trusted_uids.h>
 
 #include "task.h"
 
 // This prctl is only available in Android kernels.
 #define PR_SET_TIMERSLACK_PID 41
 
+using android::dvr::IsTrustedUid;
+using android::dvr::Task;
+using android::pdx::ErrorStatus;
 using android::pdx::Message;
+using android::pdx::Status;
 using android::pdx::rpc::DispatchRemoteMethod;
 using android::pdx::default_transport::Endpoint;
 
@@ -26,6 +32,68 @@
 constexpr unsigned long kTimerSlackForegroundNs = 50000;
 constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
 
+// Expands the given parameter pack expression using an initializer list to
+// guarantee ordering and a comma expression to guarantee even void expressions
+// are valid elements of the initializer list.
+#define EXPAND_PACK(...) \
+  std::initializer_list<int> { (__VA_ARGS__, 0)... }
+
+// Returns true if the sender's euid matches any of the uids in |UIDs|.
+template <uid_t... UIDs>
+struct UserId {
+  static bool Check(const Message& sender, const Task&) {
+    const uid_t uid = sender.GetEffectiveUserId();
+    bool allow = false;
+    EXPAND_PACK(allow |= (uid == UIDs));
+    return allow;
+  }
+};
+
+// Returns true if the sender's egid matches any of the gids in |GIDs|.
+template <gid_t... GIDs>
+struct GroupId {
+  static bool Check(const Message& sender, const Task&) {
+    const gid_t gid = sender.GetEffectiveGroupId();
+    bool allow = false;
+    EXPAND_PACK(allow |= (gid == GIDs));
+    return allow;
+  }
+};
+
+// Returns true if the sender's euid is trusted according to VR manager service.
+struct Trusted {
+  static bool Check(const Message& sender, const Task&) {
+    return IsTrustedUid(sender.GetEffectiveUserId(), false);
+  }
+};
+
+// Returns returns true if the task belongs to the sending process.
+struct SameProcess {
+  static bool Check(const Message& sender, const Task& task) {
+    return sender.GetProcessId() == task.thread_group_id();
+  }
+};
+
+// Returns true if any of the checks in |Allows| pass, false otherwise.
+template <typename... Allows>
+struct CheckOr {
+  static bool Check(const Message& sender, const Task& task) {
+    bool allow = false;
+    EXPAND_PACK(allow |= Allows::Check(sender, task));
+    return allow;
+  }
+};
+
+// Returns true if all of the checks in |Allows| pass, false otherwise.
+template <typename... Allows>
+struct CheckAnd {
+  static bool Check(const Message& sender, const Task& task) {
+    bool allow = true;
+    EXPAND_PACK(allow &= Allows::Check(sender, task));
+    return allow;
+  }
+};
+
 }  // anonymous namespace
 
 namespace android {
@@ -48,43 +116,79 @@
   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.
+  // TODO(eieio): Make this configurable on the command line or config file.
   cpuset_.MoveUnboundTasks("/kernel");
 
+  // TODO(eieio): Replace this witha device-specific config file. This is just a
+  // hack for now to put some form of permission logic in place while a longer
+  // term solution is developed.
+  using AllowRootSystem =
+      CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM>,
+                                    GroupId<AID_SYSTEM>>>;
+  using AllowRootSystemGraphics =
+      CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>,
+                                    GroupId<AID_SYSTEM, AID_GRAPHICS>>>;
+  using AllowRootSystemAudio =
+      CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_AUDIO>,
+                                    GroupId<AID_SYSTEM, AID_AUDIO>>>;
+  using AllowRootSystemTrusted = CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>,
+                                        GroupId<AID_SYSTEM>>;
+
+  partition_permission_check_ = AllowRootSystemTrusted::Check;
+
   // Setup the scheduler classes.
+  // TODO(eieio): Replace this with a device-specific config file.
   scheduler_classes_ = {
       {"audio:low",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
-        .priority = fifo_medium}},
+        .priority = fifo_medium,
+        .permission_check = AllowRootSystemAudio::Check}},
       {"audio:high",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
-        .priority = fifo_medium + 3}},
+        .priority = fifo_medium + 3,
+        .permission_check = AllowRootSystemAudio::Check}},
       {"graphics",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
-        .priority = fifo_medium}},
+        .priority = fifo_medium,
+        .permission_check = AllowRootSystemGraphics::Check}},
       {"graphics:low",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
-        .priority = fifo_medium}},
+        .priority = fifo_medium,
+        .permission_check = AllowRootSystemGraphics::Check}},
       {"graphics:high",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
-        .priority = fifo_medium + 2}},
+        .priority = fifo_medium + 2,
+        .permission_check = AllowRootSystemGraphics::Check}},
       {"sensors",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
-        .priority = fifo_low}},
+        .priority = fifo_low,
+        .permission_check = AllowRootSystem::Check}},
       {"sensors:low",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
-        .priority = fifo_low}},
+        .priority = fifo_low,
+        .permission_check = AllowRootSystem::Check}},
       {"sensors:high",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
-        .priority = fifo_low + 1}},
+        .priority = fifo_low + 1,
+        .permission_check = AllowRootSystem::Check}},
+      {"vr:system:arp",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_medium + 2,
+        .permission_check = AllowRootSystemTrusted::Check}},
+      {"vr:app:render",
+       {.timer_slack = kTimerSlackForegroundNs,
+        .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+        .priority = fifo_medium + 1,
+        .permission_check = AllowRootSystemTrusted::Check}},
       {"normal",
        {.timer_slack = kTimerSlackForegroundNs,
         .scheduler_policy = SCHED_NORMAL,
@@ -113,67 +217,94 @@
   return cpuset_.DumpState();
 }
 
-int PerformanceService::OnSetCpuPartition(Message& message, pid_t task_id,
-                                          const std::string& partition) {
+Status<void> PerformanceService::OnSetSchedulerPolicy(
+    Message& message, pid_t task_id, const std::string& scheduler_policy) {
+  // Forward to scheduler class handler for now. In the future this method will
+  // subsume the others by unifying both scheduler class and cpu partiton into a
+  // single policy concept.
+  ALOGI(
+      "PerformanceService::OnSetSchedulerPolicy: task_id=%d "
+      "scheduler_policy=%s",
+      task_id, scheduler_policy.c_str());
+  return OnSetSchedulerClass(message, task_id, scheduler_policy);
+}
+
+Status<void> 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;
+    return ErrorStatus(EINVAL);
+
+  // Temporary permission check.
+  // TODO(eieio): Replace this with a configuration file.
+  if (partition_permission_check_ &&
+      !partition_permission_check_(message, task)) {
+    return ErrorStatus(EINVAL);
+  }
 
   auto target_set = cpuset_.Lookup(partition);
   if (!target_set)
-    return -ENOENT;
+    return ErrorStatus(ENOENT);
 
-  const auto attach_error = target_set->AttachTask(task_id);
-  if (attach_error)
-    return attach_error;
+  auto attach_status = target_set->AttachTask(task_id);
+  if (!attach_status)
+    return attach_status;
 
-  return 0;
+  return {};
 }
 
-int PerformanceService::OnSetSchedulerClass(
+Status<void> 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;
+  if (!task)
+    return ErrorStatus(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;
+
+    // Make sure the sending process is allowed to make the requested change to
+    // this task.
+    if (!config.IsAllowed(message, task))
+      return ErrorStatus(EINVAL);
+
+    struct sched_param param;
     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;
+    return {};
   } else {
     ALOGE(
         "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
         "by task=%d.",
         scheduler_class.c_str(), task_id);
-    return -EINVAL;
+    return ErrorStatus(EINVAL);
   }
 }
 
-std::string PerformanceService::OnGetCpuPartition(Message& message,
-                                                  pid_t task_id) {
+Status<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 ErrorStatus(EINVAL);
 
   return task.GetCpuSetPath();
 }
 
-pdx::Status<void> PerformanceService::HandleMessage(Message& message) {
+Status<void> PerformanceService::HandleMessage(Message& message) {
+  ALOGD_IF(TRACE, "PerformanceService::HandleMessage: op=%d", message.GetOp());
   switch (message.GetOp()) {
+    case PerformanceRPC::SetSchedulerPolicy::Opcode:
+      DispatchRemoteMethod<PerformanceRPC::SetSchedulerPolicy>(
+          *this, &PerformanceService::OnSetSchedulerPolicy, message);
+      return {};
+
     case PerformanceRPC::SetCpuPartition::Opcode:
-      DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+      DispatchRemoteMethod<PerformanceRPC::SetCpuPartition>(
           *this, &PerformanceService::OnSetCpuPartition, message);
       return {};
 
diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h
index 34abba7..b812535 100644
--- a/services/vr/performanced/performance_service.h
+++ b/services/vr/performanced/performance_service.h
@@ -1,12 +1,14 @@
 #ifndef ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
 #define ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
 
+#include <functional>
 #include <string>
 #include <unordered_map>
 
 #include <pdx/service.h>
 
 #include "cpu_set.h"
+#include "task.h"
 
 namespace android {
 namespace dvr {
@@ -27,11 +29,15 @@
 
   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);
+  pdx::Status<void> OnSetSchedulerPolicy(pdx::Message& message, pid_t task_id,
+                                         const std::string& scheduler_class);
+
+  pdx::Status<void> OnSetCpuPartition(pdx::Message& message, pid_t task_id,
+                                      const std::string& partition);
+  pdx::Status<void> OnSetSchedulerClass(pdx::Message& message, pid_t task_id,
+                                        const std::string& scheduler_class);
+  pdx::Status<std::string> OnGetCpuPartition(pdx::Message& message,
+                                             pid_t task_id);
 
   CpuSetManager cpuset_;
 
@@ -43,10 +49,24 @@
     unsigned long timer_slack;
     int scheduler_policy;
     int priority;
+    std::function<bool(const pdx::Message& message, const Task& task)>
+        permission_check;
+
+    // Check the permisison of the given task to use this scheduler class. If a
+    // permission check function is not set then all tasks are allowed.
+    bool IsAllowed(const pdx::Message& message, const Task& task) const {
+      if (permission_check)
+        return permission_check(message, task);
+      else
+        return true;
+    }
   };
 
   std::unordered_map<std::string, SchedulerClassConfig> scheduler_classes_;
 
+  std::function<bool(const pdx::Message& message, const Task& task)>
+      partition_permission_check_;
+
   PerformanceService(const PerformanceService&) = delete;
   void operator=(const PerformanceService&) = delete;
 };
diff --git a/services/vr/performanced/performance_service_tests.cpp b/services/vr/performanced/performance_service_tests.cpp
index b526082..7de1f08 100644
--- a/services/vr/performanced/performance_service_tests.cpp
+++ b/services/vr/performanced/performance_service_tests.cpp
@@ -1,12 +1,22 @@
 #include <errno.h>
 #include <sched.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <condition_variable>
+#include <cstdlib>
 #include <mutex>
 #include <thread>
 
 #include <dvr/performance_client_api.h>
 #include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+
+namespace {
+
+const char kTrustedUidEnvironmentVariable[] = "GTEST_TRUSTED_UID";
+
+}  // anonymous namespace
 
 TEST(DISABLED_PerformanceTest, SetCpuPartition) {
   int error;
@@ -86,6 +96,27 @@
   EXPECT_EQ(-EINVAL, error);
 }
 
+// This API mirrors SetSchedulerClass for now. Replace with with a more specific
+// test once the policy API is fully implemented.
+TEST(PerformanceTest, SetSchedulerPolicy) {
+  int error;
+
+  error = dvrSetSchedulerPolicy(0, "background");
+  EXPECT_EQ(0, error);
+  EXPECT_EQ(SCHED_BATCH, sched_getscheduler(0));
+
+  error = dvrSetSchedulerPolicy(0, "audio:low");
+  EXPECT_EQ(0, error);
+  EXPECT_EQ(SCHED_FIFO | SCHED_RESET_ON_FORK, sched_getscheduler(0));
+
+  error = dvrSetSchedulerPolicy(0, "normal");
+  EXPECT_EQ(0, error);
+  EXPECT_EQ(SCHED_NORMAL, sched_getscheduler(0));
+
+  error = dvrSetSchedulerPolicy(0, "foobar");
+  EXPECT_EQ(-EINVAL, error);
+}
+
 TEST(PerformanceTest, SchedulerClassResetOnFork) {
   int error;
 
@@ -135,3 +166,273 @@
   error = dvrGetCpuPartition(0, nullptr, sizeof(partition));
   EXPECT_EQ(-EINVAL, error);
 }
+
+TEST(PerformanceTest, Permissions) {
+  int error;
+
+  const int original_uid = getuid();
+  const int original_gid = getgid();
+  int trusted_uid = -1;
+
+  // See if the environment variable GTEST_TRUSTED_UID is set. If it is enable
+  // testing the ActivityManager trusted uid permission checks using that uid.
+  const char* trusted_uid_env = std::getenv(kTrustedUidEnvironmentVariable);
+  if (trusted_uid_env)
+    trusted_uid = std::atoi(trusted_uid_env);
+
+  ASSERT_EQ(AID_ROOT, original_uid)
+      << "This test must run as root to function correctly!";
+
+  // Switch the uid/gid to an id that should not have permission to access any
+  // privileged actions.
+  ASSERT_EQ(0, setresgid(AID_NOBODY, AID_NOBODY, -1))
+      << "Failed to set gid: " << strerror(errno);
+  ASSERT_EQ(0, setresuid(AID_NOBODY, AID_NOBODY, -1))
+      << "Failed to set uid: " << strerror(errno);
+
+  // Unprivileged policies.
+  error = dvrSetSchedulerPolicy(0, "batch");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "background");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "foreground");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "normal");
+  EXPECT_EQ(0, error);
+
+  // Privileged policies.
+  error = dvrSetSchedulerPolicy(0, "audio:low");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "audio:high");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "graphics");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:low");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:high");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "sensors");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:low");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:high");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "vr:app:render");
+  EXPECT_EQ(-EINVAL, error);
+
+  // uid=AID_SYSTEM / gid=AID_NOBODY
+  ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+      << "Failed to restore uid: " << strerror(errno);
+  ASSERT_EQ(0, setresuid(AID_SYSTEM, AID_SYSTEM, -1))
+      << "Failed to set uid: " << strerror(errno);
+
+  // Unprivileged policies.
+  error = dvrSetSchedulerPolicy(0, "batch");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "background");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "foreground");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "normal");
+  EXPECT_EQ(0, error);
+
+  // Privileged policies.
+  error = dvrSetSchedulerPolicy(0, "audio:low");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "audio:high");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:low");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:high");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "sensors");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:low");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:high");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "vr:app:render");
+  EXPECT_EQ(0, error);
+
+  // uid=AID_NOBODY / gid=AID_SYSTEM
+  ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+      << "Failed to restore uid: " << strerror(errno);
+  ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+      << "Failed to restore gid: " << strerror(errno);
+  ASSERT_EQ(0, setresgid(AID_SYSTEM, AID_SYSTEM, -1))
+      << "Failed to set gid: " << strerror(errno);
+  ASSERT_EQ(0, setresuid(AID_SYSTEM, AID_NOBODY, -1))
+      << "Failed to set uid: " << strerror(errno);
+
+  // Unprivileged policies.
+  error = dvrSetSchedulerPolicy(0, "batch");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "background");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "foreground");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "normal");
+  EXPECT_EQ(0, error);
+
+  // Privileged policies.
+  error = dvrSetSchedulerPolicy(0, "audio:low");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "audio:high");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:low");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:high");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "sensors");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:low");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:high");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "vr:app:render");
+  EXPECT_EQ(0, error);
+
+  // uid=AID_GRAPHICS / gid=AID_NOBODY
+  ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+      << "Failed to restore uid: " << strerror(errno);
+  ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+      << "Failed to restore gid: " << strerror(errno);
+  ASSERT_EQ(0, setresgid(AID_NOBODY, AID_NOBODY, -1))
+      << "Failed to set gid: " << strerror(errno);
+  ASSERT_EQ(0, setresuid(AID_GRAPHICS, AID_GRAPHICS, -1))
+      << "Failed to set uid: " << strerror(errno);
+
+  // Unprivileged policies.
+  error = dvrSetSchedulerPolicy(0, "batch");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "background");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "foreground");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "normal");
+  EXPECT_EQ(0, error);
+
+  // Privileged policies.
+  error = dvrSetSchedulerPolicy(0, "audio:low");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "audio:high");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "graphics");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:low");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:high");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "sensors");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:low");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:high");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "vr:app:render");
+  EXPECT_EQ(-EINVAL, error);
+
+  // uid=AID_NOBODY / gid=AID_GRAPHICS
+  ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+      << "Failed to restore uid: " << strerror(errno);
+  ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+      << "Failed to restore gid: " << strerror(errno);
+  ASSERT_EQ(0, setresgid(AID_GRAPHICS, AID_GRAPHICS, -1))
+      << "Failed to set gid: " << strerror(errno);
+  ASSERT_EQ(0, setresuid(AID_NOBODY, AID_NOBODY, -1))
+      << "Failed to set uid: " << strerror(errno);
+
+  // Unprivileged policies.
+  error = dvrSetSchedulerPolicy(0, "batch");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "background");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "foreground");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "normal");
+  EXPECT_EQ(0, error);
+
+  // Privileged policies.
+  error = dvrSetSchedulerPolicy(0, "audio:low");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "audio:high");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "graphics");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:low");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "graphics:high");
+  EXPECT_EQ(0, error);
+  error = dvrSetSchedulerPolicy(0, "sensors");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:low");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "sensors:high");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+  EXPECT_EQ(-EINVAL, error);
+  error = dvrSetSchedulerPolicy(0, "vr:app:render");
+  EXPECT_EQ(-EINVAL, error);
+
+  if (trusted_uid != -1) {
+    // uid=<trusted uid> / gid=AID_NOBODY
+    ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+        << "Failed to restore uid: " << strerror(errno);
+    ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+        << "Failed to restore gid: " << strerror(errno);
+    ASSERT_EQ(0, setresgid(AID_NOBODY, AID_NOBODY, -1))
+        << "Failed to set gid: " << strerror(errno);
+    ASSERT_EQ(0, setresuid(trusted_uid, trusted_uid, -1))
+        << "Failed to set uid: " << strerror(errno);
+
+    // Unprivileged policies.
+    error = dvrSetSchedulerPolicy(0, "batch");
+    EXPECT_EQ(0, error);
+    error = dvrSetSchedulerPolicy(0, "background");
+    EXPECT_EQ(0, error);
+    error = dvrSetSchedulerPolicy(0, "foreground");
+    EXPECT_EQ(0, error);
+    error = dvrSetSchedulerPolicy(0, "normal");
+    EXPECT_EQ(0, error);
+
+    // Privileged policies.
+    error = dvrSetSchedulerPolicy(0, "audio:low");
+    EXPECT_EQ(-EINVAL, error);
+    error = dvrSetSchedulerPolicy(0, "audio:high");
+    EXPECT_EQ(-EINVAL, error);
+    error = dvrSetSchedulerPolicy(0, "graphics");
+    EXPECT_EQ(0, error);
+    error = dvrSetSchedulerPolicy(0, "graphics:low");
+    EXPECT_EQ(0, error);
+    error = dvrSetSchedulerPolicy(0, "graphics:high");
+    EXPECT_EQ(0, error);
+    error = dvrSetSchedulerPolicy(0, "sensors");
+    EXPECT_EQ(-EINVAL, error);
+    error = dvrSetSchedulerPolicy(0, "sensors:low");
+    EXPECT_EQ(-EINVAL, error);
+    error = dvrSetSchedulerPolicy(0, "sensors:high");
+    EXPECT_EQ(-EINVAL, error);
+    error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+    EXPECT_EQ(0, error);
+    error = dvrSetSchedulerPolicy(0, "vr:app:render");
+    EXPECT_EQ(0, error);
+  }
+
+  // Restore original effective uid/gid.
+  ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+      << "Failed to restore gid: " << strerror(errno);
+  ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+      << "Failed to restore uid: " << strerror(errno);
+}