Merge "Trust navbar panel and accessibility overlays" into oc-dr1-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2f8840b..4161bd7 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -843,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"});
@@ -1013,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");
 
@@ -1090,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();
 
@@ -1215,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");
@@ -1752,13 +1798,7 @@
     ds.PrintHeader();
 
     if (telephony_only) {
-        DumpIpTables();
-        if (!DropRootUser()) {
-            return -1;
-        }
-        do_dmesg();
-        DoLogcat();
-        DoKmsg();
+        DumpstateTelephonyOnly();
         ds.DumpstateBoard();
     } else {
         // Dumps systrace right away, otherwise it will be filled with unnecessary events.
@@ -1793,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/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/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index d1a9b04..e9fc8fd 100644
--- a/libs/gui/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/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_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 c44f8a6..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;
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index 4b530b2..ca41e52 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>
@@ -320,6 +321,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 64e5e71..09bae84 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -151,3 +151,6 @@
 
 // Read the native display metrics from the hardware composer
 DVR_V1_API_ENTRY(GetNativeDisplayMetrics);
+
+// Performance
+DVR_V1_API_ENTRY(PerformanceSetSchedulerPolicy);
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/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/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/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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8192968..d128a06 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -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/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);
+}