Add shmem config buffer to libvrflinger

This allows VrCore to configure some tunable behaviors
of libvrflinger.

- Added dvrDisplayManagerDeleteNamedBuffer for testing config buffer
  creation.
- Added tests for named buffers, including one for the config buffer.
- Added IsValid to broadcast_ring to avoid tracking redundant state
  externally.

Bug: 38193993
Test: Run dvr_named_buffer-test
Change-Id: I52722dd314233b5bea1ca6377c14b5c856825746
diff --git a/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h b/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h
index 69cb648..236e3aa 100644
--- a/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h
+++ b/libs/vr/libbroadcastring/include/libbroadcastring/broadcast_ring.h
@@ -348,6 +348,9 @@
     return Get(sequence, record);
   }
 
+  // Returns true if this instance has been created or imported.
+  bool is_valid() const { return !!data_.mmap; }
+
   uint32_t record_count() const { return record_count_internal(); }
   uint32_t record_size() const { return record_size_internal(); }
   static constexpr uint32_t mmap_alignment() { return alignof(Mmap); }
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
index 47c4599..37d6ff9 100644
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ b/libs/vr/libdisplay/display_manager_client.cpp
@@ -49,8 +49,8 @@
   const int ret = native_buffer_handle.Import(ion_buffer.get());
   if (ret < 0) {
     ALOGE(
-        "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
-        "key=%d; error=%s",
+        "DisplayManagerClient::GetGlobalBuffer: Failed to import global "
+        "buffer: key=%d; error=%s",
         key, strerror(-ret));
     return ErrorStatus(-ret);
   }
@@ -58,6 +58,18 @@
   return {std::move(ion_buffer)};
 }
 
+pdx::Status<void> DisplayManagerClient::DeleteGlobalBuffer(
+    DvrGlobalBufferKey key) {
+  auto status =
+      InvokeRemoteMethod<DisplayManagerProtocol::DeleteGlobalBuffer>(key);
+  if (!status) {
+    ALOGE("DisplayManagerClient::DeleteGlobalBuffer Failed: %s",
+          status.GetErrorMessage().c_str());
+  }
+
+  return status;
+}
+
 pdx::Status<std::string> DisplayManagerClient::GetConfigurationData(
     ConfigFileType config_type) {
   auto status =
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
index 8bf12b6..c5ec231 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
@@ -23,6 +23,7 @@
   pdx::Status<std::vector<SurfaceState>> GetSurfaceState();
   pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer(
       DvrGlobalBufferKey key, size_t size, uint64_t usage);
+  pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
   pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id,
                                                               int queue_id);
 
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
index b22ddfa..b857d48 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
@@ -235,6 +235,7 @@
     kOpGetSurfaceQueue,
     kOpSetupGlobalBuffer,
     kOpGetConfigurationData,
+    kOpDeleteGlobalBuffer,
   };
 
   // Aliases.
@@ -251,6 +252,8 @@
                                             uint64_t usage));
   PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData,
                     std::string(ConfigFileType config_type));
+  PDX_REMOTE_METHOD(DeleteGlobalBuffer, kOpDeleteGlobalBuffer,
+                    void(DvrGlobalBufferKey key));
 };
 
 struct VSyncSchedInfo {
diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp
index e67e8c3..ffe090d 100644
--- a/libs/vr/libdvr/dvr_display_manager.cpp
+++ b/libs/vr/libdvr/dvr_display_manager.cpp
@@ -133,6 +133,23 @@
   return 0;
 }
 
+int dvrDisplayManagerDeleteGlobalBuffer(DvrDisplayManager* client,
+                                        DvrGlobalBufferKey key) {
+  if (!client)
+    return -EINVAL;
+
+  auto buffer_status = client->client->DeleteGlobalBuffer(key);
+  if (!buffer_status) {
+    ALOGE(
+        "dvrDisplayManagerDeleteGlobalBuffer: Failed to delete named buffer: "
+        "%s",
+        buffer_status.GetErrorMessage().c_str());
+    return -buffer_status.error();
+  }
+
+  return 0;
+}
+
 int dvrConfigurationDataGet(DvrDisplayManager* client, int config_type,
                             uint8_t** data, size_t* data_size) {
   if (!client || !data || !data_size) {
diff --git a/libs/vr/libdvr/include/dvr/dvr_display_manager.h b/libs/vr/libdvr/include/dvr/dvr_display_manager.h
index f8db182..eb1e711 100644
--- a/libs/vr/libdvr/include/dvr/dvr_display_manager.h
+++ b/libs/vr/libdvr/include/dvr/dvr_display_manager.h
@@ -32,6 +32,14 @@
                                        DvrGlobalBufferKey key, size_t size,
                                        uint64_t usage, DvrBuffer** buffer_out);
 
+// Deletes a named buffer. WARNING: This is dangerous because any existing
+// clients of this buffer will not be notified and will remain attached to
+// the old buffer. This is useful for tests, but probably not for production
+// code.
+// @return 0 on success. Otherwise returns a negative error value.
+int dvrDisplayManagerDeleteGlobalBuffer(DvrDisplayManager* client,
+                                        DvrGlobalBufferKey key);
+
 // Device metrics data type enums.
 enum {
   DVR_CONFIGURATION_DATA_LENS_METRICS = 0,
diff --git a/libs/vr/libdvr/include/dvr/dvr_vrflinger_config_buffer.h b/libs/vr/libdvr/include/dvr/dvr_vrflinger_config_buffer.h
new file mode 100644
index 0000000..108c78b
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_vrflinger_config_buffer.h
@@ -0,0 +1,51 @@
+#ifndef ANDROID_DVR_VRFLINGER_CONFIG_BUFFER_H
+#define ANDROID_DVR_VRFLINGER_CONFIG_BUFFER_H
+
+#include <libbroadcastring/broadcast_ring.h>
+
+// This header is shared by VrCore and Android and must be kept in sync.
+
+namespace android {
+namespace dvr {
+
+// Increment when the layout for the buffers change.
+constexpr uint32_t kSharedConfigBufferLayoutVersion = 1;
+
+// This is a shared memory buffer for passing config data from VrCore to
+// libvrflinger in SurfaceFlinger.
+struct DvrVrFlingerConfigBuffer {
+  // Offset before vsync to submit frames to hardware composer.
+  int frame_post_offset_ns{4000000};
+
+  // If the number of pending fences goes over this count at the point when we
+  // are about to submit a new frame to HWC, we will drop the frame. This
+  // should be a signal that the display driver has begun queuing frames. Note
+  // that with smart displays (with RAM), the fence is signaled earlier than
+  // the next vsync, at the point when the DMA to the display completes.
+  // Currently we use a smart display and the EDS timing coincides with zero
+  // pending fences, so this is 0.
+  size_t allowed_pending_fence_count{0};
+
+  // New fields should always be added to the end for backwards compat.
+};
+
+class DvrVrFlingerConfigBufferTraits {
+ public:
+  using Record = DvrVrFlingerConfigBuffer;
+  static constexpr bool kUseStaticRecordSize = false;
+  static constexpr uint32_t kStaticRecordCount = 2;
+  static constexpr int kMaxReservedRecords = 1;
+  static constexpr int kMinAvailableRecords = 1;
+};
+
+// The broadcast ring classes that will expose the data.
+using DvrVrFlingerConfigRing =
+    BroadcastRing<DvrVrFlingerConfigBuffer, DvrVrFlingerConfigBufferTraits>;
+
+// Common buffers.
+constexpr int kVrFlingerConfigBufferKey = 5;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VRFLINGER_CONFIG_BUFFER_H
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index ef746e2..ab2ee75 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -32,6 +32,7 @@
     "libdvrcommon",
     "libdisplay",
     "libpdx_default_transport",
+    "libbroadcastring",
 ]
 
 cc_test {
diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
index 336f11f..cf04588 100644
--- a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
@@ -2,6 +2,7 @@
 #include <dvr/dvr_buffer.h>
 #include <dvr/dvr_display_manager.h>
 #include <dvr/dvr_surface.h>
+#include <dvr/dvr_vrflinger_config_buffer.h>
 #include <system/graphics.h>
 
 #include <base/logging.h>
@@ -12,7 +13,7 @@
 
 namespace {
 
-class DvrNamedBufferTest : public ::testing::Test {
+class DvrGlobalBufferTest : public ::testing::Test {
  protected:
   void SetUp() override {
     const int ret = dvrDisplayManagerCreate(&client_);
@@ -28,7 +29,7 @@
   DvrDisplayManager* client_ = nullptr;
 };
 
-TEST_F(DvrNamedBufferTest, TestNamedBuffersSameName) {
+TEST_F(DvrGlobalBufferTest, TestGlobalBuffersSameName) {
   const DvrGlobalBufferKey buffer_key = 101;
   DvrBuffer* buffer1 = nullptr;
   int ret1 =
@@ -95,7 +96,7 @@
   AHardwareBuffer_release(hardware_buffer3);
 }
 
-TEST_F(DvrNamedBufferTest, TestMultipleNamedBuffers) {
+TEST_F(DvrGlobalBufferTest, TestMultipleGlobalBuffers) {
   const DvrGlobalBufferKey buffer_key1 = 102;
   const DvrGlobalBufferKey buffer_key2 = 103;
   DvrBuffer* setup_buffer1 = nullptr;
@@ -125,7 +126,7 @@
   dvrBufferDestroy(buffer2);
 }
 
-TEST_F(DvrNamedBufferTest, TestNamedBufferUsage) {
+TEST_F(DvrGlobalBufferTest, TestGlobalBufferUsage) {
   const DvrGlobalBufferKey buffer_key = 100;
 
   // Set usage to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. We use this because
@@ -154,6 +155,153 @@
   AHardwareBuffer_release(hardware_buffer);
 }
 
+TEST_F(DvrGlobalBufferTest, TestGlobalBufferCarriesData) {
+  const DvrGlobalBufferKey buffer_name = 110;
+
+  uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                   AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+  constexpr size_t size = 1024 * sizeof(uint64_t);
+  constexpr uint64_t value = 0x123456787654321;
+
+  {
+    // Allocate some data and set it to something.
+    DvrBuffer* setup_buffer = nullptr;
+    int e1 = dvrDisplayManagerSetupGlobalBuffer(client_, buffer_name, size,
+                                                usage, &setup_buffer);
+    ASSERT_NE(nullptr, setup_buffer);
+    ASSERT_EQ(0, e1);
+
+    AHardwareBuffer* hardware_buffer = nullptr;
+    int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
+    ASSERT_EQ(0, e2);
+    ASSERT_NE(nullptr, hardware_buffer);
+
+    void* buffer;
+    int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
+    ASSERT_EQ(0, e3);
+    ASSERT_NE(nullptr, buffer);
+    // Verify that the buffer pointer is at least 16 byte aligned.
+    ASSERT_EQ(0, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
+
+    uint64_t* data = static_cast<uint64_t*>(buffer);
+    constexpr size_t num_values = size / sizeof(uint64_t);
+    for (size_t i = 0; i < num_values; ++i) {
+      data[i] = value;
+    }
+
+    int32_t fence = -1;
+    int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
+    ASSERT_EQ(0, e4);
+
+    dvrBufferDestroy(setup_buffer);
+    AHardwareBuffer_release(hardware_buffer);
+  }
+
+  {
+    // Get the buffer and check that all the data is still present.
+    DvrBuffer* setup_buffer = nullptr;
+    int e1 = dvrGetGlobalBuffer(buffer_name, &setup_buffer);
+    ASSERT_NE(nullptr, setup_buffer);
+    ASSERT_EQ(0, e1);
+
+    AHardwareBuffer* hardware_buffer = nullptr;
+    int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
+    ASSERT_EQ(0, e2);
+    ASSERT_NE(nullptr, hardware_buffer);
+
+    void* buffer;
+    int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
+    ASSERT_EQ(0, e3);
+    ASSERT_NE(nullptr, buffer);
+    // Verify that the buffer pointer is at least 16 byte aligned.
+    ASSERT_EQ(0, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
+
+    uint64_t* data = static_cast<uint64_t*>(buffer);
+    constexpr size_t num_values = size / sizeof(uint64_t);
+    bool is_equal = true;
+    for (size_t i = 0; i < num_values; ++i) {
+      is_equal &= (data[i] == value);
+    }
+    ASSERT_TRUE(is_equal);
+
+    int32_t fence = -1;
+    int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
+    ASSERT_EQ(0, e4);
+
+    dvrBufferDestroy(setup_buffer);
+    AHardwareBuffer_release(hardware_buffer);
+  }
+}
+
+TEST_F(DvrGlobalBufferTest, TestGlobalBufferZeroed) {
+  const DvrGlobalBufferKey buffer_name = 120;
+
+  // Allocate 1MB and check that it is all zeros.
+  uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                   AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+  constexpr size_t size = 1024 * 1024;
+  DvrBuffer* setup_buffer = nullptr;
+  int e1 = dvrDisplayManagerSetupGlobalBuffer(client_, buffer_name, size, usage,
+                                              &setup_buffer);
+  ASSERT_NE(nullptr, setup_buffer);
+  ASSERT_EQ(0, e1);
+
+  AHardwareBuffer* hardware_buffer = nullptr;
+  int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
+  ASSERT_EQ(0, e2);
+  ASSERT_NE(nullptr, hardware_buffer);
+
+  void* buffer;
+  int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
+  ASSERT_EQ(0, e3);
+  ASSERT_NE(nullptr, buffer);
+  // Verify that the buffer pointer is at least 16 byte aligned.
+  ASSERT_EQ(0, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
+
+  uint64_t* data = static_cast<uint64_t*>(buffer);
+  constexpr size_t num_values = size / sizeof(uint64_t);
+  uint64_t zero = 0;
+  for (size_t i = 0; i < num_values; ++i) {
+    zero |= data[i];
+  }
+  ASSERT_EQ(0, zero);
+
+  int32_t fence = -1;
+  int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
+  ASSERT_EQ(0, e4);
+
+  dvrBufferDestroy(setup_buffer);
+  AHardwareBuffer_release(hardware_buffer);
+}
+
+TEST_F(DvrGlobalBufferTest, TestVrflingerConfigBuffer) {
+  const DvrGlobalBufferKey buffer_name = kVrFlingerConfigBufferKey;
+
+  // First delete any existing buffer so we can test the failure case.
+  dvrDisplayManagerDeleteGlobalBuffer(client_, buffer_name);
+
+  const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
+                         AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY;
+
+  size_t correct_size = DvrVrFlingerConfigRing::MemorySize();
+  size_t wrong_size = DvrVrFlingerConfigRing::MemorySize(0);
+
+  // Setup an invalid config buffer (too small) and assert that it fails.
+  DvrBuffer* setup_buffer = nullptr;
+  int e1 = dvrDisplayManagerSetupGlobalBuffer(client_, buffer_name, wrong_size,
+                                              usage, &setup_buffer);
+  ASSERT_EQ(nullptr, setup_buffer);
+  ASSERT_GT(0, e1);
+
+  // Setup a correct config buffer.
+  int e2 = dvrDisplayManagerSetupGlobalBuffer(
+      client_, buffer_name, correct_size, usage, &setup_buffer);
+  ASSERT_NE(nullptr, setup_buffer);
+  ASSERT_EQ(0, e2);
+
+  dvrBufferDestroy(setup_buffer);
+}
+
 }  // namespace
 
 }  // namespace dvr
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index d37031f..080479a 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -36,6 +36,7 @@
     "libvrsensor",
     "libpdx_default_transport",
     "libvr_manager",
+    "libbroadcastring",
 ]
 
 sharedLibraries = [
@@ -77,7 +78,7 @@
         "-DEGL_EGLEXT_PROTOTYPES",
     ],
     shared_libs: sharedLibraries,
-    header_libs: headerLibraries,
     whole_static_libs: staticLibraries,
+    header_libs: headerLibraries,
     name: "libvrflinger",
 }
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
index 7b5aa79..c5b0d88 100644
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ b/libs/vr/libvrflinger/display_manager_service.cpp
@@ -98,6 +98,11 @@
           *this, &DisplayManagerService::OnGetConfigurationData, message);
       return {};
 
+    case DisplayManagerProtocol::DeleteGlobalBuffer::Opcode:
+      DispatchRemoteMethod<DisplayManagerProtocol::DeleteGlobalBuffer>(
+          *this, &DisplayManagerService::OnDeleteGlobalBuffer, message);
+      return {};
+
     default:
       return Service::DefaultHandleMessage(message);
   }
@@ -161,6 +166,19 @@
   return display_service_->SetupGlobalBuffer(key, size, usage);
 }
 
+pdx::Status<void> DisplayManagerService::OnDeleteGlobalBuffer(
+    pdx::Message& message, DvrGlobalBufferKey key) {
+  const int user_id = message.GetEffectiveUserId();
+  const bool trusted = (user_id == AID_ROOT) || IsTrustedUid(user_id);
+
+  if (!trusted) {
+    ALOGE("DisplayService::DeleteGlobalBuffer: Untrusted user_id (%d)",
+          user_id);
+    return ErrorStatus(EPERM);
+  }
+  return display_service_->DeleteGlobalBuffer(key);
+}
+
 pdx::Status<std::string> DisplayManagerService::OnGetConfigurationData(
     pdx::Message& message, display::ConfigFileType config_type) {
   std::string property_name;
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
index 4a08405..20c5507 100644
--- a/libs/vr/libvrflinger/display_manager_service.h
+++ b/libs/vr/libvrflinger/display_manager_service.h
@@ -59,6 +59,8 @@
   pdx::Status<BorrowedNativeBufferHandle> OnSetupGlobalBuffer(
       pdx::Message& message, DvrGlobalBufferKey key, size_t size,
       uint64_t usage);
+  pdx::Status<void> OnDeleteGlobalBuffer(pdx::Message& message,
+                                         DvrGlobalBufferKey key);
   pdx::Status<std::string> OnGetConfigurationData(
       pdx::Message& message, display::ConfigFileType config_type);
 
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index 4cc5f02..b180848 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -227,6 +227,12 @@
   if (global_buffer == global_buffers_.end()) {
     auto ion_buffer = std::make_unique<IonBuffer>(static_cast<int>(size), 1,
                                                   HAL_PIXEL_FORMAT_BLOB, usage);
+
+    // Some buffers are used internally. If they were configured with an
+    // invalid size or format, this will fail.
+    int result = hardware_composer_.OnNewGlobalBuffer(key, *ion_buffer.get());
+    if (result < 0)
+      return ErrorStatus(result);
     global_buffer =
         global_buffers_.insert(std::make_pair(key, std::move(ion_buffer)))
             .first;
@@ -235,6 +241,17 @@
   return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
 }
 
+pdx::Status<void> DisplayService::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
+  auto global_buffer = global_buffers_.find(key);
+  if (global_buffer != global_buffers_.end()) {
+    // Some buffers are used internally.
+    hardware_composer_.OnDeletedGlobalBuffer(key);
+    global_buffers_.erase(global_buffer);
+  }
+
+  return {0};
+}
+
 void DisplayService::OnHardwareComposerRefresh() {
   hardware_composer_.OnHardwareComposerRefresh();
 }
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index 77e45a4..8ba1728 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -44,6 +44,8 @@
   pdx::Status<BorrowedNativeBufferHandle> SetupGlobalBuffer(
       DvrGlobalBufferKey key, size_t size, uint64_t usage);
 
+  pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
+
   template <class A>
   void ForEachDisplaySurface(SurfaceType surface_type, A action) const {
     ForEachChannel([surface_type,
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 34474d9..88173ca 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -19,6 +19,7 @@
 #include <chrono>
 #include <functional>
 #include <map>
+#include <tuple>
 
 #include <dvr/dvr_display_types.h>
 #include <dvr/performance_client_api.h>
@@ -37,17 +38,6 @@
 
 namespace {
 
-// If the number of pending fences goes over this count at the point when we
-// are about to submit a new frame to HWC, we will drop the frame. This should
-// be a signal that the display driver has begun queuing frames. Note that with
-// smart displays (with RAM), the fence is signaled earlier than the next vsync,
-// at the point when the DMA to the display completes. Currently we use a smart
-// display and the EDS timing coincides with zero pending fences, so this is 0.
-constexpr int kAllowedPendingFenceCount = 0;
-
-// Offset before vsync to submit frames to hardware composer.
-constexpr int64_t kFramePostOffsetNs = 4000000;  // 4ms
-
 const char kBacklightBrightnessSysFile[] =
     "/sys/class/leds/lcd-backlight/brightness";
 
@@ -397,8 +387,8 @@
   }
 
   const bool is_frame_pending = IsFramePendingInDriver();
-  const bool is_fence_pending =
-      retire_fence_fds_.size() > kAllowedPendingFenceCount;
+  const bool is_fence_pending = retire_fence_fds_.size() >
+                                post_thread_config_.allowed_pending_fence_count;
 
   if (is_fence_pending || is_frame_pending) {
     ATRACE_INT("frame_skip_count", ++frame_skip_count_);
@@ -475,6 +465,62 @@
     request_display_callback_(!display_idle);
 }
 
+int HardwareComposer::OnNewGlobalBuffer(DvrGlobalBufferKey key,
+                                        IonBuffer& ion_buffer) {
+  if (key == kVrFlingerConfigBufferKey) {
+    return MapConfigBuffer(ion_buffer);
+  }
+
+  return 0;
+}
+
+void HardwareComposer::OnDeletedGlobalBuffer(DvrGlobalBufferKey key) {
+  if (key == kVrFlingerConfigBufferKey) {
+    ConfigBufferDeleted();
+  }
+}
+
+int HardwareComposer::MapConfigBuffer(IonBuffer& ion_buffer) {
+  std::lock_guard<std::mutex> lock(shared_config_mutex_);
+  shared_config_ring_ = DvrVrFlingerConfigRing();
+
+  if (ion_buffer.width() < DvrVrFlingerConfigRing::MemorySize()) {
+    ALOGE("HardwareComposer::MapConfigBuffer: invalid buffer size.");
+    return -EINVAL;
+  }
+
+  void* buffer_base = 0;
+  int result = ion_buffer.Lock(ion_buffer.usage(), 0, 0, ion_buffer.width(),
+                               ion_buffer.height(), &buffer_base);
+  if (result != 0) {
+    ALOGE("HardwareComposer::MapConfigBuffer: Failed to map vrflinger config "
+          "buffer.");
+    return -EPERM;
+  }
+
+  shared_config_ring_ =
+      DvrVrFlingerConfigRing::Create(buffer_base, ion_buffer.width());
+  ion_buffer.Unlock();
+
+  return 0;
+}
+
+void HardwareComposer::ConfigBufferDeleted() {
+  std::lock_guard<std::mutex> lock(shared_config_mutex_);
+  shared_config_ring_ = DvrVrFlingerConfigRing();
+}
+
+void HardwareComposer::UpdateConfigBuffer() {
+  std::lock_guard<std::mutex> lock(shared_config_mutex_);
+  if (!shared_config_ring_.is_valid())
+    return;
+  // Copy from latest record in shared_config_ring_ to local copy.
+  DvrVrFlingerConfigBuffer record;
+  if (shared_config_ring_.GetNewest(&shared_config_ring_sequence_, &record)) {
+    post_thread_config_ = record;
+  }
+}
+
 int HardwareComposer::PostThreadPollInterruptible(
     const pdx::LocalHandle& event_fd, int requested_events) {
   pollfd pfd[2] = {
@@ -744,6 +790,9 @@
   while (1) {
     ATRACE_NAME("HardwareComposer::PostThread");
 
+    // Check for updated config once per vsync.
+    UpdateConfigBuffer();
+
     while (post_thread_quiescent_) {
       std::unique_lock<std::mutex> lock(post_thread_mutex_);
       ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
@@ -823,9 +872,10 @@
 
       const int64_t display_time_est_ns = vsync_timestamp + ns_per_frame;
       const int64_t now_ns = GetSystemClockNs();
-      const int64_t sleep_time_ns =
-          display_time_est_ns - now_ns - kFramePostOffsetNs;
-      const int64_t wakeup_time_ns = display_time_est_ns - kFramePostOffsetNs;
+      const int64_t sleep_time_ns = display_time_est_ns - now_ns -
+                                    post_thread_config_.frame_post_offset_ns;
+      const int64_t wakeup_time_ns =
+          display_time_est_ns - post_thread_config_.frame_post_offset_ns;
 
       ATRACE_INT64("sleep_time_ns", sleep_time_ns);
       if (sleep_time_ns > 0) {
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index 8ba72ab..c182bf9 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -16,6 +16,7 @@
 #include <tuple>
 #include <vector>
 
+#include <dvr/dvr_vrflinger_config_buffer.h>
 #include <dvr/pose_client.h>
 #include <pdx/file_handle.h>
 #include <pdx/rpc/variant.h>
@@ -284,6 +285,9 @@
   void SetDisplaySurfaces(
       std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces);
 
+  int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer);
+  void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
+
   void OnHardwareComposerRefresh();
 
  private:
@@ -365,6 +369,13 @@
   // Called on the post thread when the post thread is paused or quits.
   void OnPostThreadPaused();
 
+  // Map the given shared memory buffer to our broadcast ring to track updates
+  // to the config parameters.
+  int MapConfigBuffer(IonBuffer& ion_buffer);
+  void ConfigBufferDeleted();
+  // Poll for config udpates.
+  void UpdateConfigBuffer();
+
   bool initialized_;
 
   // Hardware composer HAL device from SurfaceFlinger. VrFlinger does not own
@@ -439,6 +450,13 @@
   // out to display frame boundaries, so we need to tell it about vsyncs.
   DvrPose* pose_client_ = nullptr;
 
+  // Broadcast ring for receiving config data from the DisplayManager.
+  DvrVrFlingerConfigRing shared_config_ring_;
+  uint32_t shared_config_ring_sequence_{0};
+  // Config buffer for reading from the post thread.
+  DvrVrFlingerConfigBuffer post_thread_config_;
+  std::mutex shared_config_mutex_;
+
   static constexpr int kPostThreadInterrupted = 1;
 
   static void HwcRefresh(hwc2_callback_data_t data, hwc2_display_t display);