Merge changes from topic 'vrflinger-cleanup'

* changes:
  libbufferhub: Fix native buffer.
  libvrflinger: Move display request to avoid duplicate calls.
  libvrflinger: Remove superfluous update to display manager.
  libvrflinger: Move some tracing to a more verbose level.
  libvrflinger: Add additional info to PDX dump.
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 595fbab..d61cbc9 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -446,7 +446,10 @@
 // Set the default size of cmdline hashtable
 static bool setCmdlineSize()
 {
-    return writeStr(k_traceCmdlineSizePath, "8192");
+    if (fileExists(k_traceCmdlineSizePath)) {
+        return writeStr(k_traceCmdlineSizePath, "8192");
+    }
+    return true;
 }
 
 // Set the clock to the best available option while tracing. Use 'boot' if it's
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 1e0f6f8..aab86c7 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -15,6 +15,8 @@
     chown root shell /sys/kernel/tracing/options/overwrite
     chown root shell /sys/kernel/debug/tracing/options/print-tgid
     chown root shell /sys/kernel/tracing/options/print-tgid
+    chown root shell /sys/kernel/debug/tracing/options/saved_cmdlines_size
+    chown root shell /sys/kernel/tracing/options/saved_cmdlines_size
     chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable
     chown root shell /sys/kernel/tracing/events/sched/sched_switch/enable
     chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
@@ -65,6 +67,8 @@
     chmod 0664 /sys/kernel/tracing/options/overwrite
     chmod 0664 /sys/kernel/debug/tracing/options/print-tgid
     chmod 0664 /sys/kernel/tracing/options/print-tgid
+    chmod 0664 /sys/kernel/debug/tracing/options/saved_cmdlines_size
+    chmod 0664 /sys/kernel/tracing/options/saved_cmdlines_size
     chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable
     chmod 0664 /sys/kernel/tracing/events/sched/sched_switch/enable
     chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index af78bfb..4ecbf92 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1214,7 +1214,7 @@
 // out_vdex_wrapper_fd. Returns true for success or false in case of errors.
 bool open_vdex_files(const char* apk_path, const char* out_oat_path, int dexopt_needed,
         const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
-        Dex2oatFileWrapper* in_vdex_wrapper_fd,
+        bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd,
         Dex2oatFileWrapper* out_vdex_wrapper_fd) {
     CHECK(in_vdex_wrapper_fd != nullptr);
     CHECK(out_vdex_wrapper_fd != nullptr);
@@ -1224,6 +1224,14 @@
     int dexopt_action = abs(dexopt_needed);
     bool is_odex_location = dexopt_needed < 0;
     std::string in_vdex_path_str;
+
+    // Infer the name of the output VDEX.
+    const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
+    if (out_vdex_path_str.empty()) {
+        return false;
+    }
+
+    bool update_vdex_in_place = false;
     if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
         // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
         const char* path = nullptr;
@@ -1242,21 +1250,48 @@
             ALOGE("installd cannot compute input vdex location for '%s'\n", path);
             return false;
         }
-        in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+        // We can update in place when all these conditions are met:
+        // 1) The vdex location to write to is the same as the vdex location to read (vdex files
+        //    on /system typically cannot be updated in place).
+        // 2) We dex2oat due to boot image change, because we then know the existing vdex file
+        //    cannot be currently used by a running process.
+        // 3) We are not doing a profile guided compilation, because dexlayout requires two
+        //    different vdex files to operate.
+        update_vdex_in_place =
+            (in_vdex_path_str == out_vdex_path_str) &&
+            (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) &&
+            !profile_guided;
+        if (update_vdex_in_place) {
+            // Open the file read-write to be able to update it.
+            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
+            if (in_vdex_wrapper_fd->get() == -1) {
+                // If we failed to open the file, we cannot update it in place.
+                update_vdex_in_place = false;
+            }
+        } else {
+            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+        }
     }
 
-    // Infer the name of the output VDEX and create it.
-    const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
-    if (out_vdex_path_str.empty()) {
-        return false;
-    }
-
-    out_vdex_wrapper_fd->reset(
-          open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
-          [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
-    if (out_vdex_wrapper_fd->get() < 0) {
-        ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
-        return false;
+    // If we are updating the vdex in place, we do not need to recreate a vdex,
+    // and can use the same existing one.
+    if (update_vdex_in_place) {
+        // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
+        // have bogus stale vdex files.
+        out_vdex_wrapper_fd->reset(
+              in_vdex_wrapper_fd->get(),
+              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+        // Disable auto close for the in wrapper fd (it will be done when destructing the out
+        // wrapper).
+        in_vdex_wrapper_fd->DisableAutoClose();
+    } else {
+        out_vdex_wrapper_fd->reset(
+              open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
+              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+        if (out_vdex_wrapper_fd->get() < 0) {
+            ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
+            return false;
+        }
     }
     if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
             out_vdex_path_str.c_str(), is_secondary_dex)) {
@@ -1568,7 +1603,7 @@
     Dex2oatFileWrapper in_vdex_fd;
     Dex2oatFileWrapper out_vdex_fd;
     if (!open_vdex_files(dex_path, out_oat_path, dexopt_needed, instruction_set, is_public, uid,
-            is_secondary_dex, &in_vdex_fd, &out_vdex_fd)) {
+            is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
         return -1;
     }
 
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index c2d477e..0a537a3 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -30,7 +30,7 @@
         mName(name), mHandle(0), mType(0),
         mMinValue(0), mMaxValue(0), mResolution(0),
         mPower(0), mMinDelay(0), mVersion(0), mFifoReservedEventCount(0),
-        mFifoMaxEventCount(0), mRequiredAppOp(0),
+        mFifoMaxEventCount(0), mRequiredAppOp(-1),
         mMaxDelay(0), mFlags(0) {
 }
 
@@ -412,6 +412,10 @@
     return (mFlags & SENSOR_FLAG_DYNAMIC_SENSOR) != 0;
 }
 
+bool Sensor::isDataInjectionSupported() const {
+    return (mFlags & SENSOR_FLAG_DATA_INJECTION) != 0;
+}
+
 bool Sensor::hasAdditionalInfo() const {
     return (mFlags & SENSOR_FLAG_ADDITIONAL_INFO) != 0;
 }
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h
index 043e635..6926f7f 100644
--- a/libs/sensor/include/sensor/Sensor.h
+++ b/libs/sensor/include/sensor/Sensor.h
@@ -90,6 +90,7 @@
     uint32_t getFlags() const;
     bool isWakeUpSensor() const;
     bool isDynamicSensor() const;
+    bool isDataInjectionSupported() const;
     bool hasAdditionalInfo() const;
     int32_t getHighestDirectReportRateLevel() const;
     bool isDirectChannelTypeSupported(int32_t sharedMemType) const;
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index a53c8b0..f0ef299 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -442,46 +442,78 @@
   SetupQueue(status.get());
 }
 
-Status<void> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
-                                           uint32_t layer_count,
-                                           uint32_t format, uint64_t usage,
-                                           size_t* out_slot) {
-  if (out_slot == nullptr) {
-    ALOGE("ProducerQueue::AllocateBuffer: Parameter out_slot cannot be null.");
-    return ErrorStatus(EINVAL);
-  }
-
-  if (is_full()) {
-    ALOGE("ProducerQueue::AllocateBuffer queue is at maximum capacity: %zu",
-          capacity());
+Status<std::vector<size_t>> ProducerQueue::AllocateBuffers(
+    uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
+    uint64_t usage, size_t buffer_count) {
+  if (capacity() + buffer_count > kMaxQueueCapacity) {
+    ALOGE(
+        "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot "
+        "allocate %zu more buffer(s).",
+        capacity(), buffer_count);
     return ErrorStatus(E2BIG);
   }
 
-  const size_t kBufferCount = 1u;
   Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
       InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
-          width, height, layer_count, format, usage, kBufferCount);
+          width, height, layer_count, format, usage, buffer_count);
   if (!status) {
-    ALOGE("ProducerQueue::AllocateBuffer failed to create producer buffer: %s",
+    ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s",
           status.GetErrorMessage().c_str());
     return status.error_status();
   }
 
   auto buffer_handle_slots = status.take();
-  LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != kBufferCount,
+  LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count,
                       "BufferHubRPC::ProducerQueueAllocateBuffers should "
-                      "return one and only one buffer handle.");
+                      "return %zu buffer handle(s), but returned %zu instead.",
+                      buffer_count, buffer_handle_slots.size());
 
+  std::vector<size_t> buffer_slots;
+  buffer_slots.reserve(buffer_count);
+
+  // Bookkeeping for each buffer.
+  for (auto& hs : buffer_handle_slots) {
+    auto& buffer_handle = hs.first;
+    size_t buffer_slot = hs.second;
+
+    // Note that import might (though very unlikely) fail. If so, buffer_handle
+    // will be closed and included in returned buffer_slots.
+    if (AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
+                  buffer_slot)) {
+      ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu",
+               buffer_slot);
+      buffer_slots.push_back(buffer_slot);
+    }
+  }
+
+  if (buffer_slots.size() == 0) {
+    // Error out if no buffer is allocated and improted.
+    ALOGE(TRACE, "ProducerQueue::AllocateBuffers: no buffer allocated.");
+    ErrorStatus(ENOMEM);
+  }
+
+  return {std::move(buffer_slots)};
+}
+
+Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
+                                             uint32_t layer_count,
+                                             uint32_t format, uint64_t usage) {
   // We only allocate one buffer at a time.
-  auto& buffer_handle = buffer_handle_slots[0].first;
-  size_t buffer_slot = buffer_handle_slots[0].second;
-  ALOGD_IF(TRACE,
-           "ProducerQueue::AllocateBuffer, new buffer, channel_handle: %d",
-           buffer_handle.value());
+  constexpr size_t buffer_count = 1;
+  auto status =
+      AllocateBuffers(width, height, layer_count, format, usage, buffer_count);
+  if (!status) {
+    ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s",
+          status.GetErrorMessage().c_str());
+    return status.error_status();
+  }
 
-  *out_slot = buffer_slot;
-  return AddBuffer(BufferProducer::Import(std::move(buffer_handle)),
-                   buffer_slot);
+  if (status.get().size() == 0) {
+    ALOGE(TRACE, "ProducerQueue::AllocateBuffer: no buffer allocated.");
+    ErrorStatus(ENOMEM);
+  }
+
+  return {status.get()[0]};
 }
 
 Status<void> ProducerQueue::AddBuffer(
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
index fca5eca..0f75a5c 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_producer.cpp
@@ -612,9 +612,8 @@
                                                 uint32_t layer_count,
                                                 PixelFormat format,
                                                 uint64_t usage) {
-  size_t slot;
   auto status =
-      queue_->AllocateBuffer(width, height, layer_count, format, usage, &slot);
+      queue_->AllocateBuffer(width, height, layer_count, format, usage);
   if (!status) {
     ALOGE(
         "BufferHubQueueProducer::AllocateBuffer: Failed to allocate buffer: %s",
@@ -622,6 +621,7 @@
     return NO_MEMORY;
   }
 
+  size_t slot = status.get();
   auto buffer_producer = queue_->GetBuffer(slot);
 
   LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr,
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 912cee0..d57c7af 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -274,12 +274,20 @@
         BufferHubQueue::GetBuffer(slot));
   }
 
+  // Batch allocate buffers. Once allocated, producer buffers are automatically
+  // enqueue'd into the ProducerQueue and available to use (i.e. in GAINED
+  // state). Upon success, returns a list of slots for each buffer allocated.
+  pdx::Status<std::vector<size_t>> AllocateBuffers(
+      uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
+      uint64_t usage, size_t buffer_count);
+
   // Allocate producer buffer to populate the queue. Once allocated, a producer
   // buffer is automatically enqueue'd into the ProducerQueue and available to
-  // use (i.e. in GAINED state).
-  pdx::Status<void> AllocateBuffer(uint32_t width, uint32_t height,
-                                   uint32_t layer_count, uint32_t format,
-                                   uint64_t usage, size_t* out_slot);
+  // use (i.e. in GAINED state). Upon success, returns the slot number for the
+  // buffer allocated.
+  pdx::Status<size_t> AllocateBuffer(uint32_t width, uint32_t height,
+                                     uint32_t layer_count, uint32_t format,
+                                     uint64_t usage);
 
   // Add a producer buffer to populate the queue. Once added, a producer buffer
   // is available to use (i.e. in GAINED state).
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index 228212e..e0a4052 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -46,12 +46,12 @@
 
   void AllocateBuffer(size_t* slot_out = nullptr) {
     // Create producer buffer.
-    size_t slot;
     auto status = producer_queue_->AllocateBuffer(
         kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-        kBufferUsage, &slot);
-    ASSERT_TRUE(status.ok());
+        kBufferUsage);
 
+    ASSERT_TRUE(status.ok());
+    size_t slot = status.take();
     if (slot_out)
       *slot_out = slot;
   }
@@ -478,13 +478,13 @@
                            UsagePolicy{set_mask, 0, 0, 0}));
 
   // When allocation, leave out |set_mask| from usage bits on purpose.
-  size_t slot;
   auto status = producer_queue_->AllocateBuffer(
       kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage & ~set_mask, &slot);
+      kBufferUsage & ~set_mask);
   ASSERT_TRUE(status.ok());
 
   LocalHandle fence;
+  size_t slot;
   auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
   ASSERT_TRUE(p1_status.ok());
   auto p1 = p1_status.take();
@@ -497,13 +497,13 @@
                            UsagePolicy{0, clear_mask, 0, 0}));
 
   // When allocation, add |clear_mask| into usage bits on purpose.
-  size_t slot;
   auto status = producer_queue_->AllocateBuffer(
       kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage | clear_mask, &slot);
+      kBufferUsage | clear_mask);
   ASSERT_TRUE(status.ok());
 
   LocalHandle fence;
+  size_t slot;
   auto p1_status = producer_queue_->Dequeue(0, &slot, &fence);
   ASSERT_TRUE(p1_status.ok());
   auto p1 = p1_status.take();
@@ -517,16 +517,15 @@
 
   // Now that |deny_set_mask| is illegal, allocation without those bits should
   // be able to succeed.
-  size_t slot;
   auto status = producer_queue_->AllocateBuffer(
       kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage & ~deny_set_mask, &slot);
+      kBufferUsage & ~deny_set_mask);
   ASSERT_TRUE(status.ok());
 
   // While allocation with those bits should fail.
   status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
                                            kBufferLayerCount, kBufferFormat,
-                                           kBufferUsage | deny_set_mask, &slot);
+                                           kBufferUsage | deny_set_mask);
   ASSERT_FALSE(status.ok());
   ASSERT_EQ(EINVAL, status.error());
 }
@@ -538,16 +537,15 @@
 
   // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are
   // mandatory), allocation with those bits should be able to succeed.
-  size_t slot;
   auto status = producer_queue_->AllocateBuffer(
       kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage | deny_clear_mask, &slot);
+      kBufferUsage | deny_clear_mask);
   ASSERT_TRUE(status.ok());
 
   // While allocation without those bits should fail.
   status = producer_queue_->AllocateBuffer(
       kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
-      kBufferUsage & ~deny_clear_mask, &slot);
+      kBufferUsage & ~deny_clear_mask);
   ASSERT_FALSE(status.ok());
   ASSERT_EQ(EINVAL, status.error());
 }
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
index 6e7f556..40c158a 100644
--- a/libs/vr/libdisplay/display_client.cpp
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -145,20 +145,12 @@
   auto producer_queue = status.take();
 
   ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity);
-  for (size_t i = 0; i < capacity; i++) {
-    size_t slot;
-    auto allocate_status = producer_queue->AllocateBuffer(
-        width, height, layer_count, format, usage, &slot);
-    if (!allocate_status) {
-      ALOGE(
-          "Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s",
+  auto allocate_status = producer_queue->AllocateBuffers(
+      width, height, layer_count, format, usage, capacity);
+  if (!allocate_status) {
+    ALOGE("Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s",
           producer_queue->id(), allocate_status.GetErrorMessage().c_str());
-      return allocate_status.error_status();
-    }
-    ALOGD_IF(
-        TRACE,
-        "Surface::CreateQueue: Allocated buffer at slot=%zu of capacity=%zu",
-        slot, capacity);
+    return allocate_status.error_status();
   }
 
   return {std::move(producer_queue)};
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
index 5f35dcf..dc31917 100644
--- a/libs/vr/libdvr/dvr_api.cpp
+++ b/libs/vr/libdvr/dvr_api.cpp
@@ -3,6 +3,8 @@
 #include <errno.h>
 #include <utils/Log.h>
 
+#include <algorithm>
+
 // Headers from libdvr
 #include <dvr/dvr_buffer.h>
 #include <dvr/dvr_buffer_queue.h>
@@ -23,15 +25,20 @@
   ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size,
         version);
   if (version == 1) {
-    if (struct_size != sizeof(DvrApi_v1)) {
-      ALOGE("dvrGetApi: Size mismatch: expected %zu; actual %zu",
-            sizeof(DvrApi_v1), struct_size);
-      return -EINVAL;
-    }
+    // New entry points are added at the end. If the caller's struct and
+    // this library have different sizes, we define the entry points in common.
+    // The caller is expected to handle unset entry points if necessary.
+    size_t clamped_struct_size = std::min(struct_size, sizeof(DvrApi_v1));
     DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api);
 
 // Defines an API entry for V1 (no version suffix).
-#define DVR_V1_API_ENTRY(name) dvr_api->name = dvr##name
+#define DVR_V1_API_ENTRY(name)                                 \
+  do {                                                         \
+    if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \
+        clamped_struct_size) {                                 \
+      dvr_api->name = dvr##name;                               \
+    }                                                          \
+  } while (0)
 
 #include "include/dvr/dvr_api_entries.h"
 
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
index 5d01c8f..aa2ed94 100644
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ b/libs/vr/libdvr/dvr_buffer_queue.cpp
@@ -122,7 +122,7 @@
     }
 
     auto allocate_status = producer_queue_->AllocateBuffer(
-        width_, height_, old_layer_count, format_, old_usage, &slot);
+        width_, height_, old_layer_count, format_, old_usage);
     if (!allocate_status) {
       ALOGE("DvrWriteBufferQueue::Dequeue: Failed to allocate buffer: %s",
             allocate_status.GetErrorMessage().c_str());
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index 0c10907..4693c7f 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -238,6 +238,8 @@
                                           float pressure);
 typedef int (*DvrVirtualTouchpadButtonStatePtr)(DvrVirtualTouchpad* client,
                                                 int touchpad, int buttons);
+typedef int (*DvrVirtualTouchpadScrollPtr)(DvrVirtualTouchpad* client,
+                                           int touchpad, float x, float y);
 
 // dvr_hardware_composer_client.h
 typedef struct DvrHwcClient DvrHwcClient;
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index 802a0f7..ae5ef18 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -141,3 +141,9 @@
 DVR_V1_API_ENTRY(HwcFrameGetLayerVisibleRegion);
 DVR_V1_API_ENTRY(HwcFrameGetLayerNumDamagedRegions);
 DVR_V1_API_ENTRY(HwcFrameGetLayerDamagedRegion);
+
+// New entries added at the end to allow the DVR platform library API
+// to be updated before updating VrCore.
+
+// Virtual touchpad client
+DVR_V1_API_ENTRY(VirtualTouchpadScroll);
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
index f2ae09e..5158612 100644
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
@@ -56,13 +56,10 @@
   }
 
   void AllocateBuffers(size_t buffer_count) {
-    size_t out_slot;
-    for (size_t i = 0; i < buffer_count; i++) {
-      auto status = write_queue_->producer_queue()->AllocateBuffer(
-          kBufferWidth, kBufferHeight, kLayerCount, kBufferFormat, kBufferUsage,
-          &out_slot);
-      ASSERT_TRUE(status.ok());
-    }
+    auto status = write_queue_->producer_queue()->AllocateBuffers(
+        kBufferWidth, kBufferHeight, kLayerCount, kBufferFormat, kBufferUsage,
+        buffer_count);
+    ASSERT_TRUE(status.ok());
   }
 
   void HandleBufferAvailable() {
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index ab08cac..aa306d8 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -124,14 +124,15 @@
     forEachSensor([&result] (const Sensor& s) -> bool {
             result.appendFormat(
                     "%#010x) %-25s | %-15s | ver: %" PRId32 " | type: %20s(%" PRId32
-                        ") | perm: %s\n",
+                        ") | perm: %s | flags: 0x%08x\n",
                     s.getHandle(),
                     s.getName().string(),
                     s.getVendor().string(),
                     s.getVersion(),
                     s.getStringType().string(),
                     s.getType(),
-                    s.getRequiredPermission().size() ? s.getRequiredPermission().string() : "n/a");
+                    s.getRequiredPermission().size() ? s.getRequiredPermission().string() : "n/a",
+                    static_cast<int>(s.getFlags()));
 
             result.append("\t");
             const int reportingMode = s.getReportingMode();
@@ -173,9 +174,14 @@
                 result.appendFormat("non-wakeUp | ");
             }
 
+            if (s.isDataInjectionSupported()) {
+                result.appendFormat("data-injection, ");
+            }
+
             if (s.isDynamicSensor()) {
                 result.appendFormat("dynamic, ");
             }
+
             if (s.hasAdditionalInfo()) {
                 result.appendFormat("has-additional-info, ");
             }
@@ -190,7 +196,6 @@
                 if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC)) {
                     result.append("gralloc, ");
                 }
-                result.appendFormat("flag =0x%08x", static_cast<int>(s.getFlags()));
                 result.append("\n");
             }
             return true;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index edc0140..13472df 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -225,6 +225,9 @@
     property_get("ro.sf.disable_triple_buffer", value, "1");
     mLayerTripleBufferingDisabled = atoi(value);
     ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
+
+    property_get("persist.sys.sf.color_saturation", value, "1.0");
+    mSaturation = atof(value);
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -3846,10 +3849,11 @@
     status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
     if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
         CHECK_INTERFACE(ISurfaceComposer, data, reply);
-        if (CC_UNLIKELY(!PermissionCache::checkCallingPermission(sHardwareTest))) {
-            IPCThreadState* ipc = IPCThreadState::self();
+        IPCThreadState* ipc = IPCThreadState::self();
+        const int uid = ipc->getCallingUid();
+        if (CC_UNLIKELY(uid != AID_SYSTEM
+                && !PermissionCache::checkCallingPermission(sHardwareTest))) {
             const int pid = ipc->getCallingPid();
-            const int uid = ipc->getCallingUid();
             ALOGE("Permission Denial: "
                     "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
             return PERMISSION_DENIED;
diff --git a/services/vr/virtual_touchpad/DvrVirtualTouchpadClient.cpp b/services/vr/virtual_touchpad/DvrVirtualTouchpadClient.cpp
index eb152ed..3ab77a7 100644
--- a/services/vr/virtual_touchpad/DvrVirtualTouchpadClient.cpp
+++ b/services/vr/virtual_touchpad/DvrVirtualTouchpadClient.cpp
@@ -40,6 +40,11 @@
   return FromC(client)->ButtonState(touchpad, buttons);
 }
 
+int dvrVirtualTouchpadScroll(DvrVirtualTouchpad* client, int touchpad, float x,
+                             float y) {
+  return FromC(client)->Scroll(touchpad, x, y);
+}
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/services/vr/virtual_touchpad/EvdevInjector.cpp b/services/vr/virtual_touchpad/EvdevInjector.cpp
index a4ccdd0..7fad379 100644
--- a/services/vr/virtual_touchpad/EvdevInjector.cpp
+++ b/services/vr/virtual_touchpad/EvdevInjector.cpp
@@ -168,6 +168,25 @@
   return ConfigureAbs(ABS_MT_SLOT, 0, slots, 0, 0);
 }
 
+int EvdevInjector::ConfigureRel(uint16_t rel_type) {
+  ALOGV("ConfigureRel 0x%" PRIX16 "", rel_type);
+  if (rel_type < 0 || rel_type >= REL_CNT) {
+    ALOGE("EV_REL type 0x%" PRIX16 " out of range [0,0x%X)", rel_type, REL_CNT);
+    return Error(ERROR_REL_RANGE);
+  }
+  if (const int status = RequireState(State::CONFIGURING)) {
+    return status;
+  }
+  if (const int status = EnableEventType(EV_REL)) {
+    return status;
+  }
+  if (const int status = uinput_->IoctlSetInt(UI_SET_RELBIT, rel_type)) {
+    ALOGE("failed to enable EV_REL 0x%" PRIX16 "", rel_type);
+    return Error(status);
+  }
+  return 0;
+}
+
 int EvdevInjector::ConfigureEnd() {
   ALOGV("ConfigureEnd:");
   ALOGV("  name=\"%s\"", uidev_.name);
@@ -236,6 +255,10 @@
   return Send(EV_ABS, code, value);
 }
 
+int EvdevInjector::SendRel(uint16_t code, int32_t value) {
+  return Send(EV_REL, code, value);
+}
+
 int EvdevInjector::SendMultiTouchSlot(int32_t slot) {
   if (latest_slot_ != slot) {
     if (const int status = SendAbs(ABS_MT_SLOT, slot)) {
diff --git a/services/vr/virtual_touchpad/EvdevInjector.h b/services/vr/virtual_touchpad/EvdevInjector.h
index c69dbef..e87c959 100644
--- a/services/vr/virtual_touchpad/EvdevInjector.h
+++ b/services/vr/virtual_touchpad/EvdevInjector.h
@@ -30,6 +30,7 @@
     ERROR_KEY_RANGE = -3,       // |KEY_*|/|BTN_*| code out of range.
     ERROR_ABS_RANGE = -4,       // |ABS_*| code out of range.
     ERROR_SEQUENCING = -5,      // Configure/Send out of order.
+    ERROR_REL_RANGE = -6,       // |REL_*| code out of range.
   };
 
   // Key event |value| is not defined in <linux/input.h>.
@@ -87,6 +88,10 @@
   // Configure multitouch coordinate range.
   int ConfigureMultiTouchXY(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
 
+  // Configure a relative axis.
+  // @param rel_type One of the |REL_*| constants from <linux/input.h>.
+  int ConfigureRel(uint16_t rel_type);
+
   // Complete configuration and create the input device.
   int ConfigureEnd();
 
@@ -96,6 +101,7 @@
   int SendSynReport();
   int SendKey(uint16_t code, int32_t value);
   int SendAbs(uint16_t code, int32_t value);
+  int SendRel(uint16_t code, int32_t value);
   int SendMultiTouchSlot(int32_t slot);
   int SendMultiTouchXY(int32_t slot, int32_t id, int32_t x, int32_t y);
   int SendMultiTouchLift(int32_t slot);
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
index c7c8184..00e4ce6 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadClient.cpp
@@ -60,6 +60,13 @@
     return service_->buttonState(touchpad, buttons).transactionError();
   }
 
+  status_t Scroll(int touchpad, float x, float y) override {
+    if (service_ == nullptr) {
+      return NO_INIT;
+    }
+    return service_->scroll(touchpad, x, y).transactionError();
+  }
+
   void dumpInternal(String8& result) override {
     result.append("[virtual touchpad]\n");
     result.appendFormat("connected = %s\n\n",
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
index f0bdcd9..ae56bf6 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
@@ -28,6 +28,22 @@
 static constexpr int32_t kHeight = 0x10000;
 static constexpr int32_t kSlots = 2;
 
+int32_t scale_relative_scroll(float x) {
+  // Guilty with an explanation, your honor.
+  // Ideally we should be able to communicate the full incoming precision
+  // to InputFlinger, through the evdev int32_t value, by scaling by a
+  // large factor, i.e. 2²³ for IEEE single precision floating point.
+  // However, although InputFlinger has |wheelVelocityControlParameters|,
+  // those parameters are currently hard coded, with a scale factor of 1.0.
+  // The observed evdev value for a physical mouse scroll wheel is usually
+  // ±1, with higher values up to ±4 for a very fast spin. So we imitate
+  // that. If the incoming value is not actually 0, the resulting magnitude
+  // should be at least 1, so that small movements are not lost.
+  // Adding IDC configurability of |VelocityControlParameters| may be
+  // desirable in the future.
+  return copysignf(ceilf(fabs(4.0f * x)), x);
+}
+
 }  // anonymous namespace
 
 std::unique_ptr<VirtualTouchpad> VirtualTouchpadEvdev::Create() {
@@ -162,6 +178,32 @@
   return touchpad.injector->GetError();
 }
 
+int VirtualTouchpadEvdev::Scroll(int touchpad_id, float x, float y) {
+  if (touchpad_id < 0 || touchpad_id >= kTouchpads) {
+    return EINVAL;
+  }
+  if ((x < -1.0f) || (x > 1.0f) || (y < -1.0f) || (y > 1.0f)) {
+    return EINVAL;
+  }
+  Touchpad& touchpad = touchpad_[touchpad_id];
+  if (!touchpad.injector) {
+    return EvdevInjector::ERROR_SEQUENCING;
+  }
+  touchpad.injector->ResetError();
+  const int32_t scaled_x = scale_relative_scroll(x);
+  const int32_t scaled_y = scale_relative_scroll(y);
+  if (scaled_x) {
+    touchpad.injector->SendRel(REL_HWHEEL, scaled_x);
+  }
+  if (scaled_y) {
+    touchpad.injector->SendRel(REL_WHEEL, scaled_y);
+  }
+  if (scaled_x || scaled_y) {
+    touchpad.injector->SendSynReport();
+  }
+  return touchpad.injector->GetError();
+}
+
 void VirtualTouchpadEvdev::dumpInternal(String8& result) {
   for (int i = 0; i < kTouchpads; ++i) {
     const auto& touchpad = touchpad_[i];
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
index 2fb8ff3..c9578bf 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.h
@@ -21,6 +21,7 @@
   status_t Detach() override;
   status_t Touch(int touchpad, float x, float y, float pressure) override;
   status_t ButtonState(int touchpad, int buttons) override;
+  status_t Scroll(int touchpad, float x, float y) override;
   void dumpInternal(String8& result) override;
 
  protected:
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
index 81edd32..523f890 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -87,6 +87,16 @@
   return binder::Status::ok();
 }
 
+binder::Status VirtualTouchpadService::scroll(int touchpad, float x, float y) {
+  if (!CheckPermissions()) {
+    return binder::Status::fromStatusT(PERMISSION_DENIED);
+  }
+  if (const status_t error = touchpad_->Scroll(touchpad, x, y)) {
+    return binder::Status::fromStatusT(error);
+  }
+  return binder::Status::ok();
+}
+
 status_t VirtualTouchpadService::dump(
     int fd, const Vector<String16>& args[[gnu::unused]]) {
   String8 result;
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.h b/services/vr/virtual_touchpad/VirtualTouchpadService.h
index cf236f9..2c46209 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.h
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.h
@@ -23,6 +23,7 @@
   binder::Status detach() override;
   binder::Status touch(int touchpad, float x, float y, float pressure) override;
   binder::Status buttonState(int touchpad, int buttons) override;
+  binder::Status scroll(int touchpad, float x, float y) override;
 
   // Implements BBinder::dump().
   status_t dump(int fd, const Vector<String16>& args) override;
diff --git a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
index 9cfb186..256203c 100644
--- a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
@@ -34,4 +34,15 @@
    * @param buttons A union of MotionEvent BUTTON_* values.
    */
   void buttonState(int touchpad, int buttons) = 3;
+
+  /**
+   * Generate a simulated scroll event.
+   *
+   * @param touchpad Selects touchpad.
+   * @param x Horizontal scroll increment.
+   * @param y Vertical scroll increment.
+   *
+   * Scroll values are in the range [-1.0, 1.0].
+   */
+  void scroll(int touchpad, float x, float y) = 4;
 }
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpad.h b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
index da3a0b7..99b72fc 100644
--- a/services/vr/virtual_touchpad/include/VirtualTouchpad.h
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpad.h
@@ -61,6 +61,16 @@
   //
   virtual status_t ButtonState(int touchpad, int buttons) = 0;
 
+  // Generate a simulated scroll event.
+  //
+  // @param touchpad Touchpad selector index.
+  // @param x Horizontal scroll increment.
+  // @param y Vertical scroll increment.
+  //            Values must be in the range [-1.0, 1.0].
+  // @returns OK on success.
+  //
+  virtual status_t Scroll(int touchpad, float x, float y) = 0;
+
   // Report state for 'dumpsys'.
   virtual void dumpInternal(String8& result) = 0;
 
diff --git a/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
index 23fb9f8..7d73f06 100644
--- a/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
+++ b/services/vr/virtual_touchpad/include/VirtualTouchpadClient.h
@@ -17,6 +17,7 @@
   status_t Detach() override;
   status_t Touch(int touchpad, float x, float y, float pressure) override;
   status_t ButtonState(int touchpad, int buttons) override;
+  status_t Scroll(int touchpad, float x, float y) override;
   void dumpInternal(String8& result) override;
 
  protected:
diff --git a/services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h b/services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
index 15e6687..09fb1cc 100644
--- a/services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
+++ b/services/vr/virtual_touchpad/include/dvr/virtual_touchpad_client.h
@@ -61,6 +61,17 @@
 int dvrVirtualTouchpadButtonState(DvrVirtualTouchpad* client, int touchpad,
                                   int buttons);
 
+// Generate a simulated scroll event.
+//
+// @param client Pointer to the virtual touchpad client.
+// @param touchpad Selects touchpad.
+// @param x Horizontal scroll increment.
+// @param y Vertical scroll increment.
+// @return Zero on success, status_t-style error code on failure.
+//
+int dvrVirtualTouchpadScroll(DvrVirtualTouchpad* client, int touchpad, float x,
+                             float y);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif