Upgrade drm_hwcomposer to 851ea4dc268c4da612c65a5dfedbd3ed85960405 am: 32e0a72311 am: 0e86a316f0

Original change: undetermined

Change-Id: Iab46cd74595a23a580a3989e462f599f019bf9df
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.ci/.gitlab-ci-checkcommit.sh b/.ci/.gitlab-ci-checkcommit.sh
index 3f597c3..1ca2876 100755
--- a/.ci/.gitlab-ci-checkcommit.sh
+++ b/.ci/.gitlab-ci-checkcommit.sh
@@ -61,7 +61,7 @@
 		exit 1
 	fi
 
-	git show "$h" -- | clang-format-diff-19 -p 1 -style=file > /tmp/format-fixup.patch
+	git show -U0 "$h" -- | clang-format-diff-19 -p 1 -style=file > /tmp/format-fixup.patch
 	if [ -s  /tmp/format-fixup.patch ]; then
 		cat /tmp/format-fixup.patch >&2
 		exit 1
diff --git a/.ci/Dockerfile b/.ci/Dockerfile
index a9e2242..8a3172e 100644
--- a/.ci/Dockerfile
+++ b/.ci/Dockerfile
@@ -41,10 +41,10 @@
 USER ${RUN_USER}
 
 # Install aospless package (produced by GloDroid/aospext)
-RUN wget -P ${USER_HOME} https://gitlab.freedesktop.org/-/project/5/uploads/97f99b51143107ba02a51cf1c0ddb542/aospless_drm_hwcomposer_arm64.tar.xz && \
+RUN wget -P ${USER_HOME} https://gitlab.freedesktop.org/-/project/5/uploads/cafa930dad28acf7ee44d50101d5e8f0/aospless_drm_hwcomposer_arm64.tar.xz && \
     cd ${USER_HOME} && \
     sha256sum aospless_drm_hwcomposer_arm64.tar.xz && \
-    (echo dae29adb121f51e59c95fb7b29e0f7aed5b2983d10c7f1d5f1b9fd551c4bbb47 aospless_drm_hwcomposer_arm64.tar.xz | sha256sum --check) && \
+    (echo f792b1140861112f80c8a3a22e1af8e3eccf4910fe4449705e62d2032b713bf9 aospless_drm_hwcomposer_arm64.tar.xz | sha256sum --check) && \
     tar xf aospless_drm_hwcomposer_arm64.tar.xz && ln -s ../drm_hwcomposer/ ${USER_HOME}/aospless/src
 
 # Create project path
diff --git a/.ci/Makefile b/.ci/Makefile
index 051a437..0dba6cc 100644
--- a/.ci/Makefile
+++ b/.ci/Makefile
@@ -16,8 +16,6 @@
 CXXARGS := $(subst [BASE_DIR],$(BASE_DIR),$(CXXARGS))
 # clang-tidy doesn't like -mcpu=xxx flag
 CXXARGS := $(patsubst -mcpu=%,,$(CXXARGS))
-# TODO: build aospless with gtest enabled and remove line below
-CXXARGS := $(subst -nostdlibinc,,$(CXXARGS))
 CXXARGS += -I. -I./tests/test_include $(CXXFLAGS)
 
 TIDY_FILES_OVERRIDE := \
@@ -55,14 +53,6 @@
     -readability-redundant-member-init                  \
     -cppcoreguidelines-avoid-const-or-ref-data-members  \
     -cert-err33-c                                       \
-    -readability-math-missing-parentheses               \
-    -readability-avoid-unconditional-preprocessor-if    \
-    -modernize-type-traits                              \
-    -clang-analyzer-optin.core.EnumCastOutOfRange       \
-    -performance-inefficient-vector-operation           \
-    -readability-static-accessed-through-instance       \
-    -misc-use-internal-linkage                          \
-    -performance-avoid-endl                             \
 
 TIDY_CHECKS_NORMAL :=                                   \
     $(TIDY_CHECKS_FINE)                                 \
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9f3d022..fbe2a85 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -26,12 +26,13 @@
     - cd ..
     - rm -f aospless_drm_hwcomposer_arm64.tar.xz
     - rm -rf aospless/*
-    - wget https://gitlab.freedesktop.org/-/project/5/uploads/97f99b51143107ba02a51cf1c0ddb542/aospless_drm_hwcomposer_arm64.tar.xz
+    - wget https://gitlab.freedesktop.org/-/project/5/uploads/cafa930dad28acf7ee44d50101d5e8f0/aospless_drm_hwcomposer_arm64.tar.xz
     - tar xf aospless_drm_hwcomposer_arm64.tar.xz
     - rm -rf aospless/src
     - ln -s ../drm-hwcomposer/ aospless/src
     - make -C ./aospless install
     - cp -r aospless/install/* drm-hwcomposer/install/arm64
+    - ls drm-hwcomposer/install/arm64
 
   artifacts:
     paths:
@@ -44,7 +45,7 @@
     - cd ..
     - rm -f aospless_drm_hwcomposer_arm64.tar.xz
     - rm -rf aospless/*
-    - wget https://gitlab.freedesktop.org/-/project/5/uploads/97f99b51143107ba02a51cf1c0ddb542/aospless_drm_hwcomposer_arm64.tar.xz
+    - wget https://gitlab.freedesktop.org/-/project/5/uploads/cafa930dad28acf7ee44d50101d5e8f0/aospless_drm_hwcomposer_arm64.tar.xz
     - tar xf aospless_drm_hwcomposer_arm64.tar.xz
     - cd -
     - make -j$(nproc) -k -f .ci/Makefile
diff --git a/Android.bp b/Android.bp
index 2cbdc44..3951949 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,6 +56,7 @@
 
     static_libs: [
         "libaidlcommonsupport",
+        "libdisplay_info",
     ],
 
     header_libs: [
@@ -68,6 +69,7 @@
     ],
 
     cppflags: [
+        "-DHAS_LIBDISPLAY_INFO",
         "-DHWC2_INCLUDE_STRINGIFICATION",
         "-DHWC2_USE_CPP11",
     ],
@@ -117,6 +119,7 @@
         "hwc2_device/HwcLayer.cpp",
         "hwc2_device/hwc2_device.cpp",
 
+        "utils/LibdisplayEdidWrapper.cpp",
         "utils/fd.cpp",
         "utils/properties.cpp",
     ],
@@ -154,32 +157,6 @@
     ],
 }
 
-// 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"],
-    srcs: [
-        ":drm_hwcomposer_common",
-        "bufferinfo/legacy/BufferInfoLibdrm.cpp",
-    ],
-    cflags: ["-DUSE_IMAPPER4_METADATA_API"],
-}
-
-cc_library_shared {
-    name: "hwcomposer.drm_minigbm",
-    defaults: ["hwcomposer.drm_defaults"],
-    srcs: [
-        ":drm_hwcomposer_common",
-        "bufferinfo/legacy/BufferInfoMinigbm.cpp",
-    ],
-}
-
 cc_defaults {
     name: "android.hardware.composer.hwc3-service.drm.defaults",
 
diff --git a/METADATA b/METADATA
index 0c9c1ce..dacef3a 100644
--- a/METADATA
+++ b/METADATA
@@ -1,15 +1,19 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update external/drm_hwcomposer
+# For more info, check https://cs.android.com/android/platform/superproject/main/+/main:tools/external_updater/README.md
+
 name: "drm_hwcomposer"
 description: "KMS-based HWComposer implementation."
 third_party {
   license_type: NOTICE
   last_upgrade_date {
     year: 2025
-    month: 1
-    day: 14
+    month: 2
+    day: 4
   }
   identifier {
     type: "Git"
     value: "https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer"
-    version: "a29289940d7cf806029a36e2e01667708294c67b"
+    version: "851ea4dc268c4da612c65a5dfedbd3ed85960405"
   }
 }
diff --git a/compositor/DisplayInfo.h b/compositor/DisplayInfo.h
index 6ddc66f..f580d99 100644
--- a/compositor/DisplayInfo.h
+++ b/compositor/DisplayInfo.h
@@ -18,6 +18,31 @@
 
 #include <cstdint>
 
+/*
+ * Display colorimetry enums.
+ */
+// NOLINTBEGIN(readability-identifier-naming)
+enum class Colormode : int32_t {
+  kNative,
+  kBt601_625,
+  kBt601_625Unadjusted,
+  kBt601_525,
+  kBt601_525Unadjusted,
+  kBt709,
+  kDciP3,
+  kSrgb,
+  kAdobeRgb,
+  kDisplayP3,
+  kBt2020,
+  kBt2100Pq,
+  kBt2100Hlg,
+  kDisplayBt2020,
+};
+// NOLINTEND(readability-identifier-naming)
+
+/**
+ * Display panel colorspace property values.
+ */
 enum class Colorspace : int32_t {
   kDefault,
   kSmpte170MYcc,
diff --git a/compositor/LayerData.h b/compositor/LayerData.h
index a808adc..7eb6cba 100644
--- a/compositor/LayerData.h
+++ b/compositor/LayerData.h
@@ -34,13 +34,11 @@
 class DrmFbIdHandle;
 
 /* Rotation is defined in the clockwise direction */
-enum LayerTransform : uint32_t {
-  kIdentity = 0,
-  kFlipH = 1 << 0,
-  kFlipV = 1 << 1,
-  kRotate90 = 1 << 2,
-  kRotate180 = 1 << 3,
-  kRotate270 = 1 << 4,
+/* The flip is done before rotation */
+struct LayerTransform {
+  bool hflip;
+  bool vflip;
+  bool rotate90;
 };
 
 struct PresentInfo {
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
index 9a8769a..9ce9a93 100644
--- a/drm/DrmAtomicStateManager.cpp
+++ b/drm/DrmAtomicStateManager.cpp
@@ -150,6 +150,21 @@
       return -EINVAL;
   }
 
+  if (args.hdr_metadata && connector->GetHdrOutputMetadataProperty()) {
+    auto blob = drm->RegisterUserPropertyBlob(args.hdr_metadata.get(),
+                                              sizeof(hdr_output_metadata));
+    new_frame_state.hdr_metadata_blob = std::move(blob);
+    if (!new_frame_state.hdr_metadata_blob) {
+      ALOGE("Failed to create %s blob",
+            connector->GetHdrOutputMetadataProperty().GetName().c_str());
+      return -EINVAL;
+    }
+
+    if (!connector->GetHdrOutputMetadataProperty()
+             .AtomicSet(*pset, *new_frame_state.hdr_metadata_blob))
+      return -EINVAL;
+  }
+
   auto unused_planes = new_frame_state.used_planes;
 
   if (args.composition) {
diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h
index 8d22b99..4af04d1 100644
--- a/drm/DrmAtomicStateManager.h
+++ b/drm/DrmAtomicStateManager.h
@@ -40,6 +40,7 @@
   std::shared_ptr<drm_color_ctm> color_matrix;
   std::optional<Colorspace> colorspace;
   std::optional<int32_t> content_type;
+  std::shared_ptr<hdr_output_metadata> hdr_metadata;
 
   std::shared_ptr<DrmFbIdHandle> writeback_fb;
   SharedFd writeback_release_fence;
@@ -84,6 +85,7 @@
 
     DrmModeUserPropertyBlobUnique mode_blob;
     DrmModeUserPropertyBlobUnique ctm_blob;
+    DrmModeUserPropertyBlobUnique hdr_metadata_blob;
 
     int release_fence_pt_index{};
 
diff --git a/drm/DrmConnector.cpp b/drm/DrmConnector.cpp
index eeec3b1..37e1be4 100644
--- a/drm/DrmConnector.cpp
+++ b/drm/DrmConnector.cpp
@@ -89,6 +89,12 @@
   }
 
   UpdateEdidProperty();
+#if HAS_LIBDISPLAY_INFO
+  auto edid = LibdisplayEdidWrapper::Create(GetEdidBlob());
+  edid_wrapper_ = edid ? std::move(edid) : std::make_unique<EdidWrapper>();
+#else
+  edid_wrapper_ = std::make_unique<EdidWrapper>();
+#endif
 
   if (IsWriteback() &&
       (!GetConnectorProperty("WRITEBACK_PIXEL_FORMATS",
@@ -99,8 +105,7 @@
     return false;
   }
 
-  if (GetConnectorProperty("Colorspace", &colorspace_property_,
-                           /*is_optional=*/true)) {
+  if (GetOptionalConnectorProperty("Colorspace", &colorspace_property_)) {
     colorspace_property_.AddEnumToMap("Default", Colorspace::kDefault,
                                       colorspace_enum_map_);
     colorspace_property_.AddEnumToMap("SMPTE_170M_YCC", Colorspace::kSmpte170MYcc,
@@ -129,17 +134,19 @@
                                       colorspace_enum_map_);
     colorspace_property_.AddEnumToMap("RGB_WIDE_FIXED", Colorspace::kRgbWideFixed,
                                       colorspace_enum_map_);
-    colorspace_property_.AddEnumToMap("RGB_WIDE_FLOAT", Colorspace::kRgbWideFloat,
+    colorspace_property_.AddEnumToMap("RGB_WIDE_FLOAT",
+                                      Colorspace::kRgbWideFloat,
                                       colorspace_enum_map_);
     colorspace_property_.AddEnumToMap("BT601_YCC", Colorspace::kBt601Ycc,
                                       colorspace_enum_map_);
   }
 
-  GetConnectorProperty("content type", &content_type_property_,
-                       /*is_optional=*/true);
+  GetOptionalConnectorProperty("content type", &content_type_property_);
 
-  if (GetConnectorProperty("panel orientation", &panel_orientation_,
-                           /*is_optional=*/true)) {
+  GetOptionalConnectorProperty("HDR_OUTPUT_METADATA",
+                               &hdr_output_metadata_property_);
+
+  if (GetOptionalConnectorProperty("panel orientation", &panel_orientation_)) {
     panel_orientation_
         .AddEnumToMapReverse("Normal",
                              PanelOrientation::kModePanelOrientationNormal,
@@ -162,9 +169,7 @@
 }
 
 int DrmConnector::UpdateEdidProperty() {
-  return GetConnectorProperty("EDID", &edid_property_, /*is_optional=*/true)
-             ? 0
-             : -EINVAL;
+  return GetOptionalConnectorProperty("EDID", &edid_property_) ? 0 : -EINVAL;
 }
 
 auto DrmConnector::GetEdidBlob() -> DrmModePropertyBlobUnique {
diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h
index be84ae3..c22d059 100644
--- a/drm/DrmConnector.h
+++ b/drm/DrmConnector.h
@@ -27,11 +27,14 @@
 #include "DrmProperty.h"
 #include "DrmUnique.h"
 #include "compositor/DisplayInfo.h"
+#include "utils/EdidWrapper.h"
 
 namespace android {
 
 class DrmDevice;
 
+using EdidWrapperUnique = std::unique_ptr<EdidWrapper>;
+
 class DrmConnector : public PipelineBindable<DrmConnector> {
  public:
   static auto CreateInstance(DrmDevice &dev, uint32_t connector_id,
@@ -42,6 +45,9 @@
 
   int UpdateEdidProperty();
   auto GetEdidBlob() -> DrmModePropertyBlobUnique;
+  auto GetParsedEdid() -> EdidWrapperUnique & {
+    return edid_wrapper_;
+  }
 
   auto GetDev() const -> DrmDevice & {
     return *drm_;
@@ -109,6 +115,10 @@
     return content_type_property_;
   }
 
+  auto &GetHdrOutputMetadataProperty() const {
+    return hdr_output_metadata_property_;
+  }
+
   auto &GetWritebackFbIdProperty() const {
     return writeback_fb_id_;
   }
@@ -147,6 +157,12 @@
   auto Init() -> bool;
   auto GetConnectorProperty(const char *prop_name, DrmProperty *property,
                             bool is_optional = false) -> bool;
+  auto GetOptionalConnectorProperty(const char *prop_name,
+                                    DrmProperty *property) -> bool {
+    return GetConnectorProperty(prop_name, property, /*is_optional=*/true);
+  }
+
+  EdidWrapperUnique edid_wrapper_;
 
   const uint32_t index_in_res_array_;
 
@@ -157,6 +173,7 @@
   DrmProperty edid_property_;
   DrmProperty colorspace_property_;
   DrmProperty content_type_property_;
+  DrmProperty hdr_output_metadata_property_;
 
   DrmProperty link_status_property_;
   DrmProperty writeback_pixel_formats_;
diff --git a/drm/DrmPlane.cpp b/drm/DrmPlane.cpp
index dbb5ad6..0010742 100644
--- a/drm/DrmPlane.cpp
+++ b/drm/DrmPlane.cpp
@@ -88,22 +88,8 @@
 
   GetPlaneProperty("zpos", zpos_property_, Presence::kOptional);
 
-  /* DRM/KMS uses counter-clockwise rotations, while HWC API uses
-   * clockwise. That's why 90 and 270 are swapped here.
-   */
   if (GetPlaneProperty("rotation", rotation_property_, Presence::kOptional)) {
-    rotation_property_.AddEnumToMap("rotate-0", LayerTransform::kIdentity,
-                                    transform_enum_map_);
-    rotation_property_.AddEnumToMap("rotate-90", LayerTransform::kRotate270,
-                                    transform_enum_map_);
-    rotation_property_.AddEnumToMap("rotate-180", LayerTransform::kRotate180,
-                                    transform_enum_map_);
-    rotation_property_.AddEnumToMap("rotate-270", LayerTransform::kRotate90,
-                                    transform_enum_map_);
-    rotation_property_.AddEnumToMap("reflect-x", LayerTransform::kFlipH,
-                                    transform_enum_map_);
-    rotation_property_.AddEnumToMap("reflect-y", LayerTransform::kFlipV,
-                                    transform_enum_map_);
+    rotation_property_.GetEnumMask(transform_enum_mask_);
   }
 
   GetPlaneProperty("alpha", alpha_property_, Presence::kOptional);
@@ -166,22 +152,40 @@
   return ((1 << crtc.GetIndexInResArray()) & plane_->possible_crtcs) != 0;
 }
 
+static uint64_t ToDrmRotation(LayerTransform transform) {
+  /* DRM/KMS uses counter-clockwise rotations, while HWC API uses
+   * clockwise. That's why 90 and 270 are swapped here.
+   */
+  uint64_t rotation = DRM_MODE_ROTATE_0;
+
+  if (transform.rotate90) {
+    rotation |= DRM_MODE_ROTATE_270;
+  }
+
+  if (transform.hflip) {
+    rotation |= DRM_MODE_REFLECT_X;
+  }
+
+  if (transform.vflip) {
+    rotation |= DRM_MODE_REFLECT_Y;
+  }
+
+  // TODO(nobody): Respect transform_enum_mask_ to find alternative rotation
+  // values
+
+  return rotation;
+}
+
 bool DrmPlane::IsValidForLayer(LayerData *layer) {
   if (layer == nullptr || !layer->bi) {
     ALOGE("%s: Invalid parameters", __func__);
     return false;
   }
 
-  if (!rotation_property_) {
-    if (layer->pi.transform != LayerTransform::kIdentity) {
-      ALOGV("No rotation property on plane %d", GetId());
-      return false;
-    }
-  } else {
-    if (transform_enum_map_.count(layer->pi.transform) == 0) {
-      ALOGV("Transform is not supported on plane %d", GetId());
-      return false;
-    }
+  uint64_t drm_rotation = ToDrmRotation(layer->pi.transform);
+  if ((drm_rotation & transform_enum_mask_) != drm_rotation) {
+    ALOGV("Transform is not supported on plane %d", GetId());
+    return false;
   }
 
   if (!alpha_property_ && layer->pi.alpha != UINT16_MAX) {
@@ -218,27 +222,6 @@
                           }) != std::end(formats_);
 }
 
-static uint64_t ToDrmRotation(LayerTransform transform) {
-  uint64_t rotation = 0;
-  /* DRM/KMS uses counter-clockwise rotations, while HWC API uses
-   * clockwise. That's why 90 and 270 are swapped here.
-   */
-  if ((transform & LayerTransform::kFlipH) != 0)
-    rotation |= DRM_MODE_REFLECT_X;
-  if ((transform & LayerTransform::kFlipV) != 0)
-    rotation |= DRM_MODE_REFLECT_Y;
-  if ((transform & LayerTransform::kRotate90) != 0)
-    rotation |= DRM_MODE_ROTATE_270;
-  else if ((transform & LayerTransform::kRotate180) != 0)
-    rotation |= DRM_MODE_ROTATE_180;
-  else if ((transform & LayerTransform::kRotate270) != 0)
-    rotation |= DRM_MODE_ROTATE_90;
-  else
-    rotation |= DRM_MODE_ROTATE_0;
-
-  return rotation;
-}
-
 /* Convert float to 16.16 fixed point */
 static int To1616FixPt(float in) {
   constexpr int kBitShift = 16;
diff --git a/drm/DrmPlane.h b/drm/DrmPlane.h
index c26a3cc..24d21c8 100644
--- a/drm/DrmPlane.h
+++ b/drm/DrmPlane.h
@@ -96,6 +96,6 @@
   std::map<BufferBlendMode, uint64_t> blending_enum_map_;
   std::map<BufferColorSpace, uint64_t> color_encoding_enum_map_;
   std::map<BufferSampleRange, uint64_t> color_range_enum_map_;
-  std::map<LayerTransform, uint64_t> transform_enum_map_;
+  uint64_t transform_enum_mask_ = DRM_MODE_ROTATE_0;
 };
 }  // namespace android
diff --git a/drm/DrmProperty.cpp b/drm/DrmProperty.cpp
index dbd307e..8dc62f6 100644
--- a/drm/DrmProperty.cpp
+++ b/drm/DrmProperty.cpp
@@ -144,4 +144,24 @@
   return {};
 }
 
+auto DrmProperty::GetEnumMask(uint64_t &mask) -> bool {
+  if (enums_.empty()) {
+    ALOGE("No enum values for property: %s", name_.c_str());
+    return false;
+  }
+
+  if (!IsBitmask()) {
+    ALOGE("Property %s is not a bitmask property.", name_.c_str());
+    return false;
+  }
+
+  mask = 0;
+
+  for (const auto &it : enums_) {
+    mask |= (1 << it.value);
+  }
+
+  return true;
+}
+
 }  // namespace android
diff --git a/drm/DrmProperty.h b/drm/DrmProperty.h
index 2683ad8..99bddb1 100644
--- a/drm/DrmProperty.h
+++ b/drm/DrmProperty.h
@@ -54,6 +54,10 @@
     return id_ != 0 && (flags_ & DRM_MODE_PROP_RANGE) != 0;
   }
 
+  bool IsBitmask() const {
+    return id_ != 0 && (flags_ & DRM_MODE_PROP_BITMASK) != 0;
+  }
+
   auto RangeMin() const -> std::tuple<int, uint64_t>;
   auto RangeMax() const -> std::tuple<int, uint64_t>;
 
@@ -68,6 +72,8 @@
   auto AddEnumToMapReverse(const std::string &name, E value,
                            std::map<uint64_t, E> &map) -> bool;
 
+  auto GetEnumMask(uint64_t &mask) -> bool;
+
   explicit operator bool() const {
     return id_ != 0;
   }
diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp
index 9c68816..c866263 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -124,7 +124,7 @@
   struct timespec ts {};
   clock_gettime(CLOCK_MONOTONIC, &ts);
   constexpr int64_t kNsInSec = 1000000000LL;
-  return int64_t(ts.tv_sec) * kNsInSec + int64_t(ts.tv_nsec);
+  return (int64_t(ts.tv_sec) * kNsInSec) + int64_t(ts.tv_nsec);
 }
 
 void ResourceManager::UpdateFrontendDisplays() {
diff --git a/drm/VSyncWorker.cpp b/drm/VSyncWorker.cpp
index 64152ab..defbe42 100644
--- a/drm/VSyncWorker.cpp
+++ b/drm/VSyncWorker.cpp
@@ -124,7 +124,7 @@
   if (last_timestamp_ < 0)
     return current + frame_ns;
 
-  return frame_ns * ((current - last_timestamp_) / frame_ns + 1) +
+  return (frame_ns * ((current - last_timestamp_) / frame_ns + 1)) +
          last_timestamp_;
 }
 
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 3469c85..16d8bac 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -21,7 +21,10 @@
 
 #include <cinttypes>
 
+#include <xf86drmMode.h>
+
 #include <hardware/gralloc.h>
+#include <ui/ColorSpace.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
 #include <ui/PixelFormat.h>
@@ -37,10 +40,61 @@
 #include "utils/properties.h"
 
 using ::android::DrmDisplayPipeline;
+using ColorGamut = ::android::ColorSpace;
 
 namespace android {
 
 namespace {
+
+constexpr int kCtmRows = 3;
+constexpr int kCtmCols = 3;
+
+constexpr std::array<float, 16> kIdentityMatrix = {
+    1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F,
+    0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F,
+};
+
+uint64_t To3132FixPt(float in) {
+  constexpr uint64_t kSignMask = (1ULL << 63);
+  constexpr uint64_t kValueMask = ~(1ULL << 63);
+  constexpr auto kValueScale = static_cast<float>(1ULL << 32);
+  if (in < 0)
+    return (static_cast<uint64_t>(-in * kValueScale) & kValueMask) | kSignMask;
+  return static_cast<uint64_t>(in * kValueScale) & kValueMask;
+}
+
+auto ToColorTransform(const std::array<float, 16> &color_transform_matrix) {
+  /* HAL provides a 4x4 float type matrix:
+   * | 0  1  2  3|
+   * | 4  5  6  7|
+   * | 8  9 10 11|
+   * |12 13 14 15|
+   *
+   * R_out = R*0 + G*4 + B*8 + 12
+   * G_out = R*1 + G*5 + B*9 + 13
+   * B_out = R*2 + G*6 + B*10 + 14
+   *
+   * DRM expects a 3x3 s31.32 fixed point matrix:
+   * out   matrix    in
+   * |R|   |0 1 2|   |R|
+   * |G| = |3 4 5| x |G|
+   * |B|   |6 7 8|   |B|
+   *
+   * R_out = R*0 + G*1 + B*2
+   * G_out = R*3 + G*4 + B*5
+   * B_out = R*6 + G*7 + B*8
+   */
+  auto color_matrix = std::make_shared<drm_color_ctm>();
+  for (int i = 0; i < kCtmCols; i++) {
+    for (int j = 0; j < kCtmRows; j++) {
+      constexpr int kInCtmRows = 4;
+      color_matrix->matrix[(i * kCtmRows) + j] = To3132FixPt(
+          color_transform_matrix[(j * kInCtmRows) + i]);
+    }
+  }
+  return color_matrix;
+}
+
 // Allocate a black buffer that can be used for an initial modeset when there.
 // is no appropriate client buffer available to be used.
 // Caller must free the returned buffer with GraphicBufferAllocator::free.
@@ -105,10 +159,38 @@
 }
 }  // namespace
 
+static BufferColorSpace Hwc2ToColorSpace(int32_t dataspace) {
+  switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+    case HAL_DATASPACE_STANDARD_BT709:
+      return BufferColorSpace::kItuRec709;
+    case HAL_DATASPACE_STANDARD_BT601_625:
+    case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+    case HAL_DATASPACE_STANDARD_BT601_525:
+    case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+      return BufferColorSpace::kItuRec601;
+    case HAL_DATASPACE_STANDARD_BT2020:
+    case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+      return BufferColorSpace::kItuRec2020;
+    default:
+      return BufferColorSpace::kUndefined;
+  }
+}
+
+static BufferSampleRange Hwc2ToSampleRange(int32_t dataspace) {
+  switch (dataspace & HAL_DATASPACE_RANGE_MASK) {
+    case HAL_DATASPACE_RANGE_FULL:
+      return BufferSampleRange::kFullRange;
+    case HAL_DATASPACE_RANGE_LIMITED:
+      return BufferSampleRange::kLimitedRange;
+    default:
+      return BufferSampleRange::kUndefined;
+  }
+}
+
 std::string HwcDisplay::DumpDelta(HwcDisplay::Stats delta) {
   if (delta.total_pixops_ == 0)
     return "No stats yet";
-  auto ratio = 1.0 - double(delta.gpu_pixops_) / double(delta.total_pixops_);
+  auto ratio = 1.0 - (double(delta.gpu_pixops_) / double(delta.total_pixops_));
 
   std::stringstream ss;
   ss << " Total frames count: " << delta.total_frames_ << "\n"
@@ -150,12 +232,30 @@
   }
 }
 
+void HwcDisplay::SetColorTransformMatrix(
+    const std::array<float, 16> &color_transform_matrix) {
+  auto almost_equal = [](auto a, auto b) {
+    const float epsilon = 0.001F;
+    return std::abs(a - b) < epsilon;
+  };
+  const bool is_identity = std::equal(color_transform_matrix.begin(),
+                                      color_transform_matrix.end(),
+                                      kIdentityMatrix.begin(), almost_equal);
+  color_transform_hint_ = is_identity ? HAL_COLOR_TRANSFORM_IDENTITY
+                                      : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+  if (color_transform_hint_ == is_identity) {
+    SetColorMatrixToIdentity();
+  } else {
+    color_matrix_ = ToColorTransform(color_transform_matrix);
+  }
+}
+
 void HwcDisplay::SetColorMatrixToIdentity() {
   color_matrix_ = std::make_shared<drm_color_ctm>();
   for (int i = 0; i < kCtmCols; i++) {
     for (int j = 0; j < kCtmRows; j++) {
       constexpr uint64_t kOne = (1ULL << 32); /* 1.0 in s31.32 format */
-      color_matrix_->matrix[i * kCtmRows + j] = (i == j) ? kOne : 0;
+      color_matrix_->matrix[(i * kCtmRows) + j] = (i == j) ? kOne : 0;
     }
   }
 
@@ -270,6 +370,68 @@
   return ConfigError::kNone;
 }
 
+auto HwcDisplay::ValidateStagedComposition() -> std::vector<ChangedLayer> {
+  if (IsInHeadlessMode()) {
+    return {};
+  }
+
+  /* In current drm_hwc design in case previous frame layer was not validated as
+   * a CLIENT, it is used by display controller (Front buffer). We have to store
+   * this state to provide the CLIENT with the release fences for such buffers.
+   */
+  for (auto &l : layers_) {
+    l.second.SetPriorBufferScanOutFlag(l.second.GetValidatedType() !=
+                                       HWC2::Composition::Client);
+  }
+
+  // ValidateDisplay returns the number of layers that may be changed.
+  uint32_t num_types = 0;
+  uint32_t num_requests = 0;
+  backend_->ValidateDisplay(this, &num_types, &num_requests);
+
+  if (num_types == 0) {
+    return {};
+  }
+
+  // Iterate through the layers to find which layers actually changed.
+  std::vector<ChangedLayer> changed_layers;
+  for (auto &l : layers_) {
+    if (l.second.IsTypeChanged()) {
+      changed_layers.emplace_back(l.first, l.second.GetValidatedType());
+    }
+  }
+  return changed_layers;
+}
+
+auto HwcDisplay::AcceptValidatedComposition() -> void {
+  for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) {
+    l.second.AcceptTypeChange();
+  }
+}
+
+auto HwcDisplay::PresentStagedComposition(
+    SharedFd &out_present_fence, std::vector<ReleaseFence> &out_release_fences)
+    -> bool {
+  int out_fd = -1;
+  auto error = PresentDisplay(&out_fd);
+  out_present_fence = MakeSharedFd(out_fd);
+  if (error != HWC2::Error::None) {
+    return false;
+  }
+
+  if (!out_present_fence) {
+    return true;
+  }
+
+  for (auto &l : layers_) {
+    if (l.second.GetPriorBufferScanOutFlag()) {
+      out_release_fences.emplace_back(l.first, out_present_fence);
+    }
+  }
+
+  return true;
+}
+
 void HwcDisplay::SetPipeline(std::shared_ptr<DrmDisplayPipeline> pipeline) {
   Deinit();
 
@@ -330,7 +492,9 @@
     flatcon_ = FlatteningController::CreateInstance(flatcbk);
   }
 
-  client_layer_.SetLayerBlendMode(HWC2_BLEND_MODE_PREMULTIPLIED);
+  HwcLayer::LayerProperties lp;
+  lp.blend_mode = BufferBlendMode::kPreMult;
+  client_layer_.SetLayerProperties(lp);
 
   SetColorMatrixToIdentity();
 
@@ -449,11 +613,23 @@
 }
 
 HWC2::Error HwcDisplay::GetColorModes(uint32_t *num_modes, int32_t *modes) {
-  if (!modes)
-    *num_modes = 1;
+  if (!modes) {
+    std::vector<Colormode> temp_modes;
+    GetEdid()->GetColorModes(temp_modes);
+    *num_modes = temp_modes.size();
+    return HWC2::Error::None;
+  }
 
-  if (modes)
-    *modes = HAL_COLOR_MODE_NATIVE;
+  std::vector<Colormode> temp_modes;
+  std::vector<int32_t> out_modes(modes, modes + *num_modes);
+  GetEdid()->GetColorModes(temp_modes);
+  if (temp_modes.empty()) {
+    out_modes.emplace_back(HAL_COLOR_MODE_NATIVE);
+    return HWC2::Error::None;
+  }
+
+  for (auto &c : temp_modes)
+    out_modes.emplace_back(static_cast<int32_t>(c));
 
   return HWC2::Error::None;
 }
@@ -569,12 +745,35 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetHdrCapabilities(uint32_t *num_types,
-                                           int32_t * /*types*/,
-                                           float * /*max_luminance*/,
-                                           float * /*max_average_luminance*/,
-                                           float * /*min_luminance*/) {
-  *num_types = 0;
+HWC2::Error HwcDisplay::GetHdrCapabilities(uint32_t *num_types, int32_t *types,
+                                           float *max_luminance,
+                                           float *max_average_luminance,
+                                           float *min_luminance) {
+  if (!types) {
+    std::vector<ui::Hdr> temp_types;
+    float lums[3] = {0.F};
+    GetEdid()->GetHdrCapabilities(temp_types, &lums[0], &lums[1], &lums[2]);
+    *num_types = temp_types.size();
+    return HWC2::Error::None;
+  }
+
+  std::vector<ui::Hdr> temp_types;
+  std::vector<int32_t> out_types(types, types + *num_types);
+  GetEdid()->GetHdrCapabilities(temp_types, max_luminance,
+                                max_average_luminance, min_luminance);
+  for (auto &t : temp_types) {
+    switch (t) {
+      case ui::Hdr::HDR10:
+        out_types.emplace_back(HAL_HDR_HDR10);
+        break;
+      case ui::Hdr::HLG:
+        out_types.emplace_back(HAL_HDR_HLG);
+        break;
+      default:
+        // Ignore any other HDR types
+        break;
+    }
+  }
   return HWC2::Error::None;
 }
 
@@ -625,6 +824,7 @@
   args.color_matrix = color_matrix_;
   args.content_type = content_type_;
   args.colorspace = colorspace_;
+  args.hdr_metadata = hdr_metadata_;
 
   std::vector<LayerData> composition_layers;
   if (modeset_layer) {
@@ -654,6 +854,7 @@
   a_args.color_matrix = color_matrix_;
   a_args.content_type = content_type_;
   a_args.colorspace = colorspace_;
+  a_args.hdr_metadata = hdr_metadata_;
 
   uint32_t prev_vperiod_ns = 0;
   GetDisplayVsyncPeriod(&prev_vperiod_ns);
@@ -666,11 +867,14 @@
     if (staged_config == nullptr) {
       return HWC2::Error::BadConfig;
     }
-    client_layer_.SetLayerDisplayFrame(
-        (hwc_rect_t){.left = 0,
-                     .top = 0,
-                     .right = int(staged_config->mode.GetRawMode().hdisplay),
-                     .bottom = int(staged_config->mode.GetRawMode().vdisplay)});
+    HwcLayer::LayerProperties lp;
+    lp.display_frame = {
+        .left = 0,
+        .top = 0,
+        .right = int(staged_config->mode.GetRawMode().hdisplay),
+        .bottom = int(staged_config->mode.GetRawMode().vdisplay),
+    };
+    client_layer_.SetLayerProperties(lp);
 
     configs_.active_config_id = staged_mode_config_id_.value();
     a_args.display_mode = staged_config->mode;
@@ -828,8 +1032,12 @@
                                         int32_t acquire_fence,
                                         int32_t dataspace,
                                         hwc_region_t /*damage*/) {
-  client_layer_.SetLayerBuffer(target, acquire_fence);
-  client_layer_.SetLayerDataspace(dataspace);
+  HwcLayer::LayerProperties lp;
+  lp.buffer = {.buffer_handle = target,
+               .acquire_fence = MakeSharedFd(acquire_fence)};
+  lp.color_space = Hwc2ToColorSpace(dataspace);
+  lp.sample_range = Hwc2ToSampleRange(dataspace);
+  client_layer_.SetLayerProperties(lp);
 
   /*
    * target can be nullptr, this does mean the Composer Service is calling
@@ -857,11 +1065,12 @@
     return HWC2::Error::BadLayer;
   }
 
-  auto source_crop = (hwc_frect_t){.left = 0.0F,
-                                   .top = 0.0F,
-                                   .right = static_cast<float>(bi->width),
-                                   .bottom = static_cast<float>(bi->height)};
-  client_layer_.SetLayerSourceCrop(source_crop);
+  lp = {};
+  lp.source_crop = {.left = 0.0F,
+                    .top = 0.0F,
+                    .right = float(bi->width),
+                    .bottom = float(bi->height)};
+  client_layer_.SetLayerProperties(lp);
 
   return HWC2::Error::None;
 }
@@ -870,29 +1079,47 @@
   /* Maps to the Colorspace DRM connector property:
    * https://elixir.bootlin.com/linux/v6.11/source/include/drm/drm_connector.h#L538
    */
-  if (mode < HAL_COLOR_MODE_NATIVE || mode > HAL_COLOR_MODE_DISPLAY_P3)
+  if (mode < HAL_COLOR_MODE_NATIVE || mode > HAL_COLOR_MODE_DISPLAY_BT2020)
     return HWC2::Error::BadParameter;
 
   switch (mode) {
     case HAL_COLOR_MODE_NATIVE:
+      hdr_metadata_.reset();
       colorspace_ = Colorspace::kDefault;
       break;
     case HAL_COLOR_MODE_STANDARD_BT601_625:
     case HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED:
     case HAL_COLOR_MODE_STANDARD_BT601_525:
     case HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED:
+      hdr_metadata_.reset();
       // The DP spec does not say whether this is the 525 or the 625 line version.
       colorspace_ = Colorspace::kBt601Ycc;
       break;
     case HAL_COLOR_MODE_STANDARD_BT709:
     case HAL_COLOR_MODE_SRGB:
+      hdr_metadata_.reset();
       colorspace_ = Colorspace::kBt709Ycc;
       break;
     case HAL_COLOR_MODE_DCI_P3:
     case HAL_COLOR_MODE_DISPLAY_P3:
+      hdr_metadata_.reset();
       colorspace_ = Colorspace::kDciP3RgbD65;
       break;
+    case HAL_COLOR_MODE_DISPLAY_BT2020: {
+      std::vector<ui::Hdr> hdr_types;
+      GetEdid()->GetSupportedHdrTypes(hdr_types);
+      if (!hdr_types.empty()) {
+        auto ret = SetHdrOutputMetadata(hdr_types.front());
+        if (ret != HWC2::Error::None)
+          return ret;
+      }
+      colorspace_ = Colorspace::kBt2020Rgb;
+      break;
+    }
     case HAL_COLOR_MODE_ADOBE_RGB:
+    case HAL_COLOR_MODE_BT2020:
+    case HAL_COLOR_MODE_BT2100_PQ:
+    case HAL_COLOR_MODE_BT2100_HLG:
     default:
       return HWC2::Error::Unsupported;
   }
@@ -901,17 +1128,6 @@
   return HWC2::Error::None;
 }
 
-#include <xf86drmMode.h>
-
-static uint64_t To3132FixPt(float in) {
-  constexpr uint64_t kSignMask = (1ULL << 63);
-  constexpr uint64_t kValueMask = ~(1ULL << 63);
-  constexpr auto kValueScale = static_cast<float>(1ULL << 32);
-  if (in < 0)
-    return (static_cast<uint64_t>(-in * kValueScale) & kValueMask) | kSignMask;
-  return static_cast<uint64_t>(in * kValueScale) & kValueMask;
-}
-
 HWC2::Error HwcDisplay::SetColorTransform(const float *matrix, int32_t hint) {
   if (hint < HAL_COLOR_TRANSFORM_IDENTITY ||
       hint > HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA)
@@ -934,37 +1150,14 @@
       break;
     case HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX:
       // Without HW support, we cannot correctly process matrices with an offset.
-      for (int i = 12; i < 14; i++) {
-        if (matrix[i] != 0.F)
-          return HWC2::Error::Unsupported;
-      }
-
-      /* HAL provides a 4x4 float type matrix:
-       * | 0  1  2  3|
-       * | 4  5  6  7|
-       * | 8  9 10 11|
-       * |12 13 14 15|
-       *
-       * R_out = R*0 + G*4 + B*8 + 12
-       * G_out = R*1 + G*5 + B*9 + 13
-       * B_out = R*2 + G*6 + B*10 + 14
-       *
-       * DRM expects a 3x3 s31.32 fixed point matrix:
-       * out   matrix    in
-       * |R|   |0 1 2|   |R|
-       * |G| = |3 4 5| x |G|
-       * |B|   |6 7 8|   |B|
-       *
-       * R_out = R*0 + G*1 + B*2
-       * G_out = R*3 + G*4 + B*5
-       * B_out = R*6 + G*7 + B*8
-       */
-      color_matrix_ = std::make_shared<drm_color_ctm>();
-      for (int i = 0; i < kCtmCols; i++) {
-        for (int j = 0; j < kCtmRows; j++) {
-          constexpr int kInCtmRows = 4;
-          color_matrix_->matrix[i * kCtmRows + j] = To3132FixPt(matrix[j * kInCtmRows + i]);
+      {
+        for (int i = 12; i < 14; i++) {
+          if (matrix[i] != 0.F)
+            return HWC2::Error::Unsupported;
         }
+        std::array<float, 16> aidl_matrix = kIdentityMatrix;
+        memcpy(aidl_matrix.data(), matrix, aidl_matrix.size() * sizeof(float));
+        color_matrix_ = ToColorTransform(aidl_matrix);
       }
       break;
     default:
@@ -989,7 +1182,10 @@
 
 HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t buffer,
                                         int32_t release_fence) {
-  writeback_layer_->SetLayerBuffer(buffer, release_fence);
+  HwcLayer::LayerProperties lp;
+  lp.buffer = {.buffer_handle = buffer,
+               .acquire_fence = MakeSharedFd(release_fence)};
+  writeback_layer_->SetLayerProperties(lp);
   writeback_layer_->PopulateLayerData();
   if (!writeback_layer_->IsLayerUsableAsDevice()) {
     ALOGE("Output layer must be always usable by DRM/KMS");
@@ -1107,6 +1303,61 @@
                              (int32_t *)(outVsyncPeriod));
 }
 
+// Display primary values are coded as unsigned 16-bit values in units of
+// 0.00002, where 0x0000 represents zero and 0xC350 represents 1.0000.
+static uint64_t ToU16ColorValue(float in) {
+  constexpr float kPrimariesFixedPoint = 50000.F;
+  return static_cast<uint64_t>(kPrimariesFixedPoint * in);
+}
+
+HWC2::Error HwcDisplay::SetHdrOutputMetadata(ui::Hdr type) {
+  hdr_metadata_ = std::make_shared<hdr_output_metadata>();
+  hdr_metadata_->metadata_type = 0;
+  auto *m = &hdr_metadata_->hdmi_metadata_type1;
+  m->metadata_type = 0;
+
+  switch (type) {
+    case ui::Hdr::HDR10:
+      m->eotf = 2;  // PQ
+      break;
+    case ui::Hdr::HLG:
+      m->eotf = 3;  // HLG
+      break;
+    default:
+      return HWC2::Error::Unsupported;
+  }
+
+  // Most luminance values are coded as an unsigned 16-bit value in units of 1
+  // cd/m2, where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
+  std::vector<ui::Hdr> types;
+  float hdr_luminance[3]{0.F, 0.F, 0.F};
+  GetEdid()->GetHdrCapabilities(types, &hdr_luminance[0], &hdr_luminance[1],
+                                &hdr_luminance[2]);
+  m->max_display_mastering_luminance = m->max_cll = static_cast<uint64_t>(
+      hdr_luminance[0]);
+  m->max_fall = static_cast<uint64_t>(hdr_luminance[1]);
+  // The min luminance value is coded as an unsigned 16-bit value in units of
+  // 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
+  // represents 6.5535 cd/m2.
+  m->min_display_mastering_luminance = static_cast<uint64_t>(hdr_luminance[2] *
+                                                             10000.F);
+
+  auto gamut = ColorGamut::BT2020();
+  auto primaries = gamut.getPrimaries();
+  m->display_primaries[0].x = ToU16ColorValue(primaries[0].x);
+  m->display_primaries[0].y = ToU16ColorValue(primaries[0].y);
+  m->display_primaries[1].x = ToU16ColorValue(primaries[1].x);
+  m->display_primaries[1].y = ToU16ColorValue(primaries[1].y);
+  m->display_primaries[2].x = ToU16ColorValue(primaries[2].x);
+  m->display_primaries[2].y = ToU16ColorValue(primaries[2].y);
+
+  auto whitePoint = gamut.getWhitePoint();
+  m->white_point.x = ToU16ColorValue(whitePoint.x);
+  m->white_point.y = ToU16ColorValue(whitePoint.y);
+
+  return HWC2::Error::None;
+}
+
 #if __ANDROID_API__ > 29
 HWC2::Error HwcDisplay::GetDisplayConnectionType(uint32_t *outType) {
   if (IsInHeadlessMode()) {
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index acefff8..7522c8d 100644
--- a/hwc2_device/HwcDisplay.h
+++ b/hwc2_device/HwcDisplay.h
@@ -22,6 +22,8 @@
 #include <optional>
 #include <sstream>
 
+#include <ui/GraphicTypes.h>
+
 #include "HwcDisplayConfigs.h"
 #include "compositor/DisplayInfo.h"
 #include "compositor/FlatteningController.h"
@@ -52,6 +54,9 @@
   HwcDisplay(const HwcDisplay &) = delete;
   ~HwcDisplay();
 
+  void SetColorTransformMatrix(
+      const std::array<float, 16> &color_transform_matrix);
+
   /* SetPipeline should be carefully used only by DrmHwcTwo hotplug handlers */
   void SetPipeline(std::shared_ptr<DrmDisplayPipeline> pipeline);
 
@@ -86,6 +91,23 @@
   // Get the HwcDisplayConfig, or nullptor if none.
   auto GetConfig(hwc2_config_t config_id) const -> const HwcDisplayConfig *;
 
+  // To be called after SetDisplayProperties. Returns an empty vector if the
+  // requested layers have been validated, otherwise the vector describes
+  // the requested composition type changes.
+  using ChangedLayer = std::pair<hwc2_layer_t, HWC2::Composition>;
+  auto ValidateStagedComposition() -> std::vector<ChangedLayer>;
+
+  // Mark previously validated properties as ready to present.
+  auto AcceptValidatedComposition() -> void;
+
+  // Present previously staged properties, and return fences to indicate when
+  // the new content has been presented, and when the previous buffers have
+  // been released.
+  using ReleaseFence = std::pair<hwc2_layer_t, SharedFd>;
+  auto PresentStagedComposition(SharedFd &out_present_fence,
+                                std::vector<ReleaseFence> &out_release_fences)
+      -> bool;
+
   // HWC2 Hooks - these should not be used outside of the hwc2 device.
   HWC2::Error AcceptDisplayChanges();
   HWC2::Error CreateLayer(hwc2_layer_t *layer);
@@ -260,12 +282,11 @@
   uint16_t virtual_disp_width_{};
   uint16_t virtual_disp_height_{};
   int32_t color_mode_{};
-  static constexpr int kCtmRows = 3;
-  static constexpr int kCtmCols = 3;
   std::shared_ptr<drm_color_ctm> color_matrix_;
   android_color_transform_t color_transform_hint_{};
   int32_t content_type_{};
   Colorspace colorspace_{};
+  std::shared_ptr<hdr_output_metadata> hdr_metadata_;
 
   std::shared_ptr<DrmKmsPlan> current_plan_;
 
@@ -279,6 +300,10 @@
   HWC2::Error Init();
 
   HWC2::Error SetActiveConfigInternal(uint32_t config, int64_t change_time);
+  HWC2::Error SetHdrOutputMetadata(ui::Hdr hdrType);
+  auto GetEdid() -> EdidWrapperUnique & {
+    return GetPipe().connector->Get()->GetParsedEdid();
+  }
 };
 
 }  // namespace android
diff --git a/hwc2_device/HwcLayer.cpp b/hwc2_device/HwcLayer.cpp
index cb18fdd..1d1e118 100644
--- a/hwc2_device/HwcLayer.cpp
+++ b/hwc2_device/HwcLayer.cpp
@@ -60,148 +60,6 @@
   }
 }
 
-// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
-HWC2::Error HwcLayer::SetCursorPosition(int32_t /*x*/, int32_t /*y*/) {
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcLayer::SetLayerBlendMode(int32_t mode) {
-  switch (static_cast<HWC2::BlendMode>(mode)) {
-    case HWC2::BlendMode::None:
-      blend_mode_ = BufferBlendMode::kNone;
-      break;
-    case HWC2::BlendMode::Premultiplied:
-      blend_mode_ = BufferBlendMode::kPreMult;
-      break;
-    case HWC2::BlendMode::Coverage:
-      blend_mode_ = BufferBlendMode::kCoverage;
-      break;
-    default:
-      ALOGE("Unknown blending mode b=%d", mode);
-      blend_mode_ = BufferBlendMode::kUndefined;
-      break;
-  }
-  return HWC2::Error::None;
-}
-
-/* Find API details at:
- * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=2314
- */
-HWC2::Error HwcLayer::SetLayerBuffer(buffer_handle_t buffer,
-                                     int32_t acquire_fence) {
-  layer_data_.acquire_fence = MakeSharedFd(acquire_fence);
-  buffer_handle_ = buffer;
-  buffer_handle_updated_ = true;
-
-  return HWC2::Error::None;
-}
-
-// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
-HWC2::Error HwcLayer::SetLayerColor(hwc_color_t /*color*/) {
-  // TODO(nobody): Put to client composition here?
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcLayer::SetLayerCompositionType(int32_t type) {
-  sf_type_ = static_cast<HWC2::Composition>(type);
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcLayer::SetLayerDataspace(int32_t dataspace) {
-  switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
-    case HAL_DATASPACE_STANDARD_BT709:
-      color_space_ = BufferColorSpace::kItuRec709;
-      break;
-    case HAL_DATASPACE_STANDARD_BT601_625:
-    case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
-    case HAL_DATASPACE_STANDARD_BT601_525:
-    case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
-      color_space_ = BufferColorSpace::kItuRec601;
-      break;
-    case HAL_DATASPACE_STANDARD_BT2020:
-    case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
-      color_space_ = BufferColorSpace::kItuRec2020;
-      break;
-    default:
-      color_space_ = BufferColorSpace::kUndefined;
-  }
-
-  switch (dataspace & HAL_DATASPACE_RANGE_MASK) {
-    case HAL_DATASPACE_RANGE_FULL:
-      sample_range_ = BufferSampleRange::kFullRange;
-      break;
-    case HAL_DATASPACE_RANGE_LIMITED:
-      sample_range_ = BufferSampleRange::kLimitedRange;
-      break;
-    default:
-      sample_range_ = BufferSampleRange::kUndefined;
-  }
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcLayer::SetLayerDisplayFrame(hwc_rect_t frame) {
-  layer_data_.pi.display_frame = frame;
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcLayer::SetLayerPlaneAlpha(float alpha) {
-  layer_data_.pi.alpha = std::lround(alpha * UINT16_MAX);
-  return HWC2::Error::None;
-}
-
-// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
-HWC2::Error HwcLayer::SetLayerSidebandStream(
-    const native_handle_t* /*stream*/) {
-  // TODO(nobody): We don't support sideband
-  return HWC2::Error::Unsupported;
-}
-
-HWC2::Error HwcLayer::SetLayerSourceCrop(hwc_frect_t crop) {
-  layer_data_.pi.source_crop = crop;
-  return HWC2::Error::None;
-}
-
-// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
-HWC2::Error HwcLayer::SetLayerSurfaceDamage(hwc_region_t /*damage*/) {
-  // TODO(nobody): We don't use surface damage, marking as unsupported
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcLayer::SetLayerTransform(int32_t transform) {
-  uint32_t l_transform = 0;
-
-  // 270* and 180* cannot be combined with flips. More specifically, they
-  // already contain both horizontal and vertical flips, so those fields are
-  // redundant in this case. 90* rotation can be combined with either horizontal
-  // flip or vertical flip, so treat it differently
-  if (transform == HWC_TRANSFORM_ROT_270) {
-    l_transform = LayerTransform::kRotate270;
-  } else if (transform == HWC_TRANSFORM_ROT_180) {
-    l_transform = LayerTransform::kRotate180;
-  } else {
-    if ((transform & HWC_TRANSFORM_FLIP_H) != 0)
-      l_transform |= LayerTransform::kFlipH;
-    if ((transform & HWC_TRANSFORM_FLIP_V) != 0)
-      l_transform |= LayerTransform::kFlipV;
-    if ((transform & HWC_TRANSFORM_ROT_90) != 0)
-      l_transform |= LayerTransform::kRotate90;
-  }
-
-  layer_data_.pi.transform = static_cast<LayerTransform>(l_transform);
-  return HWC2::Error::None;
-}
-
-// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
-HWC2::Error HwcLayer::SetLayerVisibleRegion(hwc_region_t /*visible*/) {
-  // TODO(nobody): We don't use this information, marking as unsupported
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcLayer::SetLayerZOrder(uint32_t order) {
-  z_order_ = order;
-  return HWC2::Error::None;
-}
-
 void HwcLayer::ImportFb() {
   if (!IsLayerUsableAsDevice() || !buffer_handle_updated_) {
     return;
diff --git a/hwc2_device/HwcLayer.h b/hwc2_device/HwcLayer.h
index e1d62b7..93fd18f 100644
--- a/hwc2_device/HwcLayer.h
+++ b/hwc2_device/HwcLayer.h
@@ -82,22 +82,6 @@
 
   void SetLayerProperties(const LayerProperties &layer_properties);
 
-  // HWC2 Layer hooks
-  HWC2::Error SetCursorPosition(int32_t /*x*/, int32_t /*y*/);
-  HWC2::Error SetLayerBlendMode(int32_t mode);
-  HWC2::Error SetLayerBuffer(buffer_handle_t buffer, int32_t acquire_fence);
-  HWC2::Error SetLayerColor(hwc_color_t /*color*/);
-  HWC2::Error SetLayerCompositionType(int32_t type);
-  HWC2::Error SetLayerDataspace(int32_t dataspace);
-  HWC2::Error SetLayerDisplayFrame(hwc_rect_t frame);
-  HWC2::Error SetLayerPlaneAlpha(float alpha);
-  HWC2::Error SetLayerSidebandStream(const native_handle_t *stream);
-  HWC2::Error SetLayerSourceCrop(hwc_frect_t crop);
-  HWC2::Error SetLayerSurfaceDamage(hwc_region_t damage);
-  HWC2::Error SetLayerTransform(int32_t transform);
-  HWC2::Error SetLayerVisibleRegion(hwc_region_t visible);
-  HWC2::Error SetLayerZOrder(uint32_t order);
-
  private:
   // sf_type_ stores the initial type given to us by surfaceflinger,
   // validated_type_ stores the type after running ValidateDisplay
diff --git a/hwc2_device/hwc2_device.cpp b/hwc2_device/hwc2_device.cpp
index 28b6963..842dd9d 100644
--- a/hwc2_device/hwc2_device.cpp
+++ b/hwc2_device/hwc2_device.cpp
@@ -54,6 +54,7 @@
 
 template <typename PFN, typename T>
 static hwc2_function_pointer_t ToHook(T function) {
+  // NOLINTNEXTLINE(modernize-type-traits): ToHook is going to be removed
   static_assert(std::is_same<PFN, T>::value, "Incompatible fn pointer");
   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast):
   return reinterpret_cast<hwc2_function_pointer_t>(function);
@@ -81,24 +82,6 @@
   return static_cast<int32_t>((display->*func)(std::forward<Args>(args)...));
 }
 
-template <typename HookType, HookType func, typename... Args>
-static int32_t LayerHook(hwc2_device_t *dev, hwc2_display_t display_handle,
-                         hwc2_layer_t layer_handle, Args... args) {
-  ALOGV("Display #%" PRIu64 " Layer: #%" PRIu64 " hook: %s", display_handle,
-        layer_handle, GetFuncName(__PRETTY_FUNCTION__).c_str());
-  DrmHwcTwo *hwc = ToDrmHwcTwo(dev);
-  const std::unique_lock lock(hwc->GetResMan().GetMainLock());
-  auto *display = hwc->GetDisplay(display_handle);
-  if (display == nullptr)
-    return static_cast<int32_t>(HWC2::Error::BadDisplay);
-
-  HwcLayer *layer = display->get_layer(layer_handle);
-  if (!layer)
-    return static_cast<int32_t>(HWC2::Error::BadLayer);
-
-  return static_cast<int32_t>((layer->*func)(std::forward<Args>(args)...));
-}
-
 static int HookDevClose(hw_device_t *dev) {
   // NOLINTNEXTLINE (cppcoreguidelines-pro-type-reinterpret-cast): Safe
   auto *hwc2_dev = reinterpret_cast<hwc2_device_t *>(dev);
@@ -111,6 +94,249 @@
   *out_count = 0;
 }
 
+// NOLINTBEGIN(cppcoreguidelines-macro-usage)
+
+#define LOCK_COMPOSER(dev)       \
+  auto *ihwc = ToDrmHwcTwo(dev); \
+  const std::unique_lock lock(ihwc->GetResMan().GetMainLock());
+
+#define GET_DISPLAY(display_id)                  \
+  auto *idisplay = ihwc->GetDisplay(display_id); \
+  if (!idisplay)                                 \
+    return static_cast<int32_t>(HWC2::Error::BadDisplay);
+
+#define GET_LAYER(layer_id)                     \
+  auto *ilayer = idisplay->get_layer(layer_id); \
+  if (!ilayer)                                  \
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+
+// NOLINTEND(cppcoreguidelines-macro-usage)
+
+static BufferColorSpace Hwc2ToColorSpace(int32_t dataspace) {
+  switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+    case HAL_DATASPACE_STANDARD_BT709:
+      return BufferColorSpace::kItuRec709;
+    case HAL_DATASPACE_STANDARD_BT601_625:
+    case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+    case HAL_DATASPACE_STANDARD_BT601_525:
+    case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+      return BufferColorSpace::kItuRec601;
+    case HAL_DATASPACE_STANDARD_BT2020:
+    case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+      return BufferColorSpace::kItuRec2020;
+    default:
+      return BufferColorSpace::kUndefined;
+  }
+}
+
+static BufferSampleRange Hwc2ToSampleRange(int32_t dataspace) {
+  switch (dataspace & HAL_DATASPACE_RANGE_MASK) {
+    case HAL_DATASPACE_RANGE_FULL:
+      return BufferSampleRange::kFullRange;
+    case HAL_DATASPACE_RANGE_LIMITED:
+      return BufferSampleRange::kLimitedRange;
+    default:
+      return BufferSampleRange::kUndefined;
+  }
+}
+
+static int32_t SetLayerBlendMode(hwc2_device_t *device, hwc2_display_t display,
+                                 hwc2_layer_t layer,
+                                 int32_t /*hwc2_blend_mode_t*/ mode) {
+  ALOGV("SetLayerBlendMode");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  BufferBlendMode blend_mode{};
+  switch (static_cast<HWC2::BlendMode>(mode)) {
+    case HWC2::BlendMode::None:
+      blend_mode = BufferBlendMode::kNone;
+      break;
+    case HWC2::BlendMode::Premultiplied:
+      blend_mode = BufferBlendMode::kPreMult;
+      break;
+    case HWC2::BlendMode::Coverage:
+      blend_mode = BufferBlendMode::kCoverage;
+      break;
+    default:
+      ALOGE("Unknown blending mode b=%d", mode);
+      blend_mode = BufferBlendMode::kUndefined;
+      break;
+  }
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.blend_mode = blend_mode;
+
+  ilayer->SetLayerProperties(layer_properties);
+
+  return 0;
+}
+
+static int32_t SetLayerBuffer(hwc2_device_t *device, hwc2_display_t display,
+                              hwc2_layer_t layer, buffer_handle_t buffer,
+                              int32_t acquire_fence) {
+  ALOGV("SetLayerBuffer");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.buffer = {.buffer_handle = buffer,
+                             .acquire_fence = MakeSharedFd(acquire_fence)};
+  ilayer->SetLayerProperties(layer_properties);
+
+  return 0;
+}
+
+static int32_t SetLayerDataspace(hwc2_device_t *device, hwc2_display_t display,
+                                 hwc2_layer_t layer,
+                                 int32_t /*android_dataspace_t*/ dataspace) {
+  ALOGV("SetLayerDataspace");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.color_space = Hwc2ToColorSpace(dataspace);
+  layer_properties.sample_range = Hwc2ToSampleRange(dataspace);
+  ilayer->SetLayerProperties(layer_properties);
+  return 0;
+}
+
+static int32_t SetCursorPosition(hwc2_device_t * /*device*/,
+                                 hwc2_display_t /*display*/,
+                                 hwc2_layer_t /*layer*/, int32_t /*x*/,
+                                 int32_t /*y*/) {
+  ALOGV("SetCursorPosition");
+  return 0;
+}
+
+static int32_t SetLayerColor(hwc2_device_t * /*device*/,
+                             hwc2_display_t /*display*/, hwc2_layer_t /*layer*/,
+                             hwc_color_t /*color*/) {
+  ALOGV("SetLayerColor");
+  return 0;
+}
+
+static int32_t SetLayerCompositionType(hwc2_device_t *device,
+                                       hwc2_display_t display,
+                                       hwc2_layer_t layer,
+                                       int32_t /*hwc2_composition_t*/ type) {
+  ALOGV("SetLayerCompositionType");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.composition_type = static_cast<HWC2::Composition>(type);
+  ilayer->SetLayerProperties(layer_properties);
+
+  return 0;
+}
+
+static int32_t SetLayerDisplayFrame(hwc2_device_t *device,
+                                    hwc2_display_t display, hwc2_layer_t layer,
+                                    hwc_rect_t frame) {
+  ALOGV("SetLayerDisplayFrame");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.display_frame = frame;
+  ilayer->SetLayerProperties(layer_properties);
+
+  return 0;
+}
+
+static int32_t SetLayerPlaneAlpha(hwc2_device_t *device, hwc2_display_t display,
+                                  hwc2_layer_t layer, float alpha) {
+  ALOGV("SetLayerPlaneAlpha");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.alpha = alpha;
+  ilayer->SetLayerProperties(layer_properties);
+
+  return 0;
+}
+
+static int32_t SetLayerSidebandStream(hwc2_device_t * /*device*/,
+                                      hwc2_display_t /*display*/,
+                                      hwc2_layer_t /*layer*/,
+                                      const native_handle_t * /*stream*/) {
+  ALOGV("SetLayerSidebandStream");
+  return static_cast<int32_t>(HWC2::Error::Unsupported);
+}
+
+static int32_t SetLayerSourceCrop(hwc2_device_t *device, hwc2_display_t display,
+                                  hwc2_layer_t layer, hwc_frect_t crop) {
+  ALOGV("SetLayerSourceCrop");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.source_crop = crop;
+  ilayer->SetLayerProperties(layer_properties);
+
+  return 0;
+}
+
+static int32_t SetLayerSurfaceDamage(hwc2_device_t * /*device*/,
+                                     hwc2_display_t /*display*/,
+                                     hwc2_layer_t /*layer*/,
+                                     hwc_region_t /*damage*/) {
+  ALOGV("SetLayerSurfaceDamage");
+  return 0;
+}
+
+static int32_t SetLayerTransform(hwc2_device_t *device, hwc2_display_t display,
+                                 hwc2_layer_t layer, int32_t transform) {
+  ALOGV("SetLayerTransform");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.transform = {
+      .hflip = (transform & HAL_TRANSFORM_FLIP_H) != 0,
+      .vflip = (transform & HAL_TRANSFORM_FLIP_V) != 0,
+      .rotate90 = (transform & HAL_TRANSFORM_ROT_90) != 0,
+  };
+  ilayer->SetLayerProperties(layer_properties);
+
+  return 0;
+}
+
+static int32_t SetLayerVisibleRegion(hwc2_device_t * /*device*/,
+                                     hwc2_display_t /*display*/,
+                                     hwc2_layer_t /*layer*/,
+                                     hwc_region_t /*visible*/) {
+  ALOGV("SetLayerVisibleRegion");
+  return 0;
+}
+
+static int32_t SetLayerZOrder(hwc2_device_t *device, hwc2_display_t display,
+                              hwc2_layer_t layer, uint32_t z) {
+  ALOGV("SetLayerZOrder");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+  GET_LAYER(layer);
+
+  HwcLayer::LayerProperties layer_properties;
+  layer_properties.z_order = z;
+  ilayer->SetLayerProperties(layer_properties);
+
+  return 0;
+}
+
+/* Entry point for the HWC2 API */
+// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast)
+
 static hwc2_function_pointer_t HookDevGetFunction(struct hwc2_device * /*dev*/,
                                                   int32_t descriptor) {
   auto func = static_cast<HWC2::FunctionDescriptor>(descriptor);
@@ -307,68 +533,41 @@
 #endif
     // Layer functions
     case HWC2::FunctionDescriptor::SetCursorPosition:
-      return ToHook<HWC2_PFN_SET_CURSOR_POSITION>(
-          LayerHook<decltype(&HwcLayer::SetCursorPosition),
-                    &HwcLayer::SetCursorPosition, int32_t, int32_t>);
+      return (hwc2_function_pointer_t)SetCursorPosition;
     case HWC2::FunctionDescriptor::SetLayerBlendMode:
-      return ToHook<HWC2_PFN_SET_LAYER_BLEND_MODE>(
-          LayerHook<decltype(&HwcLayer::SetLayerBlendMode),
-                    &HwcLayer::SetLayerBlendMode, int32_t>);
+      return (hwc2_function_pointer_t)SetLayerBlendMode;
     case HWC2::FunctionDescriptor::SetLayerBuffer:
-      return ToHook<HWC2_PFN_SET_LAYER_BUFFER>(
-          LayerHook<decltype(&HwcLayer::SetLayerBuffer),
-                    &HwcLayer::SetLayerBuffer, buffer_handle_t, int32_t>);
+      return (hwc2_function_pointer_t)SetLayerBuffer;
     case HWC2::FunctionDescriptor::SetLayerColor:
-      return ToHook<HWC2_PFN_SET_LAYER_COLOR>(
-          LayerHook<decltype(&HwcLayer::SetLayerColor),
-                    &HwcLayer::SetLayerColor, hwc_color_t>);
+      return (hwc2_function_pointer_t)SetLayerColor;
     case HWC2::FunctionDescriptor::SetLayerCompositionType:
-      return ToHook<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
-          LayerHook<decltype(&HwcLayer::SetLayerCompositionType),
-                    &HwcLayer::SetLayerCompositionType, int32_t>);
+      return (hwc2_function_pointer_t)SetLayerCompositionType;
     case HWC2::FunctionDescriptor::SetLayerDataspace:
-      return ToHook<HWC2_PFN_SET_LAYER_DATASPACE>(
-          LayerHook<decltype(&HwcLayer::SetLayerDataspace),
-                    &HwcLayer::SetLayerDataspace, int32_t>);
+      return (hwc2_function_pointer_t)SetLayerDataspace;
     case HWC2::FunctionDescriptor::SetLayerDisplayFrame:
-      return ToHook<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
-          LayerHook<decltype(&HwcLayer::SetLayerDisplayFrame),
-                    &HwcLayer::SetLayerDisplayFrame, hwc_rect_t>);
+      return (hwc2_function_pointer_t)SetLayerDisplayFrame;
     case HWC2::FunctionDescriptor::SetLayerPlaneAlpha:
-      return ToHook<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
-          LayerHook<decltype(&HwcLayer::SetLayerPlaneAlpha),
-                    &HwcLayer::SetLayerPlaneAlpha, float>);
+      return (hwc2_function_pointer_t)SetLayerPlaneAlpha;
     case HWC2::FunctionDescriptor::SetLayerSidebandStream:
-      return ToHook<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(
-          LayerHook<decltype(&HwcLayer::SetLayerSidebandStream),
-                    &HwcLayer::SetLayerSidebandStream,
-                    const native_handle_t *>);
+      return (hwc2_function_pointer_t)SetLayerSidebandStream;
     case HWC2::FunctionDescriptor::SetLayerSourceCrop:
-      return ToHook<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
-          LayerHook<decltype(&HwcLayer::SetLayerSourceCrop),
-                    &HwcLayer::SetLayerSourceCrop, hwc_frect_t>);
+      return (hwc2_function_pointer_t)SetLayerSourceCrop;
     case HWC2::FunctionDescriptor::SetLayerSurfaceDamage:
-      return ToHook<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
-          LayerHook<decltype(&HwcLayer::SetLayerSurfaceDamage),
-                    &HwcLayer::SetLayerSurfaceDamage, hwc_region_t>);
+      return (hwc2_function_pointer_t)SetLayerSurfaceDamage;
     case HWC2::FunctionDescriptor::SetLayerTransform:
-      return ToHook<HWC2_PFN_SET_LAYER_TRANSFORM>(
-          LayerHook<decltype(&HwcLayer::SetLayerTransform),
-                    &HwcLayer::SetLayerTransform, int32_t>);
+      return (hwc2_function_pointer_t)SetLayerTransform;
     case HWC2::FunctionDescriptor::SetLayerVisibleRegion:
-      return ToHook<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
-          LayerHook<decltype(&HwcLayer::SetLayerVisibleRegion),
-                    &HwcLayer::SetLayerVisibleRegion, hwc_region_t>);
+      return (hwc2_function_pointer_t)SetLayerVisibleRegion;
     case HWC2::FunctionDescriptor::SetLayerZOrder:
-      return ToHook<HWC2_PFN_SET_LAYER_Z_ORDER>(
-          LayerHook<decltype(&HwcLayer::SetLayerZOrder),
-                    &HwcLayer::SetLayerZOrder, uint32_t>);
+      return (hwc2_function_pointer_t)SetLayerZOrder;
     case HWC2::FunctionDescriptor::Invalid:
     default:
       return nullptr;
   }
 }
 
+// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast)
+
 static int HookDevOpen(const struct hw_module_t *module, const char *name,
                        struct hw_device_t **dev) {
   if (strcmp(name, HWC_HARDWARE_COMPOSER) != 0) {
diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp
index 13fc7d9..b03ddd7 100644
--- a/hwc3/ComposerClient.cpp
+++ b/hwc3/ComposerClient.cpp
@@ -62,8 +62,12 @@
 namespace aidl::android::hardware::graphics::composer3::impl {
 namespace {
 
+constexpr int kCtmRows = 4;
+constexpr int kCtmColumns = 4;
+constexpr int kCtmSize = kCtmRows * kCtmColumns;
+
 // clang-format off
-constexpr std::array<float, 16> kIdentityMatrix = {
+constexpr std::array<float, kCtmSize> kIdentityMatrix = {
     1.0F, 0.0F, 0.0F, 0.0F,
     0.0F, 1.0F, 0.0F, 0.0F,
     0.0F, 0.0F, 1.0F, 0.0F,
@@ -91,12 +95,8 @@
 }
 
 std::optional<BufferColorSpace> AidlToColorSpace(
-    const std::optional<ParcelableDataspace>& dataspace) {
-  if (!dataspace) {
-    return std::nullopt;
-  }
-
-  int32_t standard = static_cast<int32_t>(dataspace->dataspace) &
+    const common::Dataspace& dataspace) {
+  int32_t standard = static_cast<int32_t>(dataspace) &
                      static_cast<int32_t>(common::Dataspace::STANDARD_MASK);
   switch (standard) {
     case static_cast<int32_t>(common::Dataspace::STANDARD_BT709):
@@ -118,13 +118,17 @@
   }
 }
 
-std::optional<BufferSampleRange> AidlToSampleRange(
+std::optional<BufferColorSpace> AidlToColorSpace(
     const std::optional<ParcelableDataspace>& dataspace) {
   if (!dataspace) {
     return std::nullopt;
   }
+  return AidlToColorSpace(dataspace->dataspace);
+}
 
-  int32_t sample_range = static_cast<int32_t>(dataspace->dataspace) &
+std::optional<BufferSampleRange> AidlToSampleRange(
+    const common::Dataspace& dataspace) {
+  int32_t sample_range = static_cast<int32_t>(dataspace) &
                          static_cast<int32_t>(common::Dataspace::RANGE_MASK);
   switch (sample_range) {
     case static_cast<int32_t>(common::Dataspace::RANGE_FULL):
@@ -139,6 +143,14 @@
   }
 }
 
+std::optional<BufferSampleRange> AidlToSampleRange(
+    const std::optional<ParcelableDataspace>& dataspace) {
+  if (!dataspace) {
+    return std::nullopt;
+  }
+  return AidlToSampleRange(dataspace->dataspace);
+}
+
 bool IsSupportedCompositionType(
     const std::optional<ParcelableComposition> composition) {
   if (!composition) {
@@ -163,6 +175,27 @@
   }
 }
 
+hwc3::Error ValidateColorTransformMatrix(
+    const std::optional<std::vector<float>>& color_transform_matrix) {
+  if (!color_transform_matrix) {
+    return hwc3::Error::kNone;
+  }
+
+  if (color_transform_matrix->size() != kCtmSize) {
+    ALOGE("Expected color transform matrix of size %d, got size %d.", kCtmSize,
+          (int)color_transform_matrix->size());
+    return hwc3::Error::kBadParameter;
+  }
+
+  // Without HW support, we cannot correctly process matrices with an offset.
+  constexpr int kOffsetIndex = kCtmColumns * 3;
+  for (int i = kOffsetIndex; i < kOffsetIndex + 3; i++) {
+    if (color_transform_matrix.value()[i] != 0.F)
+      return hwc3::Error::kUnsupported;
+  }
+  return hwc3::Error::kNone;
+}
+
 bool ValidateLayerBrightness(const std::optional<LayerBrightness>& brightness) {
   if (!brightness) {
     return true;
@@ -171,6 +204,19 @@
            std::isnan(brightness->brightness));
 }
 
+std::optional<std::array<float, kCtmSize>> AidlToColorTransformMatrix(
+    const std::optional<std::vector<float>>& aidl_color_transform_matrix) {
+  if (!aidl_color_transform_matrix ||
+      aidl_color_transform_matrix->size() < kCtmSize) {
+    return std::nullopt;
+  }
+
+  std::array<float, kCtmSize> color_transform_matrix = kIdentityMatrix;
+  std::copy(aidl_color_transform_matrix->begin(),
+            aidl_color_transform_matrix->end(), color_transform_matrix.begin());
+  return color_transform_matrix;
+}
+
 std::optional<HWC2::Composition> AidlToCompositionType(
     const std::optional<ParcelableComposition> composition) {
   if (!composition) {
@@ -281,28 +327,16 @@
     return std::nullopt;
   }
 
-  uint32_t transform = LayerTransform::kIdentity;
-  // 270* and 180* cannot be combined with flips. More specifically, they
-  // already contain both horizontal and vertical flips, so those fields are
-  // redundant in this case. 90* rotation can be combined with either horizontal
-  // flip or vertical flip, so treat it differently
-  if (aidl_transform->transform == common::Transform::ROT_270) {
-    transform = LayerTransform::kRotate270;
-  } else if (aidl_transform->transform == common::Transform::ROT_180) {
-    transform = LayerTransform::kRotate180;
-  } else {
-    auto aidl_transform_bits = static_cast<uint32_t>(aidl_transform->transform);
-    if ((aidl_transform_bits &
-         static_cast<uint32_t>(common::Transform::FLIP_H)) != 0)
-      transform |= LayerTransform::kFlipH;
-    if ((aidl_transform_bits &
-         static_cast<uint32_t>(common::Transform::FLIP_V)) != 0)
-      transform |= LayerTransform::kFlipV;
-    if ((aidl_transform_bits &
-         static_cast<uint32_t>(common::Transform::ROT_90)) != 0)
-      transform |= LayerTransform::kRotate90;
-  }
-  return static_cast<LayerTransform>(transform);
+  using aidl::android::hardware::graphics::common::Transform;
+
+  return (LayerTransform){
+      .hflip = (int32_t(aidl_transform->transform) &
+                int32_t(Transform::FLIP_H)) != 0,
+      .vflip = (int32_t(aidl_transform->transform) &
+                int32_t(Transform::FLIP_V)) != 0,
+      .rotate90 = (int32_t(aidl_transform->transform) &
+                   int32_t(Transform::ROT_90)) != 0,
+  };
 }
 
 }  // namespace
@@ -322,7 +356,7 @@
 
 ComposerClient::~ComposerClient() {
   DEBUG_FUNC();
-  {
+  if (hwc_) {
     const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
     hwc_->DeinitDisplays();
     hwc_.reset();
@@ -413,113 +447,6 @@
   return ToBinderStatus(err);
 }
 
-hwc3::Error ComposerClient::ValidateDisplayInternal(
-    HwcDisplay& display, std::vector<int64_t>* out_changed_layers,
-    std::vector<Composition>* out_composition_types,
-    int32_t* out_display_request_mask,
-    std::vector<int64_t>* out_requested_layers,
-    std::vector<int32_t>* out_request_masks,
-    ClientTargetProperty* /*out_client_target_property*/,
-    DimmingStage* /*out_dimming_stage*/) {
-  DEBUG_FUNC();
-
-  uint32_t num_types = 0;
-  uint32_t num_requests = 0;
-  const HWC2::Error hwc2_error = display.ValidateDisplay(&num_types,
-                                                         &num_requests);
-
-  /* Check if display has pending changes and no errors */
-  if (hwc2_error != HWC2::Error::None &&
-      hwc2_error != HWC2::Error::HasChanges) {
-    return Hwc2toHwc3Error(hwc2_error);
-  }
-
-  hwc3::Error error = Hwc2toHwc3Error(
-      display.GetChangedCompositionTypes(&num_types, nullptr, nullptr));
-  if (error != hwc3::Error::kNone) {
-    return error;
-  }
-
-  std::vector<hwc2_layer_t> hwc_changed_layers(num_types);
-  std::vector<int32_t> hwc_composition_types(num_types);
-  error = Hwc2toHwc3Error(
-      display.GetChangedCompositionTypes(&num_types, hwc_changed_layers.data(),
-                                         hwc_composition_types.data()));
-  if (error != hwc3::Error::kNone) {
-    return error;
-  }
-
-  int32_t display_reqs = 0;
-  out_request_masks->resize(num_requests);
-  std::vector<hwc2_layer_t> hwc_requested_layers(num_requests);
-  error = Hwc2toHwc3Error(
-      display.GetDisplayRequests(&display_reqs, &num_requests,
-                                 hwc_requested_layers.data(),
-                                 out_request_masks->data()));
-  if (error != hwc3::Error::kNone) {
-    return error;
-  }
-
-  for (const auto& layer : hwc_changed_layers) {
-    out_changed_layers->emplace_back(Hwc2LayerToHwc3(layer));
-  }
-  for (const auto& type : hwc_composition_types) {
-    out_composition_types->emplace_back(Hwc2CompositionTypeToHwc3(type));
-  }
-  for (const auto& layer : hwc_requested_layers) {
-    out_requested_layers->emplace_back(Hwc2LayerToHwc3(layer));
-  }
-  *out_display_request_mask = display_reqs;
-
-  /* Client target property/dimming stage unsupported */
-  return hwc3::Error::kNone;
-}
-
-hwc3::Error ComposerClient::PresentDisplayInternal(
-    uint64_t display_id, ::android::base::unique_fd& out_display_fence,
-    std::unordered_map<int64_t, ::android::base::unique_fd>&
-        out_release_fences) {
-  DEBUG_FUNC();
-  auto* display = GetDisplay(display_id);
-  if (display == nullptr) {
-    return hwc3::Error::kBadDisplay;
-  }
-
-  if (composer_resources_->MustValidateDisplay(display_id)) {
-    return hwc3::Error::kNotValidated;
-  }
-
-  int32_t present_fence = -1;
-  auto error = Hwc2toHwc3Error(display->PresentDisplay(&present_fence));
-  if (error != hwc3::Error::kNone) {
-    return error;
-  }
-  out_display_fence.reset(present_fence);
-
-  uint32_t release_fence_count = 0;
-  error = Hwc2toHwc3Error(
-      display->GetReleaseFences(&release_fence_count, nullptr, nullptr));
-  if (error != hwc3::Error::kNone) {
-    return error;
-  }
-
-  std::vector<hwc2_layer_t> hwc_layers(release_fence_count);
-  std::vector<int32_t> hwc_fences(release_fence_count);
-  error = Hwc2toHwc3Error(display->GetReleaseFences(&release_fence_count,
-                                                    hwc_layers.data(),
-                                                    hwc_fences.data()));
-  if (error != hwc3::Error::kNone) {
-    return error;
-  }
-
-  for (size_t i = 0; i < hwc_layers.size(); i++) {
-    auto layer = Hwc2LayerToHwc3(hwc_layers[i]);
-    out_release_fences[layer] = ::android::base::unique_fd{hwc_fences[i]};
-  }
-
-  return hwc3::Error::kNone;
-}
-
 ::android::HwcDisplay* ComposerClient::GetDisplay(uint64_t display_id) {
   return hwc_->GetDisplay(display_id);
 }
@@ -556,13 +483,11 @@
   if (command.buffer) {
     HwcLayer::Buffer buffer;
     auto err = ImportLayerBuffer(display_id, command.layer, *command.buffer,
-                                 &buffer.buffer_handle);
+                                 &buffer);
     if (err != hwc3::Error::kNone) {
       cmd_result_writer_->AddError(err);
       return;
     }
-    buffer.acquire_fence = ::android::MakeSharedFd(
-        command.buffer->fence.dup().release());
     properties.buffer.emplace(buffer);
   }
 
@@ -599,7 +524,8 @@
 
 void ComposerClient::ExecuteDisplayCommand(const DisplayCommand& command) {
   const int64_t display_id = command.display;
-  if (hwc_->GetDisplay(display_id) == nullptr) {
+  HwcDisplay* display = hwc_->GetDisplay(display_id);
+  if (display == nullptr) {
     cmd_result_writer_->AddError(hwc3::Error::kBadDisplay);
     return;
   }
@@ -610,14 +536,22 @@
     return;
   }
 
+  hwc3::Error error = ValidateColorTransformMatrix(
+      command.colorTransformMatrix);
+  if (error != hwc3::Error::kNone) {
+    ALOGE("Invalid color transform matrix.");
+    cmd_result_writer_->AddError(error);
+    return;
+  }
+
   for (const auto& layer_cmd : command.layers) {
     DispatchLayerCommand(command.display, layer_cmd);
   }
 
-  if (command.colorTransformMatrix) {
-    ExecuteSetDisplayColorTransform(command.display,
-                                    *command.colorTransformMatrix);
+  if (cmd_result_writer_->HasError()) {
+    return;
   }
+
   if (command.clientTarget) {
     ExecuteSetDisplayClientTarget(command.display, *command.clientTarget);
   }
@@ -625,18 +559,65 @@
     ExecuteSetDisplayOutputBuffer(command.display,
                                   *command.virtualDisplayOutputBuffer);
   }
-  if (command.validateDisplay) {
-    ExecuteValidateDisplay(command.display, command.expectedPresentTime);
+
+  std::optional<std::array<float, kCtmSize>> ctm = AidlToColorTransformMatrix(
+      command.colorTransformMatrix);
+  if (ctm) {
+    display->SetColorTransformMatrix(ctm.value());
   }
+
+  if (command.validateDisplay || command.presentOrValidateDisplay) {
+    std::vector<HwcDisplay::ChangedLayer>
+        changed_layers = display->ValidateStagedComposition();
+    DisplayChanges changes{};
+    for (auto [layer_id, composition_type] : changed_layers) {
+      changes.AddLayerCompositionChange(command.display,
+                                        Hwc2LayerToHwc3(layer_id),
+                                        static_cast<Composition>(
+                                            composition_type));
+    }
+    cmd_result_writer_->AddChanges(changes);
+    composer_resources_->SetDisplayMustValidateState(display_id, false);
+
+    // TODO: DisplayRequests are not implemented.
+
+    /* TODO: Add check if it's possible to skip display validation for
+     * presentOrValidateDisplay */
+    if (command.presentOrValidateDisplay) {
+      cmd_result_writer_
+          ->AddPresentOrValidateResult(display_id,
+                                       PresentOrValidate::Result::Validated);
+    }
+  }
+
   if (command.acceptDisplayChanges) {
-    ExecuteAcceptDisplayChanges(command.display);
+    display->AcceptDisplayChanges();
   }
+
   if (command.presentDisplay) {
-    ExecutePresentDisplay(command.display);
-  }
-  if (command.presentOrValidateDisplay) {
-    ExecutePresentOrValidateDisplay(command.display,
-                                    command.expectedPresentTime);
+    if (composer_resources_->MustValidateDisplay(display_id)) {
+      cmd_result_writer_->AddError(hwc3::Error::kNotValidated);
+      return;
+    }
+    ::android::SharedFd present_fence;
+    std::vector<HwcDisplay::ReleaseFence> release_fences;
+    bool ret = display->PresentStagedComposition(present_fence, release_fences);
+
+    if (!ret) {
+      cmd_result_writer_->AddError(hwc3::Error::kBadDisplay);
+      return;
+    }
+
+    using ::android::base::unique_fd;
+    cmd_result_writer_->AddPresentFence(  //
+        display_id, unique_fd(::android::DupFd(present_fence)));
+
+    std::unordered_map<int64_t, unique_fd> hal_release_fences;
+    for (const auto& [layer_id, release_fence] : release_fences) {
+      hal_release_fences[Hwc2LayerToHwc3(layer_id)] =  //
+          unique_fd(::android::DupFd(release_fence));
+    }
+    cmd_result_writer_->AddReleaseFence(display_id, hal_release_fences);
   }
 }
 
@@ -966,8 +947,28 @@
     return ToBinderStatus(hwc3::Error::kBadDisplay);
   }
 
-  /* No HDR capabilities */
-  caps->types.clear();
+  uint32_t num_types = 0;
+  hwc3::Error error = Hwc2toHwc3Error(
+      display->GetHdrCapabilities(&num_types, nullptr, nullptr, nullptr,
+                                  nullptr));
+  if (error != hwc3::Error::kNone) {
+    return ToBinderStatus(error);
+  }
+
+  std::vector<int32_t> out_types(num_types);
+  error = Hwc2toHwc3Error(
+      display->GetHdrCapabilities(&num_types, out_types.data(),
+                                  &caps->maxLuminance,
+                                  &caps->maxAverageLuminance,
+                                  &caps->minLuminance));
+  if (error != hwc3::Error::kNone) {
+    return ToBinderStatus(error);
+  }
+
+  caps->types.reserve(num_types);
+  for (const auto type : out_types)
+    caps->types.emplace_back(Hwc2HdrTypeToHwc3(type));
+
   return ndk::ScopedAStatus::ok();
 }
 
@@ -1325,41 +1326,19 @@
   return binder;
 }
 
-hwc3::Error ComposerClient::ImportLayerBuffer(
-    int64_t display_id, int64_t layer_id, const Buffer& buffer,
-    buffer_handle_t* out_imported_buffer) {
-  *out_imported_buffer = nullptr;
-
-  auto releaser = composer_resources_->CreateResourceReleaser(true);
+hwc3::Error ComposerClient::ImportLayerBuffer(int64_t display_id,
+                                              int64_t layer_id,
+                                              const Buffer& buffer,
+                                              HwcLayer::Buffer* out_buffer) {
+  auto releaser = ComposerResources::CreateResourceReleaser(true);
   auto err = composer_resources_->GetLayerBuffer(display_id, layer_id, buffer,
-                                                 out_imported_buffer,
+                                                 &out_buffer->buffer_handle,
                                                  releaser.get());
+  out_buffer->acquire_fence = ::android::MakeSharedFd(
+      buffer.fence.dup().release());
   return err;
 }
 
-void ComposerClient::ExecuteSetDisplayColorTransform(
-    uint64_t display_id, const std::vector<float>& matrix) {
-  auto* display = GetDisplay(display_id);
-  if (display == nullptr) {
-    cmd_result_writer_->AddError(hwc3::Error::kBadDisplay);
-    return;
-  }
-
-  auto almost_equal = [](auto a, auto b) {
-    const float epsilon = 0.001F;
-    return std::abs(a - b) < epsilon;
-  };
-  const bool is_identity = std::equal(matrix.begin(), matrix.end(),
-                                      kIdentityMatrix.begin(), almost_equal);
-
-  const int32_t hint = is_identity ? HAL_COLOR_TRANSFORM_IDENTITY
-                                   : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-
-  auto error = Hwc2toHwc3Error(display->SetColorTransform(matrix.data(), hint));
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
-  }
-}
 void ComposerClient::ExecuteSetDisplayClientTarget(
     uint64_t display_id, const ClientTarget& command) {
   auto* display = GetDisplay(display_id);
@@ -1378,7 +1357,7 @@
   damage_regions.rects = regions.data();
 
   buffer_handle_t imported_buffer = nullptr;
-  auto buf_releaser = composer_resources_->CreateResourceReleaser(true);
+  auto buf_releaser = ComposerResources::CreateResourceReleaser(true);
 
   auto error = composer_resources_->GetDisplayClientTarget(display_id,
                                                            command.buffer,
@@ -1410,7 +1389,7 @@
   }
 
   buffer_handle_t imported_buffer = nullptr;
-  auto buf_releaser = composer_resources_->CreateResourceReleaser(true);
+  auto buf_releaser = ComposerResources::CreateResourceReleaser(true);
 
   auto error = composer_resources_->GetDisplayOutputBuffer(display_id, buffer,
                                                            &imported_buffer,
@@ -1428,130 +1407,5 @@
     return;
   }
 }
-void ComposerClient::ExecuteValidateDisplay(
-    int64_t display_id,
-    std::optional<ClockMonotonicTimestamp> /*expected_present_time*/
-) {
-  auto* display = GetDisplay(display_id);
-  if (display == nullptr) {
-    cmd_result_writer_->AddError(hwc3::Error::kBadDisplay);
-    return;
-  }
-
-  /* TODO: Handle expectedPresentTime */
-  /* This can be implemented in multiple ways. For example, the expected present
-   * time property can be implemented by the DRM driver directly as a CRTC
-   * property. See:
-   * https://cs.android.com/android/platform/superproject/main/+/b8b3b1646e64d0235f77b9e717a3e4082e26f2a8:hardware/google/graphics/common/libhwc2.1/libdrmresource/drm/drmcrtc.cpp;drc=468f6172546ab98983de18210222f231f16b21e1;l=88
-   * Unfortunately there doesn't seem to be a standardised way of delaying
-   * presentation with a timestamp in the DRM API. What we can do alternatively
-   * is to spawn a separate presentation thread that could handle the VBlank
-   * events by using DRM_MODE_PAGE_FLIP_EVENT and schedule them appropriately.
-   */
-
-  std::vector<int64_t> changed_layers;
-  std::vector<Composition> composition_types;
-  int32_t display_request_mask = 0;
-  std::vector<int64_t> requested_layers;
-  std::vector<int32_t> request_masks;
-
-  const hwc3::Error error = ValidateDisplayInternal(*display, &changed_layers,
-                                                    &composition_types,
-                                                    &display_request_mask,
-                                                    &requested_layers,
-                                                    &request_masks, nullptr,
-                                                    nullptr);
-
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
-  }
-
-  // If a CommandError has been been set for the current DisplayCommand, then
-  // no other results should be returned besides the error.
-  if (cmd_result_writer_->HasError()) {
-    return;
-  }
-
-  DisplayChanges changes{};
-  for (size_t i = 0; i < composition_types.size(); i++) {
-    changes.AddLayerCompositionChange(display_id, changed_layers[i],
-                                      composition_types[i]);
-  }
-
-  std::vector<DisplayRequest::LayerRequest> layer_requests;
-  for (size_t i = 0; i < requested_layers.size(); i++) {
-    layer_requests.push_back({requested_layers[i], request_masks[i]});
-  }
-
-  const DisplayRequest request_changes{display_id, display_request_mask,
-                                       layer_requests};
-  changes.display_request_changes = request_changes;
-
-  cmd_result_writer_->AddChanges(changes);
-  composer_resources_->SetDisplayMustValidateState(display_id, false);
-}
-
-void ComposerClient::ExecuteAcceptDisplayChanges(int64_t display_id) {
-  auto* display = GetDisplay(display_id);
-  if (display == nullptr) {
-    cmd_result_writer_->AddError(hwc3::Error::kBadDisplay);
-    return;
-  }
-
-  auto error = Hwc2toHwc3Error(display->AcceptDisplayChanges());
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
-    return;
-  }
-}
-
-void ComposerClient::ExecutePresentDisplay(int64_t display_id) {
-  auto* display = GetDisplay(display_id);
-  if (display == nullptr) {
-    cmd_result_writer_->AddError(hwc3::Error::kBadDisplay);
-    return;
-  }
-
-  ::android::base::unique_fd display_fence;
-  std::unordered_map<int64_t, ::android::base::unique_fd> release_fences;
-  auto error = PresentDisplayInternal(display_id, display_fence,
-                                      release_fences);
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
-  }
-  if (cmd_result_writer_->HasError()) {
-    return;
-  }
-
-  cmd_result_writer_->AddPresentFence(display_id, std::move(display_fence));
-  cmd_result_writer_->AddReleaseFence(display_id, release_fences);
-}
-
-void ComposerClient::ExecutePresentOrValidateDisplay(
-    int64_t display_id,
-    std::optional<ClockMonotonicTimestamp> expected_present_time) {
-  auto* display = GetDisplay(display_id);
-  if (display == nullptr) {
-    cmd_result_writer_->AddError(hwc3::Error::kBadDisplay);
-    return;
-  }
-
-  /* TODO: Handle expectedPresentTime */
-  /* This can be implemented in multiple ways. For example, the expected present
-   * time property can be implemented by the DRM driver directly as a CRTC
-   * property. See:
-   * https://cs.android.com/android/platform/superproject/main/+/b8b3b1646e64d0235f77b9e717a3e4082e26f2a8:hardware/google/graphics/common/libhwc2.1/libdrmresource/drm/drmcrtc.cpp;drc=468f6172546ab98983de18210222f231f16b21e1;l=88
-   * Unfortunately there doesn't seem to be a standardised way of delaying
-   * presentation with a timestamp in the DRM API. What we can do alternatively
-   * is to spawn a separate presentation thread that could handle the VBlank
-   * events by using DRM_MODE_PAGE_FLIP_EVENT and schedule them appropriately.
-   */
-
-  /* TODO: Add check if it's possible to skip display validation */
-  ExecuteValidateDisplay(display_id, expected_present_time);
-  cmd_result_writer_
-      ->AddPresentOrValidateResult(display_id,
-                                   PresentOrValidate::Result::Validated);
-}
 
 }  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/hwc3/ComposerClient.h b/hwc3/ComposerClient.h
index 2595203..b0d3caa 100644
--- a/hwc3/ComposerClient.h
+++ b/hwc3/ComposerClient.h
@@ -20,6 +20,7 @@
 
 #include "aidl/android/hardware/graphics/composer3/BnComposerClient.h"
 #include "aidl/android/hardware/graphics/composer3/LayerCommand.h"
+#include "hwc2_device/HwcLayer.h"
 #include "hwc3/CommandResultWriter.h"
 #include "hwc3/ComposerResources.h"
 #include "hwc3/Utils.h"
@@ -30,10 +31,7 @@
 using AidlNativeHandle = aidl::android::hardware::common::NativeHandle;
 
 namespace android {
-
 class HwcDisplay;
-class HwcLayer;
-
 }  // namespace android
 
 namespace aidl::android::hardware::graphics::composer3::impl {
@@ -171,40 +169,16 @@
  private:
   hwc3::Error ImportLayerBuffer(int64_t display_id, int64_t layer_id,
                                 const Buffer& buffer,
-                                buffer_handle_t* out_imported_buffer);
+                                ::android::HwcLayer::Buffer* out_buffer);
 
   // Layer commands
   void DispatchLayerCommand(int64_t display_id, const LayerCommand& command);
 
   // Display commands
   void ExecuteDisplayCommand(const DisplayCommand& command);
-  void ExecuteSetDisplayColorTransform(uint64_t display_id,
-                                       const std::vector<float>& matrix);
   void ExecuteSetDisplayClientTarget(uint64_t display_id,
                                      const ClientTarget& command);
   void ExecuteSetDisplayOutputBuffer(uint64_t display_id, const Buffer& buffer);
-  void ExecuteValidateDisplay(
-      int64_t display_id,
-      std::optional<ClockMonotonicTimestamp> expected_present_time);
-  void ExecuteAcceptDisplayChanges(int64_t display_id);
-  void ExecutePresentDisplay(int64_t display_id);
-  void ExecutePresentOrValidateDisplay(
-      int64_t display_id,
-      std::optional<ClockMonotonicTimestamp> expected_present_time);
-
-  static hwc3::Error ValidateDisplayInternal(
-      ::android::HwcDisplay& display, std::vector<int64_t>* out_changed_layers,
-      std::vector<Composition>* out_composition_types,
-      int32_t* out_display_request_mask,
-      std::vector<int64_t>* out_requested_layers,
-      std::vector<int32_t>* out_request_masks,
-      ClientTargetProperty* out_client_target_property,
-      DimmingStage* out_dimming_stage);
-
-  hwc3::Error PresentDisplayInternal(
-      uint64_t display_id, ::android::base::unique_fd& out_display_fence,
-      std::unordered_map<int64_t, ::android::base::unique_fd>&
-          out_release_fences);
 
   ::android::HwcDisplay* GetDisplay(uint64_t display_id);
 
diff --git a/hwc3/ComposerResources.cpp b/hwc3/ComposerResources.cpp
index ae0edf4..5e19082 100644
--- a/hwc3/ComposerResources.cpp
+++ b/hwc3/ComposerResources.cpp
@@ -25,20 +25,21 @@
 #include "hardware/hwcomposer2.h"
 #include "hwc3/Utils.h"
 
+namespace {
+using Hwc2Display = ::android::hardware::graphics::composer::V2_1::Display;
+using Hwc2Layer = ::android::hardware::graphics::composer::V2_1::Layer;
+
+auto ToHwc2Display(uint64_t display_id) -> Hwc2Display {
+  return static_cast<Hwc2Display>(display_id);
+}
+
+auto ToHwc2Layer(int64_t layer_id) -> Hwc2Layer {
+  return static_cast<Hwc2Layer>(layer_id);
+}
+}  // namespace
+
 namespace aidl::android::hardware::graphics::composer3::impl {
 
-::android::hardware::graphics::composer::V2_1::Display ToHwc2Display(
-    uint64_t display_id) {
-  return static_cast<::android::hardware::graphics::composer::V2_1::Display>(
-      display_id);
-}
-
-::android::hardware::graphics::composer::V2_1::Layer ToHwc2Layer(
-    int64_t layer_id) {
-  return static_cast<::android::hardware::graphics::composer::V2_1::Layer>(
-      layer_id);
-}
-
 std::unique_ptr<ComposerResourceReleaser>
 ComposerResources::CreateResourceReleaser(bool is_buffer) {
   return std::make_unique<ComposerResourceReleaser>(is_buffer);
diff --git a/hwc3/Utils.h b/hwc3/Utils.h
index b322f5d..642c777 100644
--- a/hwc3/Utils.h
+++ b/hwc3/Utils.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <aidl/android/hardware/graphics/common/Hdr.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
 #include <hardware/hwcomposer2.h>
 #include <log/log.h>
@@ -165,4 +166,12 @@
   return static_cast<int32_t>(dataspace);
 }
 
-};  // namespace aidl::android::hardware::graphics::composer3
\ No newline at end of file
+// Values appear to match.
+// https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/graphics/common/aidl/android/hardware/graphics/common/Hdr.aidl
+// https://cs.android.com/android/platform/superproject/main/+/main:system/core/libsystem/include/system/graphics-base-v1.0.h;l=130;drc=7d940ae4afa450696afa25e07982f3a95e17e9b2
+// https://cs.android.com/android/platform/superproject/main/+/main:system/core/libsystem/include/system/graphics-base-v1.2.h;l=12;drc=af7be7616859f8e9e57710b9c37c66cf880a6643
+inline common::Hdr Hwc2HdrTypeToHwc3(int32_t hdr_type) {
+  return static_cast<common::Hdr>(hdr_type);
+}
+
+};  // namespace aidl::android::hardware::graphics::composer3
diff --git a/meson.build b/meson.build
index 97474e2..3d5c9f0 100644
--- a/meson.build
+++ b/meson.build
@@ -16,6 +16,7 @@
     'backend/Backend.cpp',
     'backend/BackendClient.cpp',
     'utils/fd.cpp',
+    'utils/LibdisplayEdidWrapper.cpp',
     'utils/properties.cpp',
 )
 
diff --git a/tests/uevent_print.cpp b/tests/uevent_print.cpp
index 6ffbbfb..cfe2191 100644
--- a/tests/uevent_print.cpp
+++ b/tests/uevent_print.cpp
@@ -7,7 +7,7 @@
 int main() {
   auto uevent = android::UEvent::CreateInstance();
   if (!uevent) {
-    std::cout << "Can't initialize UEvent class" << std::endl;
+    std::cout << "Can't initialize UEvent class\n";
     return -ENODEV;
   }
 
@@ -18,8 +18,8 @@
       continue;
     }
 
-    std::cout << "New event #" << number++ << std::endl
-              << *msg << std::endl
-              << std::endl;
+    std::cout << "New event #" << number++ << '\n'
+              << *msg << '\n'
+              << std::flush;
   }
 }
diff --git a/utils/EdidWrapper.h b/utils/EdidWrapper.h
new file mode 100644
index 0000000..30124d7
--- /dev/null
+++ b/utils/EdidWrapper.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#pragma once
+
+#if HAS_LIBDISPLAY_INFO
+extern "C" {
+#include <libdisplay-info/info.h>
+}
+#endif
+
+#include <ui/GraphicTypes.h>
+
+#include "compositor/DisplayInfo.h"
+#include "drm/DrmUnique.h"
+
+namespace android {
+
+// Stub wrapper class for edid parsing
+class EdidWrapper {
+ public:
+  EdidWrapper() = default;
+  EdidWrapper(const EdidWrapper &) = delete;
+  virtual ~EdidWrapper() = default;
+
+  virtual void GetSupportedHdrTypes(std::vector<ui::Hdr> &types) {
+    types.clear();
+  };
+  virtual void GetHdrCapabilities(std::vector<ui::Hdr> &types,
+                                  const float * /*max_luminance*/,
+                                  const float * /*max_average_luminance*/,
+                                  const float * /*min_luminance*/) {
+    GetSupportedHdrTypes(types);
+  };
+  virtual void GetColorModes(std::vector<Colormode> &color_modes) {
+    color_modes.clear();
+  };
+};
+
+#if HAS_LIBDISPLAY_INFO
+// Wrapper class for that uses libdisplay-info to parse edids
+class LibdisplayEdidWrapper final : public EdidWrapper {
+ public:
+  LibdisplayEdidWrapper() = delete;
+  ~LibdisplayEdidWrapper() override {
+    di_info_destroy(info_);
+  }
+  static auto Create(DrmModePropertyBlobUnique blob)
+      -> std::unique_ptr<LibdisplayEdidWrapper>;
+
+  void GetSupportedHdrTypes(std::vector<ui::Hdr> &types) override;
+
+  void GetHdrCapabilities(std::vector<ui::Hdr> &types,
+                          const float *max_luminance,
+                          const float *max_average_luminance,
+                          const float *min_luminance) override;
+
+  void GetColorModes(std::vector<Colormode> &color_modes) override;
+
+ private:
+  LibdisplayEdidWrapper(di_info *info) : info_(std::move(info)) {
+  }
+
+  di_info *info_{};
+};
+#endif
+
+}  // namespace android
diff --git a/utils/LibdisplayEdidWrapper.cpp b/utils/LibdisplayEdidWrapper.cpp
new file mode 100644
index 0000000..d2d2a1c
--- /dev/null
+++ b/utils/LibdisplayEdidWrapper.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 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 "drmhwc"
+
+#if HAS_LIBDISPLAY_INFO
+
+#include "utils/EdidWrapper.h"
+#include "utils/log.h"
+
+namespace android {
+
+auto LibdisplayEdidWrapper::Create(DrmModePropertyBlobUnique blob)
+    -> std::unique_ptr<LibdisplayEdidWrapper> {
+  if (!blob)
+    return nullptr;
+
+  auto *info = di_info_parse_edid(blob->data, blob->length);
+  if (!info) {
+    ALOGW("Failed to parse edid blob.");
+    return nullptr;
+  }
+
+  return std::unique_ptr<LibdisplayEdidWrapper>(
+      new LibdisplayEdidWrapper(std::move(info)));
+}
+
+void LibdisplayEdidWrapper::GetSupportedHdrTypes(std::vector<ui::Hdr> &types) {
+  types.clear();
+
+  const auto *hdr_static_meta = di_info_get_hdr_static_metadata(info_);
+  const auto *colorimetries = di_info_get_supported_signal_colorimetry(info_);
+  if (colorimetries->bt2020_cycc || colorimetries->bt2020_ycc ||
+      colorimetries->bt2020_rgb) {
+    if (hdr_static_meta->pq)
+      types.emplace_back(ui::Hdr::HDR10);
+    if (hdr_static_meta->hlg)
+      types.emplace_back(ui::Hdr::HLG);
+  }
+}
+
+void LibdisplayEdidWrapper::GetHdrCapabilities(
+    std::vector<ui::Hdr> &types, const float *max_luminance,
+    const float *max_average_luminance, const float *min_luminance) {
+  GetSupportedHdrTypes(types);
+
+  const auto *hdr_static_meta = di_info_get_hdr_static_metadata(info_);
+  max_luminance = &hdr_static_meta->desired_content_max_luminance;
+  max_average_luminance = &hdr_static_meta
+                               ->desired_content_max_frame_avg_luminance;
+  min_luminance = &hdr_static_meta->desired_content_min_luminance;
+}
+
+void LibdisplayEdidWrapper::GetColorModes(std::vector<Colormode> &color_modes) {
+  color_modes.clear();
+  color_modes.emplace_back(Colormode::kNative);
+
+  const auto *hdr_static_meta = di_info_get_hdr_static_metadata(info_);
+  const auto *colorimetries = di_info_get_supported_signal_colorimetry(info_);
+
+  /* Rec. ITU-R BT.2020 constant luminance YCbCr */
+  /* Rec. ITU-R BT.2020 non-constant luminance YCbCr */
+  if (colorimetries->bt2020_cycc || colorimetries->bt2020_ycc)
+    color_modes.emplace_back(Colormode::kBt2020);
+
+  /* Rec. ITU-R BT.2020 RGB */
+  if (colorimetries->bt2020_rgb)
+    color_modes.emplace_back(Colormode::kDisplayBt2020);
+
+  /* SMPTE ST 2113 RGB: P3D65 and P3DCI */
+  if (colorimetries->st2113_rgb) {
+    color_modes.emplace_back(Colormode::kDciP3);
+    color_modes.emplace_back(Colormode::kDisplayP3);
+  }
+
+  /* Rec. ITU-R BT.2100 ICtCp HDR (with PQ and/or HLG) */
+  if (colorimetries->ictcp) {
+    if (hdr_static_meta->pq)
+      color_modes.emplace_back(Colormode::kBt2100Pq);
+    if (hdr_static_meta->hlg)
+      color_modes.emplace_back(Colormode::kBt2100Hlg);
+  }
+}
+
+}  // namespace android
+#endif