drm_hwcomposer: Merge remote-tracking branch 'aosp/upstream-main' into HEAD am: 774e5e3432 am: 24ccb88ece am: 42f5da5192

Original change: https://android-review.googlesource.com/c/platform/external/drm_hwcomposer/+/1921343

Change-Id: Iae3684f162cd9e86a0feca58480a2dbcf3f450fe
diff --git a/.ci/.common.sh b/.ci/.common.sh
index 21c2b28..48cc594 100644
--- a/.ci/.common.sh
+++ b/.ci/.common.sh
@@ -4,7 +4,7 @@
 CLANG_TIDY="clang-tidy-12"
 
 CXXARGS="-fPIC -Wall -Werror -DPLATFORM_SDK_VERSION=30 -D__ANDROID_API__=30 -Wsign-promo -Wimplicit-fallthrough"
-CXXARGS+=" -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Wno-gnu-include-next "
+CXXARGS+=" -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS -Wno-gnu-include-next -Wsign-compare"
 CXXARGS+=" -fvisibility-inlines-hidden -std=gnu++17 -DHWC2_USE_CPP11 -DHWC2_INCLUDE_STRINGIFICATION -fno-rtti"
 
 BUILD_FILES=(
@@ -27,13 +27,13 @@
 drm/DrmCrtc.cpp
 drm/DrmDevice.cpp
 drm/DrmEncoder.cpp
-drm/DrmEventListener.cpp
 drm/DrmFbImporter.cpp
 drm/DrmMode.cpp
 drm/DrmPlane.cpp
 drm/DrmProperty.cpp
 DrmHwcTwo.cpp
 drm/ResourceManager.cpp
+drm/UEventListener.cpp
 drm/VSyncWorker.cpp
 tests/worker_test.cpp
 utils/autolock.cpp
diff --git a/Android.bp b/Android.bp
index a4d8e18..ae2f690 100644
--- a/Android.bp
+++ b/Android.bp
@@ -95,9 +95,8 @@
     vendor: true,
 }
 
-cc_library_static {
-    name: "drm_hwcomposer",
-    defaults: ["hwcomposer.drm_defaults"],
+filegroup {
+    name: "drm_hwcomposer_common",
     srcs: [
         "DrmHwcTwo.cpp",
 
@@ -112,12 +111,12 @@
         "drm/DrmCrtc.cpp",
         "drm/DrmDevice.cpp",
         "drm/DrmEncoder.cpp",
-        "drm/DrmEventListener.cpp",
         "drm/DrmFbImporter.cpp",
         "drm/DrmMode.cpp",
         "drm/DrmPlane.cpp",
         "drm/DrmProperty.cpp",
         "drm/ResourceManager.cpp",
+        "drm/UEventListener.cpp",
         "drm/VSyncWorker.cpp",
 
         "utils/autolock.cpp",
@@ -130,19 +129,30 @@
     ],
 }
 
+// Kept only for compatibility with older Android version. Please do not use!
+cc_library_static {
+    name: "drm_hwcomposer",
+    defaults: ["hwcomposer.drm_defaults"],
+    srcs: [":drm_hwcomposer_common"],
+}
+
 cc_library_shared {
     name: "hwcomposer.drm",
     defaults: ["hwcomposer.drm_defaults"],
-    whole_static_libs: ["drm_hwcomposer"],
-    srcs: ["bufferinfo/legacy/BufferInfoLibdrm.cpp"],
+    srcs: [
+        ":drm_hwcomposer_common",
+        "bufferinfo/legacy/BufferInfoLibdrm.cpp",
+    ],
+    cflags: ["-DUSE_IMAPPER4_METADATA_API"],
 }
 
 cc_library_shared {
     name: "hwcomposer.drm_minigbm",
     defaults: ["hwcomposer.drm_defaults"],
-    whole_static_libs: ["drm_hwcomposer"],
-    srcs: ["bufferinfo/legacy/BufferInfoMinigbm.cpp"],
-    include_dirs: ["external/minigbm/cros_gralloc"],
+    srcs: [
+        ":drm_hwcomposer_common",
+        "bufferinfo/legacy/BufferInfoMinigbm.cpp",
+    ],
 }
 
 // Used by hwcomposer.drm_imagination
diff --git a/DrmHwcTwo.cpp b/DrmHwcTwo.cpp
index 6ec8b31..9bf1327 100644
--- a/DrmHwcTwo.cpp
+++ b/DrmHwcTwo.cpp
@@ -87,11 +87,9 @@
     }
   }
 
-  const auto &drm_devices = resource_manager_.getDrmDevices();
-  for (const auto &device : drm_devices) {
-    // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
-    device->RegisterHotplugHandler(new DrmHotplugHandler(this, device.get()));
-  }
+  resource_manager_.GetUEventListener()->RegisterHotplugHandler(
+      [this] { HandleHotplugUEvent(); });
+
   return ret;
 }
 
@@ -253,7 +251,8 @@
 }
 
 void DrmHwcTwo::HwcDisplay::ClearDisplay() {
-  compositor_.ClearDisplay();
+  AtomicCommitArgs a_args = {.clear_active_composition = true};
+  compositor_.ExecuteAtomicCommit(a_args);
 }
 
 HWC2::Error DrmHwcTwo::HwcDisplay::Init(std::vector<DrmPlane *> *planes) {
@@ -342,6 +341,8 @@
     return HWC2::Error::BadDisplay;
   }
 
+  client_layer_.SetLayerBlendMode(HWC2_BLEND_MODE_PREMULTIPLIED);
+
   return ChosePreferredConfig();
 }
 
@@ -350,9 +351,9 @@
   uint32_t num_configs = 0;
   HWC2::Error err = GetDisplayConfigs(&num_configs, nullptr);
   if (err != HWC2::Error::None || !num_configs)
-    return err;
+    return HWC2::Error::BadDisplay;
 
-  return SetActiveConfig(connector_->get_preferred_mode_id());
+  return SetActiveConfig(preferred_config_id_);
 }
 
 HWC2::Error DrmHwcTwo::HwcDisplay::AcceptDisplayChanges() {
@@ -379,13 +380,13 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error DrmHwcTwo::HwcDisplay::GetActiveConfig(hwc2_config_t *config) {
+HWC2::Error DrmHwcTwo::HwcDisplay::GetActiveConfig(
+    hwc2_config_t *config) const {
   supported(__func__);
-  DrmMode const &mode = connector_->active_mode();
-  if (mode.id() == 0)
+  if (hwc_configs_.count(active_config_id_) == 0)
     return HWC2::Error::BadConfig;
 
-  *config = mode.id();
+  *config = active_config_id_;
   return HWC2::Error::None;
 }
 
@@ -444,46 +445,47 @@
                                                        int32_t attribute_in,
                                                        int32_t *value) {
   supported(__func__);
-  auto mode = std::find_if(connector_->modes().begin(),
-                           connector_->modes().end(),
-                           [config](DrmMode const &m) {
-                             return m.id() == config;
-                           });
-  if (mode == connector_->modes().end()) {
-    ALOGE("Could not find active mode for %d", config);
+  int conf = static_cast<int>(config);
+
+  if (hwc_configs_.count(conf) == 0) {
+    ALOGE("Could not find active mode for %d", conf);
     return HWC2::Error::BadConfig;
   }
 
+  auto &hwc_config = hwc_configs_[conf];
+
   static const int32_t kUmPerInch = 25400;
   uint32_t mm_width = connector_->mm_width();
   uint32_t mm_height = connector_->mm_height();
   auto attribute = static_cast<HWC2::Attribute>(attribute_in);
   switch (attribute) {
     case HWC2::Attribute::Width:
-      *value = static_cast<int>(mode->h_display());
+      *value = static_cast<int>(hwc_config.mode.h_display());
       break;
     case HWC2::Attribute::Height:
-      *value = static_cast<int>(mode->v_display());
+      *value = static_cast<int>(hwc_config.mode.v_display());
       break;
     case HWC2::Attribute::VsyncPeriod:
       // in nanoseconds
-      *value = static_cast<int>(1E9 / mode->v_refresh());
+      *value = static_cast<int>(1E9 / hwc_config.mode.v_refresh());
       break;
     case HWC2::Attribute::DpiX:
       // Dots per 1000 inches
-      *value = mm_width
-                   ? static_cast<int>(mode->h_display() * kUmPerInch / mm_width)
-                   : -1;
+      *value = mm_width ? static_cast<int>(hwc_config.mode.h_display() *
+                                           kUmPerInch / mm_width)
+                        : -1;
       break;
     case HWC2::Attribute::DpiY:
       // Dots per 1000 inches
-      *value = mm_height ? static_cast<int>(mode->v_display() * kUmPerInch /
-                                            mm_height)
+      *value = mm_height ? static_cast<int>(hwc_config.mode.v_display() *
+                                            kUmPerInch / mm_height)
                          : -1;
       break;
 #if PLATFORM_SDK_VERSION > 29
     case HWC2::Attribute::ConfigGroup:
-      *value = 0; /* TODO: Add support for config groups */
+      /* 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;
       break;
 #endif
     default:
@@ -507,79 +509,149 @@
       ALOGE("Failed to update display modes %d", ret);
       return HWC2::Error::BadDisplay;
     }
-  }
 
-  // Since the upper layers only look at vactive/hactive/refresh, height and
-  // width, it doesn't differentiate interlaced from progressive and other
-  // similar modes. Depending on the order of modes we return to SF, it could
-  // end up choosing a suboptimal configuration and dropping the preferred
-  // mode. To workaround this, don't offer interlaced modes to SF if there is
-  // at least one non-interlaced alternative and only offer a single WxH@R
-  // mode with at least the prefered mode from in DrmConnector::UpdateModes()
+    hwc_configs_.clear();
+    preferred_config_id_ = 0;
+    int preferred_config_group_id_ = 0;
 
-  // TODO(nobody): Remove the following block of code until AOSP handles all
-  // modes
-  std::vector<DrmMode> sel_modes;
-
-  // Add the preferred mode first to be sure it's not dropped
-  auto mode = std::find_if(connector_->modes().begin(),
-                           connector_->modes().end(), [&](DrmMode const &m) {
-                             return m.id() ==
-                                    connector_->get_preferred_mode_id();
-                           });
-  if (mode != connector_->modes().end())
-    sel_modes.push_back(*mode);
-
-  // Add the active mode if different from preferred mode
-  if (connector_->active_mode().id() != connector_->get_preferred_mode_id())
-    sel_modes.push_back(connector_->active_mode());
-
-  // Cycle over the modes and filter out "similar" modes, keeping only the
-  // first ones in the order given by DRM (from CEA ids and timings order)
-  for (const DrmMode &mode : connector_->modes()) {
-    // TODO(nobody): Remove this when 3D Attributes are in AOSP
-    if (mode.flags() & DRM_MODE_FLAG_3D_MASK)
-      continue;
-
-    // TODO(nobody): Remove this when the Interlaced attribute is in AOSP
-    if (mode.flags() & DRM_MODE_FLAG_INTERLACE) {
-      auto m = std::find_if(connector_->modes().begin(),
-                            connector_->modes().end(),
-                            [&mode](DrmMode const &m) {
-                              return !(m.flags() & DRM_MODE_FLAG_INTERLACE) &&
-                                     m.h_display() == mode.h_display() &&
-                                     m.v_display() == mode.v_display();
-                            });
-      if (m == connector_->modes().end())
-        sel_modes.push_back(mode);
-
-      continue;
+    if (connector_->modes().empty()) {
+      ALOGE("No modes reported by KMS");
+      return HWC2::Error::BadDisplay;
     }
 
-    // Search for a similar WxH@R mode in the filtered list and drop it if
-    // another mode with the same WxH@R has already been selected
-    // TODO(nobody): Remove this when AOSP handles duplicates modes
-    auto m = std::find_if(sel_modes.begin(), sel_modes.end(),
-                          [&mode](DrmMode const &m) {
-                            return m.h_display() == mode.h_display() &&
-                                   m.v_display() == mode.v_display() &&
-                                   m.v_refresh() == mode.v_refresh();
-                          });
-    if (m == sel_modes.end())
-      sel_modes.push_back(mode);
-  }
+    int last_config_id = 1;
+    int last_group_id = 1;
 
-  auto num_modes = static_cast<uint32_t>(sel_modes.size());
-  if (!configs) {
-    *num_configs = num_modes;
-    return HWC2::Error::None;
+    /* Group modes */
+    for (const auto &mode : connector_->modes()) {
+      /* Find group for the new mode or create new group */
+      int 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()) {
+          group_found = hwc_config.second.group_id;
+        }
+      }
+      if (group_found == 0) {
+        group_found = last_group_id++;
+      }
+
+      bool disabled = false;
+      if (mode.flags() & DRM_MODE_FLAG_3D_MASK) {
+        ALOGI("Disabling display mode %s (Modes with 3D flag aren't supported)",
+              mode.name().c_str());
+        disabled = true;
+      }
+
+      /* Add config */
+      hwc_configs_[last_config_id] = {
+          .id = last_config_id,
+          .group_id = group_found,
+          .mode = mode,
+          .disabled = disabled,
+      };
+
+      /* Chwck if the mode is preferred */
+      if ((mode.type() & DRM_MODE_TYPE_PREFERRED) != 0 &&
+          preferred_config_id_ == 0) {
+        preferred_config_id_ = last_config_id;
+        preferred_config_group_id_ = group_found;
+      }
+
+      last_config_id++;
+    }
+
+    /* We must have preferred mode. Set first mode as preferred
+     * in case KMS haven't reported anything. */
+    if (preferred_config_id_ == 0) {
+      preferred_config_id_ = 1;
+      preferred_config_group_id_ = 1;
+    }
+
+    for (int group = 1; group < last_group_id; group++) {
+      bool has_interlaced = false;
+      bool has_progressive = false;
+      for (auto &hwc_config : hwc_configs_) {
+        if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
+          continue;
+        }
+
+        if (hwc_config.second.IsInterlaced()) {
+          has_interlaced = true;
+        } else {
+          has_progressive = true;
+        }
+      }
+
+      bool has_both = has_interlaced && has_progressive;
+      if (!has_both) {
+        continue;
+      }
+
+      bool group_contains_preferred_interlaced = false;
+      if (group == preferred_config_group_id_ &&
+          hwc_configs_[preferred_config_id_].IsInterlaced()) {
+        group_contains_preferred_interlaced = true;
+      }
+
+      for (auto &hwc_config : hwc_configs_) {
+        if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
+          continue;
+        }
+
+        bool disable = group_contains_preferred_interlaced
+                           ? !hwc_config.second.IsInterlaced()
+                           : hwc_config.second.IsInterlaced();
+
+        if (disable) {
+          ALOGI(
+              "Group %i: Disabling display mode %s (This group should consist "
+              "of %s modes)",
+              group, hwc_config.second.mode.name().c_str(),
+              group_contains_preferred_interlaced ? "interlaced"
+                                                  : "progressive");
+
+          hwc_config.second.disabled = true;
+        }
+      }
+    }
+
+    /* Group should not contain 2 modes with FPS delta less than ~1HZ
+     * otherwise android.graphics.cts.SetFrameRateTest CTS will fail
+     */
+    for (int m1 = 1; m1 < last_config_id; m1++) {
+      for (int m2 = 1; 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() -
+                  hwc_configs_[m2].mode.v_refresh()) < 1.0) {
+          ALOGI(
+              "Group %i: Disabling display mode %s (Refresh rate value is "
+              "too close to existing mode %s)",
+              hwc_configs_[m2].group_id, hwc_configs_[m2].mode.name().c_str(),
+              hwc_configs_[m1].mode.name().c_str());
+
+          hwc_configs_[m2].disabled = true;
+        }
+      }
+    }
   }
 
   uint32_t idx = 0;
-  for (const DrmMode &mode : sel_modes) {
-    if (idx >= *num_configs)
-      break;
-    configs[idx++] = mode.id();
+  for (auto &hwc_config : hwc_configs_) {
+    if (hwc_config.second.disabled) {
+      continue;
+    }
+
+    if (configs != nullptr) {
+      if (idx >= *num_configs) {
+        break;
+      }
+      configs[idx] = hwc_config.second.id;
+    }
+
+    idx++;
   }
   *num_configs = idx;
   return HWC2::Error::None;
@@ -659,20 +731,7 @@
   return HWC2::Error::None;
 }
 
-void DrmHwcTwo::HwcDisplay::AddFenceToPresentFence(UniqueFd fd) {
-  if (!fd) {
-    return;
-  }
-
-  if (present_fence_) {
-    present_fence_ = UniqueFd(
-        sync_merge("dc_present", present_fence_.Get(), fd.Get()));
-  } else {
-    present_fence_ = std::move(fd);
-  }
-}
-
-HWC2::Error DrmHwcTwo::HwcDisplay::CreateComposition(bool test) {
+HWC2::Error DrmHwcTwo::HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
   // order the layers by z-order
   bool use_client_layer = false;
   uint32_t client_z_order = UINT32_MAX;
@@ -711,12 +770,12 @@
     composition_layers.emplace_back(std::move(layer));
   }
 
-  auto composition = std::make_unique<DrmDisplayComposition>(crtc_,
+  auto composition = std::make_shared<DrmDisplayComposition>(crtc_,
                                                              planner_.get());
 
   // TODO(nobody): Don't always assume geometry changed
   int ret = composition->SetLayers(composition_layers.data(),
-                                   composition_layers.size(), true);
+                                   composition_layers.size());
   if (ret) {
     ALOGE("Failed to set layers in the composition ret=%d", ret);
     return HWC2::Error::BadLayer;
@@ -740,14 +799,11 @@
     i = overlay_planes.erase(i);
   }
 
-  if (test) {
-    ret = compositor_.TestComposition(composition.get());
-  } else {
-    ret = compositor_.ApplyComposition(std::move(composition));
-    AddFenceToPresentFence(compositor_.TakeOutFence());
-  }
+  a_args.composition = composition;
+  ret = compositor_.ExecuteAtomicCommit(a_args);
+
   if (ret) {
-    if (!test)
+    if (!a_args.test_only)
       ALOGE("Failed to apply the frame composition ret=%d", ret);
     return HWC2::Error::BadParameter;
   }
@@ -763,7 +819,9 @@
 
   ++total_stats_.total_frames_;
 
-  ret = CreateComposition(false);
+  AtomicCommitArgs a_args{};
+  ret = CreateComposition(a_args);
+
   if (ret != HWC2::Error::None)
     ++total_stats_.failed_kms_present_;
 
@@ -775,7 +833,7 @@
   if (ret != HWC2::Error::None)
     return ret;
 
-  *present_fence = present_fence_.Release();
+  *present_fence = a_args.out_fence.Release();
 
   ++frame_no_;
   return HWC2::Error::None;
@@ -783,35 +841,34 @@
 
 HWC2::Error DrmHwcTwo::HwcDisplay::SetActiveConfig(hwc2_config_t config) {
   supported(__func__);
-  auto mode = std::find_if(connector_->modes().begin(),
-                           connector_->modes().end(),
-                           [config](DrmMode const &m) {
-                             return m.id() == config;
-                           });
-  if (mode == connector_->modes().end()) {
-    ALOGE("Could not find active mode for %d", config);
+
+  int conf = static_cast<int>(config);
+
+  if (hwc_configs_.count(conf) == 0) {
+    ALOGE("Could not find active mode for %d", conf);
     return HWC2::Error::BadConfig;
   }
 
-  auto composition = std::make_unique<DrmDisplayComposition>(crtc_,
-                                                             planner_.get());
-  int ret = composition->SetDisplayMode(*mode);
-  if (ret) {
-    return HWC2::Error::BadConfig;
-  }
-  ret = compositor_.ApplyComposition(std::move(composition));
-  if (ret) {
-    ALOGE("Failed to queue dpms composition on %d", ret);
+  auto &mode = hwc_configs_[conf].mode;
+
+  AtomicCommitArgs a_args = {
+      .display_mode = mode,
+      .clear_active_composition = true,
+  };
+
+  int err = compositor_.ExecuteAtomicCommit(a_args);
+  if (err != 0) {
+    ALOGE("Failed to queue mode changing commit %d", err);
     return HWC2::Error::BadConfig;
   }
 
-  connector_->set_active_mode(*mode);
+  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())};
+                              .right = static_cast<int>(mode.h_display()),
+                              .bottom = static_cast<int>(mode.v_display())};
   client_layer_.SetLayerDisplayFrame(display_frame);
 
   return HWC2::Error::None;
@@ -883,14 +940,15 @@
 
 HWC2::Error DrmHwcTwo::HwcDisplay::SetPowerMode(int32_t mode_in) {
   supported(__func__);
-  uint64_t dpms_value = 0;
   auto mode = static_cast<HWC2::PowerMode>(mode_in);
+  AtomicCommitArgs a_args{};
+
   switch (mode) {
     case HWC2::PowerMode::Off:
-      dpms_value = DRM_MODE_DPMS_OFF;
+      a_args.active = false;
       break;
     case HWC2::PowerMode::On:
-      dpms_value = DRM_MODE_DPMS_ON;
+      a_args.active = true;
       break;
     case HWC2::PowerMode::Doze:
     case HWC2::PowerMode::DozeSuspend:
@@ -900,12 +958,9 @@
       return HWC2::Error::BadParameter;
   };
 
-  auto composition = std::make_unique<DrmDisplayComposition>(crtc_,
-                                                             planner_.get());
-  composition->SetDpmsMode(dpms_value);
-  int ret = compositor_.ApplyComposition(std::move(composition));
-  if (ret) {
-    ALOGE("Failed to apply the dpms composition ret=%d", ret);
+  int err = compositor_.ExecuteAtomicCommit(a_args);
+  if (err) {
+    ALOGE("Failed to apply the dpms composition err=%d", err);
     return HWC2::Error::BadParameter;
   }
   return HWC2::Error::None;
@@ -956,12 +1011,8 @@
 HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayVsyncPeriod(
     hwc2_vsync_period_t *outVsyncPeriod /* ns */) {
   supported(__func__);
-  DrmMode const &mode = connector_->active_mode();
-  if (mode.id() == 0)
-    return HWC2::Error::BadConfig;
-
-  *outVsyncPeriod = static_cast<hwc2_vsync_period_t>(1E9 / mode.v_refresh());
-  return HWC2::Error::None;
+  return GetDisplayAttribute(active_config_id_, HWC2_ATTRIBUTE_VSYNC_PERIOD,
+                             (int32_t *)(outVsyncPeriod));
 }
 
 HWC2::Error DrmHwcTwo::HwcDisplay::SetActiveConfigWithConstraints(
@@ -1279,30 +1330,32 @@
   }
 }
 
-void DrmHwcTwo::DrmHotplugHandler::HandleEvent(uint64_t timestamp_us) {
-  for (const auto &conn : drm_->connectors()) {
-    drmModeConnection old_state = conn->state();
-    drmModeConnection cur_state = conn->UpdateModes()
-                                      ? DRM_MODE_UNKNOWNCONNECTION
-                                      : conn->state();
+void DrmHwcTwo::HandleHotplugUEvent() {
+  for (const auto &drm : resource_manager_.getDrmDevices()) {
+    for (const auto &conn : drm->connectors()) {
+      drmModeConnection old_state = conn->state();
+      drmModeConnection cur_state = conn->UpdateModes()
+                                        ? DRM_MODE_UNKNOWNCONNECTION
+                                        : conn->state();
 
-    if (cur_state == old_state)
-      continue;
+      if (cur_state == old_state)
+        continue;
 
-    ALOGI("%s event @%" PRIu64 " for connector %u on display %d",
-          cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", timestamp_us,
-          conn->id(), conn->display());
+      ALOGI("%s event for connector %u on display %d",
+            cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", conn->id(),
+            conn->display());
 
-    int display_id = conn->display();
-    if (cur_state == DRM_MODE_CONNECTED) {
-      auto &display = hwc2_->displays_.at(display_id);
-      display.ChosePreferredConfig();
-    } else {
-      auto &display = hwc2_->displays_.at(display_id);
-      display.ClearDisplay();
+      int display_id = conn->display();
+      if (cur_state == DRM_MODE_CONNECTED) {
+        auto &display = displays_.at(display_id);
+        display.ChosePreferredConfig();
+      } else {
+        auto &display = displays_.at(display_id);
+        display.ClearDisplay();
+      }
+
+      HandleDisplayHotplug(display_id, cur_state);
     }
-
-    hwc2_->HandleDisplayHotplug(display_id, cur_state);
   }
 }
 
diff --git a/DrmHwcTwo.h b/DrmHwcTwo.h
index 0d213fd..7fd4fbd 100644
--- a/DrmHwcTwo.h
+++ b/DrmHwcTwo.h
@@ -149,7 +149,7 @@
     HwcDisplay(const HwcDisplay &) = delete;
     HWC2::Error Init(std::vector<DrmPlane *> *planes);
 
-    HWC2::Error CreateComposition(bool test);
+    HWC2::Error CreateComposition(AtomicCommitArgs &a_args);
     std::vector<DrmHwcTwo::HwcLayer *> GetOrderLayersByZPos();
 
     void ClearDisplay();
@@ -160,7 +160,7 @@
     HWC2::Error AcceptDisplayChanges();
     HWC2::Error CreateLayer(hwc2_layer_t *layer);
     HWC2::Error DestroyLayer(hwc2_layer_t layer);
-    HWC2::Error GetActiveConfig(hwc2_config_t *config);
+    HWC2::Error GetActiveConfig(hwc2_config_t *config) const;
     HWC2::Error GetChangedCompositionTypes(uint32_t *num_elements,
                                            hwc2_layer_t *layers,
                                            int32_t *types);
@@ -250,6 +250,22 @@
       uint32_t frames_flattened_ = 0;
     };
 
+    struct HwcDisplayConfig {
+      int id{};
+      int group_id{};
+      DrmMode mode;
+      bool disabled{};
+
+      bool IsInterlaced() {
+        return (mode.flags() & DRM_MODE_FLAG_INTERLACE) != 0;
+      }
+    };
+
+    std::map<int /*config_id*/, struct HwcDisplayConfig> hwc_configs_;
+
+    int active_config_id_ = 0;
+    int preferred_config_id_ = 0;
+
     const Backend *backend() const {
       return backend_.get();
     }
@@ -327,8 +343,6 @@
     std::atomic_int flattenning_state_{ClientFlattenningState::NotRequired};
     VSyncWorker flattening_vsync_worker_;
 
-    void AddFenceToPresentFence(UniqueFd fd);
-
     constexpr static size_t MATRIX_SIZE = 16;
 
     DrmHwcTwo *hwc2_;
@@ -351,7 +365,6 @@
     uint32_t layer_idx_ = 0;
     std::map<hwc2_layer_t, HwcLayer> layers_;
     HwcLayer client_layer_;
-    UniqueFd present_fence_;
     int32_t color_mode_{};
     std::array<float, MATRIX_SIZE> color_transform_matrix_{};
     android_color_transform_t color_transform_hint_;
@@ -362,18 +375,6 @@
     std::string DumpDelta(DrmHwcTwo::HwcDisplay::Stats delta);
   };
 
-  class DrmHotplugHandler : public DrmEventHandler {
-   public:
-    DrmHotplugHandler(DrmHwcTwo *hwc2, DrmDevice *drm)
-        : hwc2_(hwc2), drm_(drm) {
-    }
-    void HandleEvent(uint64_t timestamp_us);
-
-   private:
-    DrmHwcTwo *hwc2_;
-    DrmDevice *drm_;
-  };
-
  private:
   static DrmHwcTwo *toDrmHwcTwo(hwc2_device_t *dev) {
     return static_cast<DrmHwcTwo *>(dev);
@@ -442,6 +443,8 @@
   void HandleDisplayHotplug(hwc2_display_t displayid, int state);
   void HandleInitialHotplugState(DrmDevice *drmDevice);
 
+  void HandleHotplugUEvent();
+
   ResourceManager resource_manager_;
   std::map<hwc2_display_t, HwcDisplay> displays_;
 
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index d7eb240..ce606dd 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -46,8 +46,10 @@
 
     bool testing_needed = !(client_start == 0 && client_size == layers.size());
 
+    AtomicCommitArgs a_args = {.test_only = true};
+
     if (testing_needed &&
-        display->CreateComposition(true) != HWC2::Error::None) {
+        display->CreateComposition(a_args) != HWC2::Error::None) {
       ++display->total_stats().failed_kms_validate_;
       client_start = 0;
       client_size = layers.size();
@@ -70,7 +72,7 @@
   int client_start = -1;
   size_t client_size = 0;
 
-  for (int z_order = 0; z_order < layers.size(); ++z_order) {
+  for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
     if (IsClientLayer(display, layers[z_order])) {
       if (client_start < 0)
         client_start = (int)z_order;
@@ -98,7 +100,7 @@
 uint32_t Backend::CalcPixOps(const std::vector<DrmHwcTwo::HwcLayer *> &layers,
                              size_t first_z, size_t size) {
   uint32_t pixops = 0;
-  for (int z_order = 0; z_order < layers.size(); ++z_order) {
+  for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
     if (z_order >= first_z && z_order < first_z + size) {
       hwc_rect_t df = layers[z_order]->display_frame();
       pixops += (df.right - df.left) * (df.bottom - df.top);
@@ -109,7 +111,7 @@
 
 void Backend::MarkValidated(std::vector<DrmHwcTwo::HwcLayer *> &layers,
                             size_t client_first_z, size_t client_size) {
-  for (int z_order = 0; z_order < layers.size(); ++z_order) {
+  for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
     if (z_order >= client_first_z && z_order < client_first_z + client_size)
       layers[z_order]->set_validated_type(HWC2::Composition::Client);
     else
@@ -150,12 +152,12 @@
       steps = 1 + layers.size() - extra_client;
     }
 
-    uint32_t gpu_pixops = INT_MAX;
-    for (int i = 0; i < steps; i++) {
+    uint32_t gpu_pixops = UINT32_MAX;
+    for (size_t i = 0; i < steps; i++) {
       uint32_t po = CalcPixOps(layers, start + i, client_size);
       if (po < gpu_pixops) {
         gpu_pixops = po;
-        client_start = start + i;
+        client_start = start + int(i);
       }
     }
   }
diff --git a/bufferinfo/BufferInfoGetter.cpp b/bufferinfo/BufferInfoGetter.cpp
index 7f7f8ae..c284365 100644
--- a/bufferinfo/BufferInfoGetter.cpp
+++ b/bufferinfo/BufferInfoGetter.cpp
@@ -32,17 +32,17 @@
 
 BufferInfoGetter *BufferInfoGetter::GetInstance() {
   static std::unique_ptr<BufferInfoGetter> inst;
-  if (inst == nullptr) {
-#if PLATFORM_SDK_VERSION >= 30
+  if (!inst) {
+#if PLATFORM_SDK_VERSION >= 30 && defined(USE_IMAPPER4_METADATA_API)
     inst.reset(BufferInfoMapperMetadata::CreateInstance());
-    if (inst == nullptr) {
+    if (!inst) {
       ALOGW(
           "Generic buffer getter is not available. Falling back to legacy...");
-#endif
-      inst = LegacyBufferInfoGetter::CreateInstance();
-#if PLATFORM_SDK_VERSION >= 30
     }
 #endif
+    if (!inst) {
+      inst = LegacyBufferInfoGetter::CreateInstance();
+    }
   }
 
   return inst.get();
diff --git a/bufferinfo/BufferInfoGetter.h b/bufferinfo/BufferInfoGetter.h
index 60ca985..7b088df 100644
--- a/bufferinfo/BufferInfoGetter.h
+++ b/bufferinfo/BufferInfoGetter.h
@@ -49,6 +49,10 @@
 
   int Init();
 
+  virtual int ValidateGralloc() {
+    return 0;
+  }
+
   int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override = 0;
 
   static std::unique_ptr<LegacyBufferInfoGetter> CreateInstance();
@@ -65,9 +69,13 @@
   LegacyBufferInfoGetter::CreateInstance() {                           \
     auto instance = std::make_unique<getter_>();                       \
     if (instance) {                                                    \
-      int ret = instance->Init();                                      \
-      if (ret) {                                                       \
-        ALOGE("Failed to initialize the " #getter_ " getter %d", ret); \
+      int err = instance->Init();                                      \
+      if (err) {                                                       \
+        ALOGE("Failed to initialize the " #getter_ " getter %d", err); \
+        instance.reset();                                              \
+      }                                                                \
+      err = instance->ValidateGralloc();                               \
+      if (err) {                                                       \
         instance.reset();                                              \
       }                                                                \
     }                                                                  \
diff --git a/bufferinfo/legacy/BufferInfoImagination.cpp b/bufferinfo/legacy/BufferInfoImagination.cpp
index d646072..691dd14 100644
--- a/bufferinfo/legacy/BufferInfoImagination.cpp
+++ b/bufferinfo/legacy/BufferInfoImagination.cpp
@@ -20,6 +20,8 @@
 
 #include <xf86drm.h>
 
+#include <cerrno>
+
 #include "img_gralloc1_public.h"
 #include "utils/log.h"
 
diff --git a/bufferinfo/legacy/BufferInfoLibdrm.cpp b/bufferinfo/legacy/BufferInfoLibdrm.cpp
index da89eb5..47481b2 100644
--- a/bufferinfo/legacy/BufferInfoLibdrm.cpp
+++ b/bufferinfo/legacy/BufferInfoLibdrm.cpp
@@ -23,6 +23,8 @@
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
+#include <mutex>
+
 #include "utils/log.h"
 #include "utils/properties.h"
 
@@ -64,15 +66,15 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
-static int get_fourcc_yuv(uint32_t native, enum chroma_order chroma_order,
-                          size_t chroma_step) {
+static uint32_t get_fourcc_yuv(uint32_t native, enum chroma_order chroma_order,
+                               size_t chroma_step) {
   for (auto droid_yuv_format : kDroidYuvFormats)
     if (droid_yuv_format.native == native &&
         droid_yuv_format.chroma_order == chroma_order &&
         droid_yuv_format.chroma_step == chroma_step)
       return droid_yuv_format.fourcc;
 
-  return -1;
+  return UINT32_MAX;
 }
 
 static bool is_yuv(uint32_t native) {
@@ -129,7 +131,7 @@
   /* .chroma_step is the byte distance between the same chroma channel
    * values of subsequent pixels, assumed to be the same for Cb and Cr. */
   bo->format = get_fourcc_yuv(bo->hal_format, chroma_order, ycbcr.chroma_step);
-  if (bo->format == -1) {
+  if (bo->format == UINT32_MAX) {
     ALOGW(
         "unsupported YUV format, native = %x, chroma_order = %s, chroma_step = "
         "%d",
@@ -202,4 +204,20 @@
   return 0;
 }
 
+constexpr char gbm_gralloc_module_name[] = "GBM Memory Allocator";
+constexpr char drm_gralloc_module_name[] = "DRM Memory Allocator";
+
+int BufferInfoLibdrm::ValidateGralloc() {
+  if (strcmp(gralloc_->common.name, drm_gralloc_module_name) != 0 &&
+      strcmp(gralloc_->common.name, gbm_gralloc_module_name) != 0) {
+    ALOGE(
+        "Gralloc name isn't valid: Expected: \"%s\" or \"%s\", Actual: \"%s\"",
+        gbm_gralloc_module_name, drm_gralloc_module_name,
+        gralloc_->common.name);
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
 }  // namespace android
diff --git a/bufferinfo/legacy/BufferInfoLibdrm.h b/bufferinfo/legacy/BufferInfoLibdrm.h
index 4d37d00..cad8add 100644
--- a/bufferinfo/legacy/BufferInfoLibdrm.h
+++ b/bufferinfo/legacy/BufferInfoLibdrm.h
@@ -27,6 +27,7 @@
  public:
   using LegacyBufferInfoGetter::LegacyBufferInfoGetter;
   int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  int ValidateGralloc() override;
 
  private:
   bool GetYuvPlaneInfo(int num_fds, buffer_handle_t handle, hwc_drm_bo_t *bo);
diff --git a/bufferinfo/legacy/BufferInfoMinigbm.cpp b/bufferinfo/legacy/BufferInfoMinigbm.cpp
index d030dff..777c2b7 100644
--- a/bufferinfo/legacy/BufferInfoMinigbm.cpp
+++ b/bufferinfo/legacy/BufferInfoMinigbm.cpp
@@ -21,35 +21,102 @@
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
-#include "cros_gralloc_handle.h"
+#include <cerrno>
+#include <cstring>
+
 #include "utils/log.h"
-
-#define DRM_FORMAT_YVU420_ANDROID fourcc_code('9', '9', '9', '7')
-
 namespace android {
 
 LEGACY_BUFFER_INFO_GETTER(BufferInfoMinigbm);
 
+constexpr int CROS_GRALLOC_DRM_GET_FORMAT = 1;
+constexpr int CROS_GRALLOC_DRM_GET_DIMENSIONS = 2;
+constexpr int CROS_GRALLOC_DRM_GET_BUFFER_INFO = 4;
+constexpr int CROS_GRALLOC_DRM_GET_USAGE = 5;
+
+struct cros_gralloc0_buffer_info {
+  uint32_t drm_fourcc;
+  int num_fds;
+  int fds[4];
+  uint64_t modifier;
+  int offset[4];
+  int stride[4];
+};
+
 int BufferInfoMinigbm::ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) {
-  auto *gr_handle = (cros_gralloc_handle *)handle;
-  if (!gr_handle)
+  if (handle == nullptr) {
     return -EINVAL;
+  }
 
-  bo->width = gr_handle->width;
-  bo->height = gr_handle->height;
-  bo->hal_format = gr_handle->droid_format;
+  uint32_t width{};
+  uint32_t height{};
+  if (gralloc_->perform(gralloc_, CROS_GRALLOC_DRM_GET_DIMENSIONS, handle,
+                        &width, &height) != 0) {
+    ALOGE(
+        "CROS_GRALLOC_DRM_GET_DIMENSIONS operation has failed. "
+        "Please ensure you are using the latest minigbm.");
+    return -EINVAL;
+  }
 
-  bo->format = gr_handle->format;
-  if (bo->format == DRM_FORMAT_YVU420_ANDROID)
-    bo->format = DRM_FORMAT_YVU420;
+  int32_t droid_format{};
+  if (gralloc_->perform(gralloc_, CROS_GRALLOC_DRM_GET_FORMAT, handle,
+                        &droid_format) != 0) {
+    ALOGE(
+        "CROS_GRALLOC_DRM_GET_FORMAT operation has failed. "
+        "Please ensure you are using the latest minigbm.");
+    return -EINVAL;
+  }
 
-  bo->usage = gr_handle->usage;
+  uint32_t usage{};
+  if (gralloc_->perform(gralloc_, CROS_GRALLOC_DRM_GET_USAGE, handle, &usage) !=
+      0) {
+    ALOGE(
+        "CROS_GRALLOC_DRM_GET_USAGE operation has failed. "
+        "Please ensure you are using the latest minigbm.");
+    return -EINVAL;
+  }
 
-  for (int i = 0; i < gr_handle->num_planes; i++) {
-    bo->modifiers[i] = gr_handle->format_modifier;
-    bo->prime_fds[i] = gr_handle->fds[i];
-    bo->pitches[i] = gr_handle->strides[i];
-    bo->offsets[i] = gr_handle->offsets[i];
+  struct cros_gralloc0_buffer_info info {};
+  if (gralloc_->perform(gralloc_, CROS_GRALLOC_DRM_GET_BUFFER_INFO, handle,
+                        &info) != 0) {
+    ALOGE(
+        "CROS_GRALLOC_DRM_GET_BUFFER_INFO operation has failed. "
+        "Please ensure you are using the latest minigbm.");
+    return -EINVAL;
+  }
+
+  bo->width = width;
+  bo->height = height;
+
+  bo->hal_format = droid_format;
+
+  bo->format = info.drm_fourcc;
+  bo->usage = usage;
+
+  for (int i = 0; i < info.num_fds; i++) {
+    bo->modifiers[i] = info.modifier;
+    bo->prime_fds[i] = info.fds[i];
+    bo->pitches[i] = info.stride[i];
+    bo->offsets[i] = info.offset[i];
+  }
+
+  return 0;
+}
+
+constexpr char cros_gralloc_module_name[] = "CrOS Gralloc";
+
+int BufferInfoMinigbm::ValidateGralloc() {
+  if (strcmp(gralloc_->common.name, cros_gralloc_module_name) != 0) {
+    ALOGE("Gralloc name isn't valid: Expected: \"%s\", Actual: \"%s\"",
+          cros_gralloc_module_name, gralloc_->common.name);
+    return -EINVAL;
+  }
+
+  if (gralloc_->perform == nullptr) {
+    ALOGE(
+        "CrOS gralloc has no perform call implemented. Please upgrade your "
+        "minigbm.");
+    return -EINVAL;
   }
 
   return 0;
diff --git a/bufferinfo/legacy/BufferInfoMinigbm.h b/bufferinfo/legacy/BufferInfoMinigbm.h
index bff9d74..04cc2ae 100644
--- a/bufferinfo/legacy/BufferInfoMinigbm.h
+++ b/bufferinfo/legacy/BufferInfoMinigbm.h
@@ -27,6 +27,7 @@
  public:
   using LegacyBufferInfoGetter::LegacyBufferInfoGetter;
   int ConvertBoInfo(buffer_handle_t handle, hwc_drm_bo_t *bo) override;
+  int ValidateGralloc() override;
 };
 
 }  // namespace android
diff --git a/compositor/DrmDisplayComposition.cpp b/compositor/DrmDisplayComposition.cpp
index c0fbba0..cd95267 100644
--- a/compositor/DrmDisplayComposition.cpp
+++ b/compositor/DrmDisplayComposition.cpp
@@ -37,41 +37,11 @@
       planner_(planner) {
 }
 
-bool DrmDisplayComposition::validate_composition_type(DrmCompositionType des) {
-  return type_ == DRM_COMPOSITION_TYPE_EMPTY || type_ == des;
-}
-
-int DrmDisplayComposition::SetLayers(DrmHwcLayer *layers, size_t num_layers,
-                                     bool geometry_changed) {
-  if (!validate_composition_type(DRM_COMPOSITION_TYPE_FRAME))
-    return -EINVAL;
-
-  geometry_changed_ = geometry_changed;
-
+int DrmDisplayComposition::SetLayers(DrmHwcLayer *layers, size_t num_layers) {
   for (size_t layer_index = 0; layer_index < num_layers; layer_index++) {
     layers_.emplace_back(std::move(layers[layer_index]));
   }
 
-  type_ = DRM_COMPOSITION_TYPE_FRAME;
-  return 0;
-}
-
-int DrmDisplayComposition::SetDpmsMode(uint32_t dpms_mode) {
-  if (!validate_composition_type(DRM_COMPOSITION_TYPE_DPMS))
-    return -EINVAL;
-  dpms_mode_ = dpms_mode;
-  type_ = DRM_COMPOSITION_TYPE_DPMS;
-  return 0;
-}
-
-int DrmDisplayComposition::SetDisplayMode(const DrmMode &display_mode) {
-  if (!validate_composition_type(DRM_COMPOSITION_TYPE_MODESET)) {
-    ALOGE("SetDisplayMode() Failed to validate composition type");
-    return -EINVAL;
-  }
-  display_mode_ = display_mode;
-  dpms_mode_ = DRM_MODE_DPMS_ON;
-  type_ = DRM_COMPOSITION_TYPE_MODESET;
   return 0;
 }
 
@@ -87,9 +57,6 @@
 
 int DrmDisplayComposition::Plan(std::vector<DrmPlane *> *primary_planes,
                                 std::vector<DrmPlane *> *overlay_planes) {
-  if (type_ != DRM_COMPOSITION_TYPE_FRAME)
-    return 0;
-
   std::map<size_t, DrmHwcLayer *> to_composite;
 
   for (size_t i = 0; i < layers_.size(); ++i)
diff --git a/compositor/DrmDisplayComposition.h b/compositor/DrmDisplayComposition.h
index bbac0af..f1958d7 100644
--- a/compositor/DrmDisplayComposition.h
+++ b/compositor/DrmDisplayComposition.h
@@ -32,13 +32,6 @@
 class Importer;
 class Planner;
 
-enum DrmCompositionType {
-  DRM_COMPOSITION_TYPE_EMPTY,
-  DRM_COMPOSITION_TYPE_FRAME,
-  DRM_COMPOSITION_TYPE_DPMS,
-  DRM_COMPOSITION_TYPE_MODESET,
-};
-
 class DrmCompositionPlane {
  public:
   enum class Type : int32_t {
@@ -86,11 +79,9 @@
   DrmDisplayComposition(DrmCrtc *crtc, Planner *planner);
   ~DrmDisplayComposition() = default;
 
-  int SetLayers(DrmHwcLayer *layers, size_t num_layers, bool geometry_changed);
+  int SetLayers(DrmHwcLayer *layers, size_t num_layers);
   int AddPlaneComposition(DrmCompositionPlane plane);
   int AddPlaneDisable(DrmPlane *plane);
-  int SetDpmsMode(uint32_t dpms_mode);
-  int SetDisplayMode(const DrmMode &display_mode);
 
   int Plan(std::vector<DrmPlane *> *primary_planes,
            std::vector<DrmPlane *> *overlay_planes);
@@ -103,22 +94,6 @@
     return composition_planes_;
   }
 
-  bool geometry_changed() const {
-    return geometry_changed_;
-  }
-
-  DrmCompositionType type() const {
-    return type_;
-  }
-
-  uint32_t dpms_mode() const {
-    return dpms_mode_;
-  }
-
-  const DrmMode &display_mode() const {
-    return display_mode_;
-  }
-
   DrmCrtc *crtc() const {
     return crtc_;
   }
@@ -127,19 +102,10 @@
     return planner_;
   }
 
-  UniqueFd out_fence_;
-
  private:
-  bool validate_composition_type(DrmCompositionType desired);
-
   DrmCrtc *crtc_ = NULL;
   Planner *planner_ = NULL;
 
-  DrmCompositionType type_ = DRM_COMPOSITION_TYPE_EMPTY;
-  uint32_t dpms_mode_ = DRM_MODE_DPMS_ON;
-  DrmMode display_mode_;
-
-  bool geometry_changed_ = true;
   std::vector<DrmHwcLayer> layers_;
   std::vector<DrmCompositionPlane> composition_planes_;
 };
diff --git a/compositor/DrmDisplayCompositor.cpp b/compositor/DrmDisplayCompositor.cpp
index 576c533..447d75e 100644
--- a/compositor/DrmDisplayCompositor.cpp
+++ b/compositor/DrmDisplayCompositor.cpp
@@ -40,21 +40,6 @@
 
 namespace android {
 
-DrmDisplayCompositor::DrmDisplayCompositor()
-    : resource_manager_(nullptr),
-      display_(-1),
-      initialized_(false),
-      active_(false),
-      use_hw_overlays_(true) {
-}
-
-DrmDisplayCompositor::~DrmDisplayCompositor() {
-  if (!initialized_)
-    return;
-
-  active_composition_.reset();
-}
-
 auto DrmDisplayCompositor::Init(ResourceManager *resource_manager, int display)
     -> int {
   resource_manager_ = resource_manager;
@@ -82,56 +67,30 @@
   return std::make_unique<DrmDisplayComposition>(crtc, planner_.get());
 }
 
-std::tuple<uint32_t, uint32_t, int>
-DrmDisplayCompositor::GetActiveModeResolution() {
-  DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
-  DrmConnector *connector = drm->GetConnectorForDisplay(display_);
-  if (connector == nullptr) {
-    ALOGE("Failed to determine display mode: no connector for display %d",
-          display_);
-    return std::make_tuple(0, 0, -ENODEV);
-  }
-
-  const DrmMode &mode = connector->active_mode();
-  return std::make_tuple(mode.h_display(), mode.v_display(), 0);
-}
-
-int DrmDisplayCompositor::DisablePlanes(DrmDisplayComposition *display_comp) {
-  auto pset = MakeDrmModeAtomicReqUnique();
-  if (!pset) {
-    ALOGE("Failed to allocate property set");
-    return -ENOMEM;
-  }
-
-  int ret = 0;
-  std::vector<DrmCompositionPlane> &comp_planes = display_comp
-                                                      ->composition_planes();
-  for (DrmCompositionPlane &comp_plane : comp_planes) {
-    if (comp_plane.plane()->AtomicDisablePlane(*pset) != 0) {
-      return -EINVAL;
-    }
-  }
-  DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
-  ret = drmModeAtomicCommit(drm->fd(), pset.get(), 0, drm);
-  if (ret) {
-    ALOGE("Failed to commit pset ret=%d\n", ret);
-    return ret;
-  }
-
-  return 0;
-}
-
-int DrmDisplayCompositor::CommitFrame(DrmDisplayComposition *display_comp,
-                                      bool test_only) {
+auto DrmDisplayCompositor::CommitFrame(AtomicCommitArgs &args) -> int {
   ATRACE_CALL();
 
-  int ret = 0;
+  if (args.active && *args.active == active_kms_data.active_state) {
+    /* Don't set the same state twice */
+    args.active.reset();
+  }
 
-  std::vector<DrmHwcLayer> &layers = display_comp->layers();
-  std::vector<DrmCompositionPlane> &comp_planes = display_comp
-                                                      ->composition_planes();
+  if (!args.HasInputs()) {
+    /* nothing to do */
+    return 0;
+  }
+
+  if (!active_kms_data.active_state) {
+    /* Force activate display */
+    args.active = true;
+  }
+
+  if (args.clear_active_composition && args.composition) {
+    ALOGE("%s: Invalid arguments", __func__);
+    return -EINVAL;
+  }
+
   DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
-  uint64_t out_fences[drm->crtcs().size()];
 
   DrmConnector *connector = drm->GetConnectorForDisplay(display_);
   if (!connector) {
@@ -150,178 +109,133 @@
     return -ENOMEM;
   }
 
+  int64_t out_fence = -1;
   if (crtc->out_fence_ptr_property() &&
-      !crtc->out_fence_ptr_property()
-           .AtomicSet(*pset, (uint64_t)&out_fences[crtc->pipe()])) {
+      !crtc->out_fence_ptr_property().AtomicSet(*pset, (uint64_t)&out_fence)) {
     return -EINVAL;
   }
 
-  if (mode_.blob &&
-      (!crtc->active_property().AtomicSet(*pset, 1) ||
-       !crtc->mode_property().AtomicSet(*pset, *mode_.blob) ||
-       !connector->crtc_id_property().AtomicSet(*pset, crtc->id()))) {
-    return -EINVAL;
+  DrmModeUserPropertyBlobUnique mode_blob;
+
+  if (args.active) {
+    if (!crtc->active_property().AtomicSet(*pset, *args.active) ||
+        !connector->crtc_id_property().AtomicSet(*pset, crtc->id())) {
+      return -EINVAL;
+    }
   }
 
-  for (DrmCompositionPlane &comp_plane : comp_planes) {
-    DrmPlane *plane = comp_plane.plane();
-    std::vector<size_t> &source_layers = comp_plane.source_layers();
+  if (args.display_mode) {
+    mode_blob = args.display_mode.value().CreateModeBlob(
+        *resource_manager_->GetDrmDevice(display_));
 
-    if (comp_plane.type() != DrmCompositionPlane::Type::kDisable) {
-      if (source_layers.size() > 1) {
-        ALOGE("Can't handle more than one source layer sz=%zu type=%d",
-              source_layers.size(), comp_plane.type());
-        continue;
-      }
+    if (!mode_blob) {
+      ALOGE("Failed to create mode_blob");
+      return -EINVAL;
+    }
 
-      if (source_layers.empty() || source_layers.front() >= layers.size()) {
-        ALOGE("Source layer index %zu out of bounds %zu type=%d",
-              source_layers.front(), layers.size(), comp_plane.type());
-        return -EINVAL;
-      }
-      DrmHwcLayer &layer = layers[source_layers.front()];
+    if (!crtc->mode_property().AtomicSet(*pset, *mode_blob)) {
+      return -EINVAL;
+    }
+  }
 
-      if (plane->AtomicSetState(*pset, layer, source_layers.front(),
-                                crtc->id()) != 0) {
-        return -EINVAL;
+  if (args.composition) {
+    std::vector<DrmHwcLayer> &layers = args.composition->layers();
+    std::vector<DrmCompositionPlane> &comp_planes = args.composition
+                                                        ->composition_planes();
+
+    for (DrmCompositionPlane &comp_plane : comp_planes) {
+      DrmPlane *plane = comp_plane.plane();
+      std::vector<size_t> &source_layers = comp_plane.source_layers();
+
+      if (comp_plane.type() != DrmCompositionPlane::Type::kDisable) {
+        if (source_layers.size() > 1) {
+          ALOGE("Can't handle more than one source layer sz=%zu type=%d",
+                source_layers.size(), comp_plane.type());
+          continue;
+        }
+
+        if (source_layers.empty() || source_layers.front() >= layers.size()) {
+          ALOGE("Source layer index %zu out of bounds %zu type=%d",
+                source_layers.front(), layers.size(), comp_plane.type());
+          return -EINVAL;
+        }
+        DrmHwcLayer &layer = layers[source_layers.front()];
+
+        if (plane->AtomicSetState(*pset, layer, source_layers.front(),
+                                  crtc->id()) != 0) {
+          return -EINVAL;
+        }
+      } else {
+        if (plane->AtomicDisablePlane(*pset) != 0) {
+          return -EINVAL;
+        }
       }
-    } else {
-      if (plane->AtomicDisablePlane(*pset) != 0) {
+    }
+  }
+
+  if (args.clear_active_composition && active_kms_data.composition) {
+    auto &comp_planes = active_kms_data.composition->composition_planes();
+    for (auto &comp_plane : comp_planes) {
+      if (comp_plane.plane()->AtomicDisablePlane(*pset) != 0) {
         return -EINVAL;
       }
     }
   }
 
-  if (!ret) {
-    uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
-    if (test_only)
-      flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+  uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
+  if (args.test_only)
+    flags |= DRM_MODE_ATOMIC_TEST_ONLY;
 
-    ret = drmModeAtomicCommit(drm->fd(), pset.get(), flags, drm);
-    if (ret) {
-      if (!test_only)
-        ALOGE("Failed to commit pset ret=%d\n", ret);
-      return ret;
+  int err = drmModeAtomicCommit(drm->fd(), pset.get(), flags, drm);
+  if (err) {
+    if (!args.test_only)
+      ALOGE("Failed to commit pset ret=%d\n", err);
+    return err;
+  }
+
+  if (!args.test_only) {
+    if (args.display_mode) {
+      connector->set_active_mode(*args.display_mode);
+      active_kms_data.mode_blob = std::move(mode_blob);
+    }
+
+    if (args.clear_active_composition) {
+      active_kms_data.composition.reset();
+    }
+
+    if (args.composition) {
+      active_kms_data.composition = args.composition;
+    }
+
+    if (args.active) {
+      active_kms_data.active_state = *args.active;
+    }
+
+    if (crtc->out_fence_ptr_property()) {
+      args.out_fence = UniqueFd((int)out_fence);
     }
   }
 
-  if (!test_only && mode_.blob) {
-    /* TODO: Add dpms to the pset when the kernel supports it */
-    ret = ApplyDpms(display_comp);
-    if (ret) {
-      ALOGE("Failed to apply DPMS after modeset %d\n", ret);
-      return ret;
-    }
-
-    connector->set_active_mode(mode_.mode);
-    mode_.old_blob = std::move(mode_.blob);
-  }
-
-  if (crtc->out_fence_ptr_property()) {
-    display_comp->out_fence_ = UniqueFd((int)out_fences[crtc->pipe()]);
-  }
-
-  return ret;
-}
-
-int DrmDisplayCompositor::ApplyDpms(DrmDisplayComposition *display_comp) {
-  DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
-  DrmConnector *conn = drm->GetConnectorForDisplay(display_);
-  if (!conn) {
-    ALOGE("Failed to get DrmConnector for display %d", display_);
-    return -ENODEV;
-  }
-
-  const DrmProperty &prop = conn->dpms_property();
-  int ret = drmModeConnectorSetProperty(drm->fd(), conn->id(), prop.id(),
-                                        display_comp->dpms_mode());
-  if (ret) {
-    ALOGE("Failed to set DPMS property for connector %d", conn->id());
-    return ret;
-  }
   return 0;
 }
 
-auto DrmDisplayCompositor::CreateModeBlob(const DrmMode &mode)
-    -> DrmModeUserPropertyBlobUnique {
-  struct drm_mode_modeinfo drm_mode {};
-  mode.ToDrmModeModeInfo(&drm_mode);
+auto DrmDisplayCompositor::ExecuteAtomicCommit(AtomicCommitArgs &args) -> int {
+  int err = CommitFrame(args);
 
-  DrmDevice *drm = resource_manager_->GetDrmDevice(display_);
-  return drm->RegisterUserPropertyBlob(&drm_mode,
-                                       sizeof(struct drm_mode_modeinfo));
-}
-
-void DrmDisplayCompositor::ClearDisplay() {
-  if (!active_composition_)
-    return;
-
-  if (DisablePlanes(active_composition_.get()))
-    return;
-
-  active_composition_.reset(nullptr);
-}
-
-void DrmDisplayCompositor::ApplyFrame(
-    std::unique_ptr<DrmDisplayComposition> composition, int status) {
-  int ret = status;
-
-  if (!ret) {
-    ret = CommitFrame(composition.get(), false);
-  }
-
-  if (ret) {
-    ALOGE("Composite failed for display %d", display_);
-    // Disable the hw used by the last active composition. This allows us to
-    // signal the release fences from that composition to avoid hanging.
-    ClearDisplay();
-    return;
-  }
-
-  active_composition_.swap(composition);
-}
-
-int DrmDisplayCompositor::ApplyComposition(
-    std::unique_ptr<DrmDisplayComposition> composition) {
-  int ret = 0;
-  switch (composition->type()) {
-    case DRM_COMPOSITION_TYPE_FRAME:
-      if (composition->geometry_changed()) {
-        // Send the composition to the kernel to ensure we can commit it. This
-        // is just a test, it won't actually commit the frame.
-        ret = CommitFrame(composition.get(), true);
-        if (ret) {
-          ALOGE("Commit test failed for display %d, FIXME", display_);
-          return ret;
-        }
+  if (!args.test_only) {
+    if (err) {
+      ALOGE("Composite failed for display %d", display_);
+      // Disable the hw used by the last active composition. This allows us to
+      // signal the release fences from that composition to avoid hanging.
+      AtomicCommitArgs cl_args = {.clear_active_composition = true};
+      if (CommitFrame(cl_args)) {
+        ALOGE("Failed to clean-up active composition for display %d", display_);
       }
-
-      ApplyFrame(std::move(composition), ret);
-      break;
-    case DRM_COMPOSITION_TYPE_DPMS:
-      active_ = (composition->dpms_mode() == DRM_MODE_DPMS_ON);
-      ret = ApplyDpms(composition.get());
-      if (ret)
-        ALOGE("Failed to apply dpms for display %d", display_);
-      return ret;
-    case DRM_COMPOSITION_TYPE_MODESET:
-      mode_.mode = composition->display_mode();
-      mode_.blob = CreateModeBlob(mode_.mode);
-      if (!mode_.blob) {
-        ALOGE("Failed to create mode blob for display %d", display_);
-        return -EINVAL;
-      }
-      return 0;
-    default:
-      ALOGE("Unknown composition type %d", composition->type());
-      return -EINVAL;
+      return err;
+    }
   }
 
-  return ret;
-}
-
-int DrmDisplayCompositor::TestComposition(DrmDisplayComposition *composition) {
-  return CommitFrame(composition, true);
-}
+  return err;
+}  // namespace android
 
 }  // namespace android
diff --git a/compositor/DrmDisplayCompositor.h b/compositor/DrmDisplayCompositor.h
index 3227e12..55fe77d 100644
--- a/compositor/DrmDisplayCompositor.h
+++ b/compositor/DrmDisplayCompositor.h
@@ -34,57 +34,49 @@
 
 namespace android {
 
+struct AtomicCommitArgs {
+  /* inputs. All fields are optional, but at least one has to be specified */
+  bool test_only = false;
+  std::optional<DrmMode> display_mode;
+  std::optional<bool> active;
+  std::shared_ptr<DrmDisplayComposition> composition;
+  /* 'clear' should never be used together with 'composition' */
+  bool clear_active_composition = false;
+
+  /* out */
+  UniqueFd out_fence;
+
+  /* helpers */
+  auto HasInputs() -> bool {
+    return display_mode || active || composition || clear_active_composition;
+  }
+};
+
 class DrmDisplayCompositor {
  public:
-  DrmDisplayCompositor();
-  ~DrmDisplayCompositor();
-
+  DrmDisplayCompositor() = default;
+  ~DrmDisplayCompositor() = default;
   auto Init(ResourceManager *resource_manager, int display) -> int;
 
   std::unique_ptr<DrmDisplayComposition> CreateInitializedComposition() const;
-  int ApplyComposition(std::unique_ptr<DrmDisplayComposition> composition);
-  int TestComposition(DrmDisplayComposition *composition);
-  int Composite();
-  void ClearDisplay();
-  UniqueFd TakeOutFence() {
-    if (!active_composition_) {
-      return UniqueFd();
-    }
-    return std::move(active_composition_->out_fence_);
-  }
 
-  std::tuple<uint32_t, uint32_t, int> GetActiveModeResolution();
+  auto ExecuteAtomicCommit(AtomicCommitArgs &args) -> int;
 
  private:
-  struct ModeState {
-    DrmMode mode;
-    DrmModeUserPropertyBlobUnique blob;
-    DrmModeUserPropertyBlobUnique old_blob;
-  };
-
   DrmDisplayCompositor(const DrmDisplayCompositor &) = delete;
 
-  int CommitFrame(DrmDisplayComposition *display_comp, bool test_only);
-  int ApplyDpms(DrmDisplayComposition *display_comp);
-  int DisablePlanes(DrmDisplayComposition *display_comp);
+  auto CommitFrame(AtomicCommitArgs &args) -> int;
 
-  void ApplyFrame(std::unique_ptr<DrmDisplayComposition> composition,
-                  int status);
+  struct {
+    std::shared_ptr<DrmDisplayComposition> composition;
+    DrmModeUserPropertyBlobUnique mode_blob;
+    bool active_state{};
+  } active_kms_data;
 
-  auto CreateModeBlob(const DrmMode &mode) -> DrmModeUserPropertyBlobUnique;
-
-  ResourceManager *resource_manager_;
-  int display_;
-
-  std::unique_ptr<DrmDisplayComposition> active_composition_;
-
-  bool initialized_;
-  bool active_;
-  bool use_hw_overlays_;
-
-  ModeState mode_;
-
+  ResourceManager *resource_manager_ = nullptr;
   std::unique_ptr<Planner> planner_;
+  bool initialized_{};
+  int display_ = -1;
 };
 }  // namespace android
 
diff --git a/drm/DrmConnector.cpp b/drm/DrmConnector.cpp
index b3179c7..5b3c697 100644
--- a/drm/DrmConnector.cpp
+++ b/drm/DrmConnector.cpp
@@ -159,43 +159,27 @@
 }
 
 int DrmConnector::UpdateModes() {
-  int fd = drm_->fd();
-
-  drmModeConnectorPtr c = drmModeGetConnector(fd, id_);
+  drmModeConnectorPtr c = drmModeGetConnector(drm_->fd(), id_);
   if (!c) {
     ALOGE("Failed to get connector %d", id_);
     return -ENODEV;
   }
 
-  state_ = c->connection;
-
-  bool preferred_mode_found = false;
-  std::vector<DrmMode> new_modes;
+  modes_.clear();
   for (int i = 0; i < c->count_modes; ++i) {
     bool exists = false;
     for (const DrmMode &mode : modes_) {
       if (mode == c->modes[i]) {
-        new_modes.push_back(mode);
         exists = true;
         break;
       }
     }
+
     if (!exists) {
-      DrmMode m(&c->modes[i]);
-      m.set_id(drm_->next_mode_id());
-      new_modes.push_back(m);
-    }
-    // Use only the first DRM_MODE_TYPE_PREFERRED mode found
-    if (!preferred_mode_found &&
-        (new_modes.back().type() & DRM_MODE_TYPE_PREFERRED)) {
-      preferred_mode_id_ = new_modes.back().id();
-      preferred_mode_found = true;
+      modes_.emplace_back(DrmMode(&c->modes[i]));
     }
   }
-  modes_.swap(new_modes);
-  if (!preferred_mode_found && !modes_.empty()) {
-    preferred_mode_id_ = modes_[0].id();
-  }
+
   return 0;
 }
 
diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h
index e2789ea..f2305aa 100644
--- a/drm/DrmConnector.h
+++ b/drm/DrmConnector.h
@@ -82,10 +82,6 @@
   uint32_t mm_width() const;
   uint32_t mm_height() const;
 
-  uint32_t get_preferred_mode_id() const {
-    return preferred_mode_id_;
-  }
-
  private:
   DrmDevice *drm_;
 
@@ -111,8 +107,6 @@
   DrmProperty writeback_out_fence_;
 
   std::vector<DrmEncoder *> possible_encoders_;
-
-  uint32_t preferred_mode_id_{};
 };
 }  // namespace android
 
diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp
index 1753ddc..0f5f581 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -111,15 +111,11 @@
   return primary_candidates;
 }
 
-DrmDevice::DrmDevice() : event_listener_(this) {
+DrmDevice::DrmDevice() {
   self.reset(this);
   mDrmFbImporter = std::make_unique<DrmFbImporter>(self);
 }
 
-DrmDevice::~DrmDevice() {
-  event_listener_.Exit();
-}
-
 std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
   /* TODO: Use drmOpenControl here instead */
   fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC));
@@ -326,12 +322,6 @@
   if (ret)
     return std::make_tuple(ret, 0);
 
-  ret = event_listener_.Init();
-  if (ret) {
-    ALOGE("Can't initialize event listener %d", ret);
-    return std::make_tuple(ret, 0);
-  }
-
   for (auto &conn : connectors_) {
     ret = CreateDisplayPipe(conn.get());
     if (ret) {
@@ -526,10 +516,6 @@
       });
 }
 
-DrmEventListener *DrmDevice::event_listener() {
-  return &event_listener_;
-}
-
 int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type,
                            const char *prop_name, DrmProperty *property) const {
   drmModeObjectPropertiesPtr props = nullptr;
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index 81c60cd..c08c766 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -25,7 +25,6 @@
 #include "DrmConnector.h"
 #include "DrmCrtc.h"
 #include "DrmEncoder.h"
-#include "DrmEventListener.h"
 #include "DrmFbImporter.h"
 #include "DrmPlane.h"
 #include "utils/UniqueFd.h"
@@ -38,7 +37,7 @@
 class DrmDevice {
  public:
   DrmDevice();
-  ~DrmDevice();
+  ~DrmDevice() = default;
 
   std::tuple<int, int> Init(const char *path, int num_displays);
 
@@ -67,7 +66,6 @@
   DrmConnector *AvailableWritebackConnector(int display) const;
   DrmCrtc *GetCrtcForDisplay(int display) const;
   DrmPlane *GetPlane(uint32_t id) const;
-  DrmEventListener *event_listener();
 
   int GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name,
                       DrmProperty *property) const;
@@ -83,9 +81,6 @@
       -> DrmModeUserPropertyBlobUnique;
 
   bool HandlesDisplay(int display) const;
-  void RegisterHotplugHandler(DrmEventHandler *handler) {
-    event_listener_.RegisterHotplugHandler(handler);
-  }
 
   bool HasAddFb2ModifiersSupport() const {
     return HasAddFb2ModifiersSupport_;
@@ -114,7 +109,6 @@
   std::vector<std::unique_ptr<DrmEncoder>> encoders_;
   std::vector<std::unique_ptr<DrmCrtc>> crtcs_;
   std::vector<std::unique_ptr<DrmPlane>> planes_;
-  DrmEventListener event_listener_;
 
   std::pair<uint32_t, uint32_t> min_resolution_;
   std::pair<uint32_t, uint32_t> max_resolution_;
diff --git a/drm/DrmEventListener.cpp b/drm/DrmEventListener.cpp
deleted file mode 100644
index 53e7032..0000000
--- a/drm/DrmEventListener.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "hwc-drm-event-listener"
-
-#include "DrmEventListener.h"
-
-#include <linux/netlink.h>
-#include <sys/socket.h>
-#include <xf86drm.h>
-
-#include <cassert>
-#include <cerrno>
-#include <cstring>
-
-#include "DrmDevice.h"
-#include "utils/log.h"
-
-/* Originally defined in system/core/libsystem/include/system/graphics.h */
-#define HAL_PRIORITY_URGENT_DISPLAY (-8)
-
-namespace android {
-
-DrmEventListener::DrmEventListener(DrmDevice *drm)
-    : Worker("drm-event-listener", HAL_PRIORITY_URGENT_DISPLAY), drm_(drm) {
-}
-
-int DrmEventListener::Init() {
-  uevent_fd_ = UniqueFd(
-      socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT));
-  if (!uevent_fd_) {
-    ALOGE("Failed to open uevent socket: %s", strerror(errno));
-    return -errno;
-  }
-
-  struct sockaddr_nl addr {};
-  addr.nl_family = AF_NETLINK;
-  addr.nl_pid = 0;
-  addr.nl_groups = 0xFFFFFFFF;
-
-  int ret = bind(uevent_fd_.Get(), (struct sockaddr *)&addr, sizeof(addr));
-  if (ret) {
-    ALOGE("Failed to bind uevent socket: %s", strerror(errno));
-    return -errno;
-  }
-
-  // NOLINTNEXTLINE(readability-isolate-declaration)
-  FD_ZERO(&fds_);
-  FD_SET(drm_->fd(), &fds_);
-  FD_SET(uevent_fd_.Get(), &fds_);
-  max_fd_ = std::max(drm_->fd(), uevent_fd_.Get());
-
-  return InitWorker();
-}
-
-void DrmEventListener::RegisterHotplugHandler(DrmEventHandler *handler) {
-  assert(!hotplug_handler_);
-  hotplug_handler_.reset(handler);
-}
-
-void DrmEventListener::FlipHandler(int /* fd */, unsigned int /* sequence */,
-                                   unsigned int tv_sec, unsigned int tv_usec,
-                                   void *user_data) {
-  auto *handler = (DrmEventHandler *)user_data;
-  if (!handler)
-    return;
-
-  handler->HandleEvent((uint64_t)tv_sec * 1000 * 1000 + tv_usec);
-  delete handler;  // NOLINT(cppcoreguidelines-owning-memory)
-}
-
-void DrmEventListener::UEventHandler() {
-  char buffer[1024];
-  ssize_t ret = 0;
-
-  struct timespec ts {};
-
-  uint64_t timestamp = 0;
-  ret = clock_gettime(CLOCK_MONOTONIC, &ts);
-  if (!ret)
-    timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
-  else
-    ALOGE("Failed to get monotonic clock on hotplug %zd", ret);
-
-  while (true) {
-    ret = read(uevent_fd_.Get(), &buffer, sizeof(buffer));
-    if (ret == 0)
-      return;
-
-    if (ret < 0) {
-      ALOGE("Got error reading uevent %zd", ret);
-      return;
-    }
-
-    if (!hotplug_handler_)
-      continue;
-
-    bool drm_event = false;
-    bool hotplug_event = false;
-    for (uint32_t i = 0; i < ret;) {
-      char *event = buffer + i;
-      if (strcmp(event, "DEVTYPE=drm_minor") != 0)
-        drm_event = true;
-      else if (strcmp(event, "HOTPLUG=1") != 0)
-        hotplug_event = true;
-
-      i += strlen(event) + 1;
-    }
-
-    if (drm_event && hotplug_event)
-      hotplug_handler_->HandleEvent(timestamp);
-  }
-}
-
-void DrmEventListener::Routine() {
-  int ret = 0;
-  do {
-    ret = select(max_fd_ + 1, &fds_, nullptr, nullptr, nullptr);
-  } while (ret == -1 && errno == EINTR);
-
-  if (FD_ISSET(drm_->fd(), &fds_)) {
-    drmEventContext event_context =
-        {.version = 2,
-         .vblank_handler = nullptr,
-         .page_flip_handler = DrmEventListener::FlipHandler};
-    drmHandleEvent(drm_->fd(), &event_context);
-  }
-
-  if (FD_ISSET(uevent_fd_.Get(), &fds_))
-    UEventHandler();
-}
-}  // namespace android
diff --git a/drm/DrmEventListener.h b/drm/DrmEventListener.h
deleted file mode 100644
index f1f7310..0000000
--- a/drm/DrmEventListener.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_DRM_EVENT_LISTENER_H_
-#define ANDROID_DRM_EVENT_LISTENER_H_
-
-#include "utils/UniqueFd.h"
-#include "utils/Worker.h"
-
-namespace android {
-
-class DrmDevice;
-
-class DrmEventHandler {
- public:
-  DrmEventHandler() {
-  }
-  virtual ~DrmEventHandler() {
-  }
-
-  virtual void HandleEvent(uint64_t timestamp_us) = 0;
-};
-
-class DrmEventListener : public Worker {
- public:
-  DrmEventListener(DrmDevice *drm);
-  virtual ~DrmEventListener() {
-  }
-
-  int Init();
-
-  void RegisterHotplugHandler(DrmEventHandler *handler);
-
-  static void FlipHandler(int fd, unsigned int sequence, unsigned int tv_sec,
-                          unsigned int tv_usec, void *user_data);
-
- protected:
-  virtual void Routine();
-
- private:
-  void UEventHandler();
-
-  fd_set fds_{};
-  UniqueFd uevent_fd_;
-  int max_fd_ = -1;
-
-  DrmDevice *drm_;
-  std::unique_ptr<DrmEventHandler> hotplug_handler_;
-};
-}  // namespace android
-
-#endif
diff --git a/drm/DrmFbImporter.cpp b/drm/DrmFbImporter.cpp
index 8440093..25f32b7 100644
--- a/drm/DrmFbImporter.cpp
+++ b/drm/DrmFbImporter.cpp
@@ -41,7 +41,7 @@
   int32_t err = 0;
 
   /* Framebuffer object creation require gem handle for every used plane */
-  for (int i = 1; i < local->gem_handles_.size(); i++) {
+  for (size_t i = 1; i < local->gem_handles_.size(); i++) {
     if (bo->prime_fds[i] > 0) {
       if (bo->prime_fds[i] != bo->prime_fds[0]) {
         err = drmPrimeFDToHandle(drm->fd(), bo->prime_fds[i],
@@ -101,7 +101,7 @@
    * request via system properties)
    */
   struct drm_gem_close gem_close {};
-  for (int i = 0; i < gem_handles_.size(); i++) {
+  for (size_t i = 0; i < gem_handles_.size(); i++) {
     /* Don't close invalid handle. Close handle only once in cases
      * where several YUV planes located in the single buffer. */
     if (gem_handles_[i] == 0 ||
diff --git a/drm/DrmMode.cpp b/drm/DrmMode.cpp
index c714458..1c8bd0f 100644
--- a/drm/DrmMode.cpp
+++ b/drm/DrmMode.cpp
@@ -49,73 +49,47 @@
          v_scan_ == m.vscan && flags_ == m.flags && type_ == m.type;
 }
 
-void DrmMode::ToDrmModeModeInfo(drm_mode_modeinfo *m) const {
-  m->clock = clock_;
-  m->hdisplay = h_display_;
-  m->hsync_start = h_sync_start_;
-  m->hsync_end = h_sync_end_;
-  m->htotal = h_total_;
-  m->hskew = h_skew_;
-  m->vdisplay = v_display_;
-  m->vsync_start = v_sync_start_;
-  m->vsync_end = v_sync_end_;
-  m->vtotal = v_total_;
-  m->vscan = v_scan_;
-  m->vrefresh = v_refresh_;
-  m->flags = flags_;
-  m->type = type_;
-  strncpy(m->name, name_.c_str(), DRM_DISPLAY_MODE_LEN);
-}
-
-uint32_t DrmMode::id() const {
-  return id_;
-}
-
-void DrmMode::set_id(uint32_t id) {
-  id_ = id;
-}
-
 uint32_t DrmMode::clock() const {
   return clock_;
 }
 
-uint32_t DrmMode::h_display() const {
+uint16_t DrmMode::h_display() const {
   return h_display_;
 }
 
-uint32_t DrmMode::h_sync_start() const {
+uint16_t DrmMode::h_sync_start() const {
   return h_sync_start_;
 }
 
-uint32_t DrmMode::h_sync_end() const {
+uint16_t DrmMode::h_sync_end() const {
   return h_sync_end_;
 }
 
-uint32_t DrmMode::h_total() const {
+uint16_t DrmMode::h_total() const {
   return h_total_;
 }
 
-uint32_t DrmMode::h_skew() const {
+uint16_t DrmMode::h_skew() const {
   return h_skew_;
 }
 
-uint32_t DrmMode::v_display() const {
+uint16_t DrmMode::v_display() const {
   return v_display_;
 }
 
-uint32_t DrmMode::v_sync_start() const {
+uint16_t DrmMode::v_sync_start() const {
   return v_sync_start_;
 }
 
-uint32_t DrmMode::v_sync_end() const {
+uint16_t DrmMode::v_sync_end() const {
   return v_sync_end_;
 }
 
-uint32_t DrmMode::v_total() const {
+uint16_t DrmMode::v_total() const {
   return v_total_;
 }
 
-uint32_t DrmMode::v_scan() const {
+uint16_t DrmMode::v_scan() const {
   return v_scan_;
 }
 
@@ -133,6 +107,31 @@
 }
 
 std::string DrmMode::name() const {
-  return name_;
+  return name_ + "@" + std::to_string(v_refresh());
 }
+
+auto DrmMode::CreateModeBlob(const DrmDevice &drm)
+    -> DrmModeUserPropertyBlobUnique {
+  struct drm_mode_modeinfo drm_mode = {
+      .clock = clock_,
+      .hdisplay = h_display_,
+      .hsync_start = h_sync_start_,
+      .hsync_end = h_sync_end_,
+      .htotal = h_total_,
+      .hskew = h_skew_,
+      .vdisplay = v_display_,
+      .vsync_start = v_sync_start_,
+      .vsync_end = v_sync_end_,
+      .vtotal = v_total_,
+      .vscan = v_scan_,
+      .vrefresh = v_refresh_,
+      .flags = flags_,
+      .type = type_,
+  };
+  strncpy(drm_mode.name, name_.c_str(), DRM_DISPLAY_MODE_LEN);
+
+  return drm.RegisterUserPropertyBlob(&drm_mode,
+                                       sizeof(struct drm_mode_modeinfo));
+}
+
 }  // namespace android
diff --git a/drm/DrmMode.h b/drm/DrmMode.h
index 313a8ea..41b9e15 100644
--- a/drm/DrmMode.h
+++ b/drm/DrmMode.h
@@ -17,37 +17,38 @@
 #ifndef ANDROID_DRM_MODE_H_
 #define ANDROID_DRM_MODE_H_
 
-#include <stdint.h>
+#include <stdio.h>
 #include <xf86drmMode.h>
 
+#include <cstdint>
 #include <string>
 
+#include "DrmUnique.h"
+
 namespace android {
 
+class DrmDevice;
+
 class DrmMode {
  public:
   DrmMode() = default;
   DrmMode(drmModeModeInfoPtr m);
 
   bool operator==(const drmModeModeInfo &m) const;
-  void ToDrmModeModeInfo(drm_mode_modeinfo *m) const;
-
-  uint32_t id() const;
-  void set_id(uint32_t id);
 
   uint32_t clock() const;
 
-  uint32_t h_display() const;
-  uint32_t h_sync_start() const;
-  uint32_t h_sync_end() const;
-  uint32_t h_total() const;
-  uint32_t h_skew() const;
+  uint16_t h_display() const;
+  uint16_t h_sync_start() const;
+  uint16_t h_sync_end() const;
+  uint16_t h_total() const;
+  uint16_t h_skew() const;
 
-  uint32_t v_display() const;
-  uint32_t v_sync_start() const;
-  uint32_t v_sync_end() const;
-  uint32_t v_total() const;
-  uint32_t v_scan() const;
+  uint16_t v_display() const;
+  uint16_t v_sync_start() const;
+  uint16_t v_sync_end() const;
+  uint16_t v_total() const;
+  uint16_t v_scan() const;
   float v_refresh() const;
 
   uint32_t flags() const;
@@ -55,23 +56,23 @@
 
   std::string name() const;
 
- private:
-  uint32_t id_ = 0;
+  auto CreateModeBlob(const DrmDevice &drm) -> DrmModeUserPropertyBlobUnique;
 
+ private:
   uint32_t clock_ = 0;
 
-  uint32_t h_display_ = 0;
-  uint32_t h_sync_start_ = 0;
-  uint32_t h_sync_end_ = 0;
-  uint32_t h_total_ = 0;
-  uint32_t h_skew_ = 0;
+  uint16_t h_display_ = 0;
+  uint16_t h_sync_start_ = 0;
+  uint16_t h_sync_end_ = 0;
+  uint16_t h_total_ = 0;
+  uint16_t h_skew_ = 0;
 
-  uint32_t v_display_ = 0;
-  uint32_t v_sync_start_ = 0;
-  uint32_t v_sync_end_ = 0;
-  uint32_t v_total_ = 0;
-  uint32_t v_scan_ = 0;
-  uint32_t v_refresh_ = 0;
+  uint16_t v_display_ = 0;
+  uint16_t v_sync_start_ = 0;
+  uint16_t v_sync_end_ = 0;
+  uint16_t v_total_ = 0;
+  uint16_t v_scan_ = 0;
+  uint16_t v_refresh_ = 0;
 
   uint32_t flags_ = 0;
   uint32_t type_ = 0;
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index c55d6b8..8baa4cb 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -70,6 +70,12 @@
     return -EINVAL;
   }
 
+  ret = uevent_listener_.Init();
+  if (ret) {
+    ALOGE("Can't initialize event listener %d", ret);
+    return ret;
+  }
+
   return 0;
 }
 
diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h
index d9e0712..5ffe92c 100644
--- a/drm/ResourceManager.h
+++ b/drm/ResourceManager.h
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include "DrmDevice.h"
+#include "UEventListener.h"
 #include "DrmFbImporter.h"
 
 namespace android {
@@ -29,6 +30,10 @@
   ResourceManager();
   ResourceManager(const ResourceManager &) = delete;
   ResourceManager &operator=(const ResourceManager &) = delete;
+  ~ResourceManager() {
+    uevent_listener_.Exit();
+  }
+
   int Init();
   DrmDevice *GetDrmDevice(int display);
   const std::vector<std::unique_ptr<DrmDevice>> &getDrmDevices() const {
@@ -41,6 +46,10 @@
     return scale_with_gpu_;
   }
 
+  UEventListener *GetUEventListener() {
+    return &uevent_listener_;
+  }
+
  private:
   int AddDrmDevice(std::string const &path);
 
@@ -48,6 +57,8 @@
   std::vector<std::unique_ptr<DrmDevice>> drms_;
 
   bool scale_with_gpu_{};
+
+  UEventListener uevent_listener_;
 };
 }  // namespace android
 
diff --git a/drm/UEventListener.cpp b/drm/UEventListener.cpp
new file mode 100644
index 0000000..eae0608
--- /dev/null
+++ b/drm/UEventListener.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc-uevent-listener"
+
+#include "UEventListener.h"
+
+#include <linux/netlink.h>
+#include <sys/socket.h>
+#include <xf86drm.h>
+
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+
+#include "utils/log.h"
+
+/* Originally defined in system/core/libsystem/include/system/graphics.h */
+#define HAL_PRIORITY_URGENT_DISPLAY (-8)
+
+namespace android {
+
+UEventListener::UEventListener()
+    : Worker("uevent-listener", HAL_PRIORITY_URGENT_DISPLAY){};
+
+int UEventListener::Init() {
+  uevent_fd_ = UniqueFd(
+      socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT));
+  if (!uevent_fd_) {
+    ALOGE("Failed to open uevent socket: %s", strerror(errno));
+    return -errno;
+  }
+
+  struct sockaddr_nl addr {};
+  addr.nl_family = AF_NETLINK;
+  addr.nl_pid = 0;
+  addr.nl_groups = 0xFFFFFFFF;
+
+  int ret = bind(uevent_fd_.Get(), (struct sockaddr *)&addr, sizeof(addr));
+  if (ret) {
+    ALOGE("Failed to bind uevent socket: %s", strerror(errno));
+    return -errno;
+  }
+
+  return InitWorker();
+}
+
+void UEventListener::Routine() {
+  char buffer[1024];
+  ssize_t ret = 0;
+
+  while (true) {
+    ret = read(uevent_fd_.Get(), &buffer, sizeof(buffer));
+    if (ret == 0)
+      return;
+
+    if (ret < 0) {
+      ALOGE("Got error reading uevent %zd", ret);
+      return;
+    }
+
+    if (!hotplug_handler_)
+      continue;
+
+    bool drm_event = false;
+    bool hotplug_event = false;
+    for (uint32_t i = 0; i < ret;) {
+      char *event = buffer + i;
+      if (strcmp(event, "DEVTYPE=drm_minor") != 0)
+        drm_event = true;
+      else if (strcmp(event, "HOTPLUG=1") != 0)
+        hotplug_event = true;
+
+      i += strlen(event) + 1;
+    }
+
+    if (drm_event && hotplug_event && hotplug_handler_) {
+      hotplug_handler_();
+    }
+  }
+}
+}  // namespace android
diff --git a/drm/UEventListener.h b/drm/UEventListener.h
new file mode 100644
index 0000000..048eb40
--- /dev/null
+++ b/drm/UEventListener.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UEVENT_LISTENER_H_
+#define ANDROID_UEVENT_LISTENER_H_
+
+#include <functional>
+
+#include "utils/UniqueFd.h"
+#include "utils/Worker.h"
+
+namespace android {
+
+class UEventListener : public Worker {
+ public:
+  UEventListener();
+  virtual ~UEventListener() = default;
+
+  int Init();
+
+  void RegisterHotplugHandler(std::function<void()> hotplug_handler) {
+    hotplug_handler_ = hotplug_handler;
+  }
+
+ protected:
+  virtual void Routine();
+
+ private:
+  UniqueFd uevent_fd_;
+
+  std::function<void()> hotplug_handler_;
+};
+}  // namespace android
+
+#endif
diff --git a/tests/Android.bp b/tests/Android.bp
index 473c944..20b87a8 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -1,9 +1,9 @@
 cc_library_shared {
     name: "hwcomposer.filegroups_build_test",
     defaults: ["hwcomposer.drm_defaults"],
-    whole_static_libs: ["drm_hwcomposer"],
 
     srcs: [
+        ":drm_hwcomposer_common",
         ":drm_hwcomposer_platformhisi",
         ":drm_hwcomposer_platformimagination",
         ":drm_hwcomposer_platformmediatek",
diff --git a/tests/test_include/cros_gralloc_handle.h b/tests/test_include/cros_gralloc_handle.h
deleted file mode 100644
index d77d777..0000000
--- a/tests/test_include/cros_gralloc_handle.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// clang-format off
-/*
- * Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef CROS_GRALLOC_HANDLE_H
-#define CROS_GRALLOC_HANDLE_H
-
-#include <cstdint>
-#include <cutils/native_handle.h>
-
-#define DRV_MAX_PLANES 4
-#define DRV_MAX_FDS (DRV_MAX_PLANES + 1)
-
-struct cros_gralloc_handle : public native_handle_t {
-	/*
-	 * File descriptors must immediately follow the native_handle_t base and used file
-	 * descriptors must be packed at the beginning of this array to work with
-	 * native_handle_clone().
-	 *
-	 * This field contains 'num_planes' plane file descriptors followed by an optional metadata
-	 * reserved region file descriptor if 'reserved_region_size' is greater than zero.
-	 */
-	int32_t fds[DRV_MAX_FDS];
-	uint32_t strides[DRV_MAX_PLANES];
-	uint32_t offsets[DRV_MAX_PLANES];
-	uint32_t sizes[DRV_MAX_PLANES];
-	uint32_t id;
-	uint32_t width;
-	uint32_t height;
-	uint32_t format; /* DRM format */
-	uint32_t tiling;
-	uint64_t format_modifier;
-	uint64_t use_flags; /* Buffer creation flags */
-	uint32_t magic;
-	uint32_t pixel_stride;
-	int32_t droid_format;
-	int32_t usage; /* Android usage. */
-	uint32_t num_planes;
-	uint64_t reserved_region_size;
-	uint64_t total_size; /* Total allocation size */
-	/*
-	 * Name is a null terminated char array located at handle->base.data[handle->name_offset].
-	 */
-	uint32_t name_offset;
-} __attribute__((packed));
-
-typedef const struct cros_gralloc_handle *cros_gralloc_handle_t;
-
-#endif
-// clang-format on