drm_hwcomposer: Implement SetActiveConfigWithConstraints

Enough to get 100% passes in Composer 2.4 VTS.
Some SOCs require a VTS fix to pass [1]

[1]: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1954544
Signed-off-by: Roman Stratiienko <roman.o.stratiienko@globallogic.com>
diff --git a/hwc2_device/DrmHwcTwo.cpp b/hwc2_device/DrmHwcTwo.cpp
index 37901ee..f1a5490 100644
--- a/hwc2_device/DrmHwcTwo.cpp
+++ b/hwc2_device/DrmHwcTwo.cpp
@@ -174,6 +174,11 @@
       vsync_2_4_callback_ = std::make_pair(HWC2_PFN_VSYNC_2_4(function), data);
       break;
     }
+    case HWC2::Callback::VsyncPeriodTimingChanged: {
+      period_timing_changed_callback_ = std::
+          make_pair(HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED(function), data);
+      break;
+    }
 #endif
     default:
       break;
@@ -220,4 +225,21 @@
   }
 }
 
+void DrmHwcTwo::SendVsyncPeriodTimingChangedEventToClient(
+    [[maybe_unused]] hwc2_display_t displayid,
+    [[maybe_unused]] int64_t timestamp) const {
+#if PLATFORM_SDK_VERSION > 29
+  hwc_vsync_period_change_timeline_t timeline = {
+      .newVsyncAppliedTimeNanos = timestamp,
+      .refreshRequired = false,
+      .refreshTimeNanos = 0,
+  };
+  if (period_timing_changed_callback_.first != nullptr &&
+      period_timing_changed_callback_.second != nullptr) {
+    period_timing_changed_callback_
+        .first(period_timing_changed_callback_.second, displayid, &timeline);
+  }
+#endif
+}
+
 }  // namespace android
diff --git a/hwc2_device/DrmHwcTwo.h b/hwc2_device/DrmHwcTwo.h
index 5379cba..0e72251 100644
--- a/hwc2_device/DrmHwcTwo.h
+++ b/hwc2_device/DrmHwcTwo.h
@@ -33,6 +33,8 @@
   std::pair<HWC2_PFN_VSYNC, hwc2_callback_data_t> vsync_callback_{};
 #if PLATFORM_SDK_VERSION > 29
   std::pair<HWC2_PFN_VSYNC_2_4, hwc2_callback_data_t> vsync_2_4_callback_{};
+  std::pair<HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED, hwc2_callback_data_t>
+      period_timing_changed_callback_{};
 #endif
   std::pair<HWC2_PFN_REFRESH, hwc2_callback_data_t> refresh_callback_{};
 
@@ -66,6 +68,8 @@
 
   void SendVsyncEventToClient(hwc2_display_t displayid, int64_t timestamp,
                               uint32_t vsync_period) const;
+  void SendVsyncPeriodTimingChangedEventToClient(hwc2_display_t displayid,
+                                                 int64_t timestamp) const;
 
  private:
   void SendHotplugEventToClient(hwc2_display_t displayid, bool connected);
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 656aa94..d7b753a 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -141,7 +141,10 @@
     if (vsync_flattening_en_) {
       ProcessFlatenningVsyncInternal();
     }
-    if (!vsync_event_en_ && !vsync_flattening_en_) {
+    if (vsync_tracking_en_) {
+      last_vsync_ts_ = timestamp;
+    }
+    if (!vsync_event_en_ && !vsync_flattening_en_ && !vsync_tracking_en_) {
       vsync_worker_.VSyncControl(false);
     }
   });
@@ -215,10 +218,10 @@
 }
 
 HWC2::Error HwcDisplay::GetActiveConfig(hwc2_config_t *config) const {
-  if (configs_.hwc_configs.count(configs_.active_config_id) == 0)
+  if (configs_.hwc_configs.count(staged_mode_config_id_) == 0)
     return HWC2::Error::BadConfig;
 
-  *config = configs_.active_config_id;
+  *config = staged_mode_config_id_;
   return HWC2::Error::None;
 }
 
@@ -321,7 +324,7 @@
     case HWC2::Attribute::ConfigGroup:
       /* Dispite ConfigGroup is a part of HWC2.4 API, framework
        * able to request it even if service @2.1 is used */
-      *value = hwc_config.group_id;
+      *value = int(hwc_config.group_id);
       break;
 #endif
     default:
@@ -436,6 +439,26 @@
     return HWC2::Error::None;
   }
 
+  int PrevModeVsyncPeriodNs = static_cast<int>(
+      1E9 / GetPipe().connector->Get()->GetActiveMode().v_refresh());
+
+  auto mode_update_commited_ = false;
+  if (staged_mode_ &&
+      staged_mode_change_time_ <= ResourceManager::GetTimeMonotonicNs()) {
+    client_layer_.SetLayerDisplayFrame(
+        (hwc_rect_t){.left = 0,
+                     .top = 0,
+                     .right = static_cast<int>(staged_mode_->h_display()),
+                     .bottom = static_cast<int>(staged_mode_->v_display())});
+
+    configs_.active_config_id = staged_mode_config_id_;
+
+    a_args.display_mode = *staged_mode_;
+    if (!a_args.test_only) {
+      mode_update_commited_ = true;
+    }
+  }
+
   // order the layers by z-order
   bool use_client_layer = false;
   uint32_t client_z_order = UINT32_MAX;
@@ -498,9 +521,6 @@
   }
 
   a_args.composition = composition;
-  if (staged_mode) {
-    a_args.display_mode = *staged_mode;
-  }
   ret = GetPipe().compositor->ExecuteAtomicCommit(a_args);
 
   if (ret) {
@@ -509,8 +529,13 @@
     return HWC2::Error::BadParameter;
   }
 
-  if (!a_args.test_only) {
-    staged_mode.reset();
+  if (mode_update_commited_) {
+    staged_mode_.reset();
+    vsync_tracking_en_ = false;
+    if (last_vsync_ts_ != 0) {
+      hwc2_->SendVsyncPeriodTimingChangedEventToClient(
+          handle_, last_vsync_ts_ + PrevModeVsyncPeriodNs);
+    }
   }
 
   return HWC2::Error::None;
@@ -548,30 +573,24 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::SetActiveConfig(hwc2_config_t config) {
-  int conf = static_cast<int>(config);
-
-  if (configs_.hwc_configs.count(conf) == 0) {
-    ALOGE("Could not find active mode for %d", conf);
+HWC2::Error HwcDisplay::SetActiveConfigInternal(uint32_t config,
+                                                int64_t change_time) {
+  if (configs_.hwc_configs.count(config) == 0) {
+    ALOGE("Could not find active mode for %u", config);
     return HWC2::Error::BadConfig;
   }
 
-  auto &mode = configs_.hwc_configs[conf].mode;
-
-  staged_mode = mode;
-
-  configs_.active_config_id = conf;
-
-  // Setup the client layer's dimensions
-  hwc_rect_t display_frame = {.left = 0,
-                              .top = 0,
-                              .right = static_cast<int>(mode.h_display()),
-                              .bottom = static_cast<int>(mode.v_display())};
-  client_layer_.SetLayerDisplayFrame(display_frame);
+  staged_mode_ = configs_.hwc_configs[config].mode;
+  staged_mode_change_time_ = change_time;
+  staged_mode_config_id_ = config;
 
   return HWC2::Error::None;
 }
 
+HWC2::Error HwcDisplay::SetActiveConfig(hwc2_config_t config) {
+  return SetActiveConfigInternal(config, ResourceManager::GetTimeMonotonicNs());
+}
+
 /* Find API details at:
  * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1861
  */
@@ -736,14 +755,37 @@
 }
 
 HWC2::Error HwcDisplay::SetActiveConfigWithConstraints(
-    hwc2_config_t /*config*/,
+    hwc2_config_t config,
     hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints,
     hwc_vsync_period_change_timeline_t *outTimeline) {
   if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) {
     return HWC2::Error::BadParameter;
   }
 
-  return HWC2::Error::BadConfig;
+  uint32_t current_vsync_period{};
+  GetDisplayVsyncPeriod(&current_vsync_period);
+
+  if (vsyncPeriodChangeConstraints->seamlessRequired) {
+    return HWC2::Error::SeamlessNotAllowed;
+  }
+
+  outTimeline->refreshTimeNanos = vsyncPeriodChangeConstraints
+                                      ->desiredTimeNanos -
+                                  current_vsync_period;
+  auto ret = SetActiveConfigInternal(config, outTimeline->refreshTimeNanos);
+  if (ret != HWC2::Error::None) {
+    return ret;
+  }
+
+  outTimeline->refreshRequired = true;
+  outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeConstraints
+                                              ->desiredTimeNanos;
+
+  last_vsync_ts_ = 0;
+  vsync_tracking_en_ = true;
+  vsync_worker_.VSyncControl(true);
+
+  return HWC2::Error::None;
 }
 
 HWC2::Error HwcDisplay::SetAutoLowLatencyMode(bool /*on*/) {
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index 5a5c98e..e7ce7ef 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -195,7 +195,9 @@
 
   DrmHwcTwo *const hwc2_;
 
-  std::optional<DrmMode> staged_mode;
+  std::optional<DrmMode> staged_mode_;
+  int64_t staged_mode_change_time_{};
+  uint32_t staged_mode_config_id_{};
 
   DrmDisplayPipeline *const pipeline_;
 
@@ -204,6 +206,8 @@
   VSyncWorker vsync_worker_;
   bool vsync_event_en_{};
   bool vsync_flattening_en_{};
+  bool vsync_tracking_en_{};
+  int64_t last_vsync_ts_{};
 
   const hwc2_display_t handle_;
   HWC2::DisplayType type_;
@@ -223,6 +227,8 @@
   std::string DumpDelta(HwcDisplay::Stats delta);
 
   HWC2::Error Init();
+
+  HWC2::Error SetActiveConfigInternal(uint32_t config, int64_t change_time);
 };
 
 }  // namespace android
diff --git a/hwc2_device/HwcDisplayConfigs.cpp b/hwc2_device/HwcDisplayConfigs.cpp
index c28ec9f..6a3ed5a 100644
--- a/hwc2_device/HwcDisplayConfigs.cpp
+++ b/hwc2_device/HwcDisplayConfigs.cpp
@@ -32,7 +32,7 @@
 namespace android {
 
 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
-int HwcDisplayConfigs::last_config_id = 1;
+uint32_t HwcDisplayConfigs::last_config_id = 1;
 
 void HwcDisplayConfigs::FillHeadless() {
   hwc_configs.clear();
@@ -77,15 +77,15 @@
   mm_height = connector.GetMmHeight();
 
   preferred_config_id = 0;
-  int preferred_config_group_id = 0;
+  uint32_t preferred_config_group_id = 0;
 
-  int first_config_id = last_config_id;
-  int last_group_id = 1;
+  uint32_t first_config_id = last_config_id;
+  uint32_t last_group_id = 1;
 
   /* Group modes */
   for (const auto &mode : connector.GetModes()) {
     /* Find group for the new mode or create new group */
-    int group_found = 0;
+    uint32_t group_found = 0;
     for (auto &hwc_config : hwc_configs) {
       if (mode.h_display() == hwc_config.second.mode.h_display() &&
           mode.v_display() == hwc_config.second.mode.v_display()) {
@@ -128,7 +128,7 @@
     preferred_config_group_id = 1;
   }
 
-  for (int group = 1; group < last_group_id; group++) {
+  for (uint32_t group = 1; group < last_group_id; group++) {
     bool has_interlaced = false;
     bool has_progressive = false;
     for (auto &hwc_config : hwc_configs) {
@@ -179,8 +179,8 @@
    * otherwise android.graphics.cts.SetFrameRateTest CTS will fail
    */
   constexpr float kMinFpsDelta = 1.0;  // FPS
-  for (int m1 = first_config_id; m1 < last_config_id; m1++) {
-    for (int m2 = first_config_id; m2 < last_config_id; m2++) {
+  for (uint32_t m1 = first_config_id; m1 < last_config_id; m1++) {
+    for (uint32_t m2 = first_config_id; m2 < last_config_id; m2++) {
       if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
           !hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
           fabsf(hwc_configs[m1].mode.v_refresh() -
diff --git a/hwc2_device/HwcDisplayConfigs.h b/hwc2_device/HwcDisplayConfigs.h
index 75852a6..7c173d6 100644
--- a/hwc2_device/HwcDisplayConfigs.h
+++ b/hwc2_device/HwcDisplayConfigs.h
@@ -28,8 +28,8 @@
 class DrmConnector;
 
 struct HwcDisplayConfig {
-  int id{};
-  int group_id{};
+  uint32_t id{};
+  uint32_t group_id{};
   DrmMode mode;
   bool disabled{};
 
@@ -42,13 +42,13 @@
   HWC2::Error Update(DrmConnector &conn);
   void FillHeadless();
 
-  std::map<int /*config_id*/, struct HwcDisplayConfig> hwc_configs;
+  std::map<uint32_t /*config_id*/, struct HwcDisplayConfig> hwc_configs;
 
-  int active_config_id = 0;
-  int preferred_config_id = 0;
+  uint32_t active_config_id = 0;
+  uint32_t preferred_config_id = 0;
 
   // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
-  static int last_config_id;
+  static uint32_t last_config_id;
 
   uint32_t mm_width = 0;
   uint32_t mm_height = 0;