Merge 25Q1 (ab/12770256) to aosp-main-future

Bug: 385190204
Merged-In: I8af97262cb31e78318d44370f31f5b9890113183
Change-Id: I06e33e880c818cc760a14c30838c3e44a22c15a0
diff --git a/.ci/.gitlab-ci-checkcommit.sh b/.ci/.gitlab-ci-checkcommit.sh
index 3f597c3..5649475 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 diff -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 9dfe3d3..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/70643bd1c4d419015b9930b7aadc9cfd/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 640e0d0e04761c804db69f880e0c0957699babdbe82c72c507177b860d878569 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 b045da8..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/70643bd1c4d419015b9930b7aadc9cfd/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/70643bd1c4d419015b9930b7aadc9cfd/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 24d4d99..47b2cd8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,8 +42,6 @@
     name: "hwcomposer.drm_defaults",
 
     shared_libs: [
-        "android.hardware.graphics.composer@2.1-resources",
-        "android.hardware.graphics.composer@2.2-resources",
         "libcutils",
         "libdrm",
         "libhardware",
@@ -56,6 +54,7 @@
 
     static_libs: [
         "libaidlcommonsupport",
+        "libdisplay_info",
     ],
 
     header_libs: [
@@ -68,6 +67,7 @@
     ],
 
     cppflags: [
+        "-DHAS_LIBDISPLAY_INFO",
         "-DHWC2_INCLUDE_STRINGIFICATION",
         "-DHWC2_USE_CPP11",
     ],
@@ -117,6 +117,7 @@
         "hwc2_device/HwcLayer.cpp",
         "hwc2_device/hwc2_device.cpp",
 
+        "utils/LibdisplayEdidWrapper.cpp",
         "utils/fd.cpp",
         "utils/properties.cpp",
     ],
@@ -127,7 +128,6 @@
     srcs: [
         "hwc3/Composer.cpp",
         "hwc3/ComposerClient.cpp",
-        "hwc3/ComposerResources.cpp",
         "hwc3/DrmHwcThree.cpp",
         "hwc3/Utils.cpp",
     ],
@@ -154,40 +154,13 @@
     ],
 }
 
-// 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_binary {
-    name: "android.hardware.composer.hwc3-service.drm",
+cc_defaults {
+    name: "android.hardware.composer.hwc3-service.drm.defaults",
 
     srcs: [
         ":drm_hwcomposer_common",
         ":drm_hwcomposer_hwc3",
         ":drm_hwcomposer_service",
-        "bufferinfo/legacy/BufferInfoLibdrm.cpp",
     ],
 
     defaults: [
@@ -203,16 +176,25 @@
     ],
 
     cflags: [
+        "-DUSE_IMAPPER4_METADATA_API",
         "-Wall",
         "-Werror",
-
-        "-DUSE_IMAPPER4_METADATA_API",
     ],
 
     cppflags: [
         "-DHWC2_INCLUDE_STRINGIFICATION",
         "-DHWC2_USE_CPP11",
     ],
+}
+
+cc_binary {
+    name: "android.hardware.composer.hwc3-service.drm",
+
+    defaults: [
+        "android.hardware.composer.hwc3-service.drm.defaults",
+    ],
+
+    srcs: ["bufferinfo/legacy/BufferInfoLibdrm.cpp"],
 
     relative_install_path: "hw",
     vendor: true,
diff --git a/METADATA b/METADATA
index d97975c..afe2045 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +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: 2
+    day: 18
+  }
+  identifier {
+    type: "Git"
+    value: "https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer"
+    version: "75855a6ab877011a2e26ae95da7ddf06dd3a04b2"
+  }
 }
diff --git a/OWNERS b/OWNERS
index a4f788d..78b324f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,3 +2,4 @@
 ddavenport@google.com
 jstultz@google.com
 seanpaul@google.com
+include platform/system/core:/janitors/OWNERS #{LAST_RESORT_SUGGESTION}
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..305410c
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "VtsHalGraphicsComposer3_TargetTest"
+    }
+  ],
+  "desktop-presubmit": [
+    {
+      "name": "VtsHalGraphicsComposer3_TargetTest"
+    }
+  ]
+}
diff --git a/backend/Backend.cpp b/backend/Backend.cpp
index 91cb84d..bf45c08 100644
--- a/backend/Backend.cpp
+++ b/backend/Backend.cpp
@@ -108,7 +108,10 @@
   for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
     if (z_order >= first_z && z_order < first_z + size) {
       auto &df = layers[z_order]->GetLayerData().pi.display_frame;
-      pixops += (df.right - df.left) * (df.bottom - df.top);
+      if (df.i_rect) {
+        pixops += (df.i_rect->right - df.i_rect->left) *
+                  (df.i_rect->bottom - df.i_rect->top);
+      }
     }
   }
   return pixops;
diff --git a/bufferinfo/BufferInfo.h b/bufferinfo/BufferInfo.h
index b2297f9..db4c53e 100644
--- a/bufferinfo/BufferInfo.h
+++ b/bufferinfo/BufferInfo.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <memory>
 
 constexpr int kBufferMaxPlanes = 4;
 
@@ -40,6 +41,11 @@
   kCoverage,
 };
 
+class PrimeFdsSharedBase {
+ public:
+  virtual ~PrimeFdsSharedBase() = default;
+};
+
 struct BufferInfo {
   uint32_t width;
   uint32_t height;
@@ -54,4 +60,9 @@
   BufferColorSpace color_space;
   BufferSampleRange sample_range;
   BufferBlendMode blend_mode;
+
+  /* prime_fds field require valid file descriptors. While their lifecycle is
+   * managed elsewhere. The shared_ptr is used to ensure that the fds are not
+   * closed while the BufferInfo is still in use. */
+  std::shared_ptr<PrimeFdsSharedBase> fds_shared;
 };
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..962141f 100644
--- a/compositor/LayerData.h
+++ b/compositor/LayerData.h
@@ -16,9 +16,6 @@
 
 #pragma once
 
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
 #include <cmath>
 #include <cstdbool>
 #include <cstdint>
@@ -34,31 +31,60 @@
 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 SrcRectInfo {
+  struct FRect {
+    float left;
+    float top;
+    float right;
+    float bottom;
+  };
+  /* nullopt means the whole buffer */
+  std::optional<FRect> f_rect;
+};
+
+struct DstRectInfo {
+  struct IRect {
+    int32_t left;
+    int32_t top;
+    int32_t right;
+    int32_t bottom;
+  };
+  /* nullopt means the whole display */
+  std::optional<IRect> i_rect;
+};
+
+constexpr float kAlphaOpaque = 1.0F;
+
 struct PresentInfo {
   LayerTransform transform{};
-  uint16_t alpha = UINT16_MAX;
-  hwc_frect_t source_crop{};
-  hwc_rect_t display_frame{};
+  float alpha = kAlphaOpaque;
+  SrcRectInfo source_crop{};
+  DstRectInfo display_frame{};
 
   bool RequireScalingOrPhasing() const {
-    const float src_width = source_crop.right - source_crop.left;
-    const float src_height = source_crop.bottom - source_crop.top;
+    if (!source_crop.f_rect || !display_frame.i_rect) {
+      return false;
+    }
 
-    auto dest_width = float(display_frame.right - display_frame.left);
-    auto dest_height = float(display_frame.bottom - display_frame.top);
+    const auto &src = *source_crop.f_rect;
+    const auto &dst = *display_frame.i_rect;
+
+    const float src_width = src.right - src.left;
+    const float src_height = src.bottom - src.top;
+
+    auto dest_width = float(dst.right - dst.left);
+    auto dest_height = float(dst.bottom - dst.top);
 
     auto scaling = src_width != dest_width || src_height != dest_height;
-    auto phasing = (source_crop.left - std::floor(source_crop.left) != 0) ||
-                   (source_crop.top - std::floor(source_crop.top) != 0);
+    auto phasing = (src.left - std::floor(src.left) != 0) ||
+                   (src.top - std::floor(src.top) != 0);
     return scaling || phasing;
   }
 };
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp
index 9a8769a..299d30c 100644
--- a/drm/DrmAtomicStateManager.cpp
+++ b/drm/DrmAtomicStateManager.cpp
@@ -120,6 +120,9 @@
       return -EINVAL;
     }
 
+    auto raw_mode = args.display_mode.value().GetRawMode();
+    whole_display_rect_.i_rect = {0, 0, raw_mode.hdisplay, raw_mode.vdisplay};
+
     if (!crtc->GetModeProperty().AtomicSet(*pset, *new_frame_state.mode_blob)) {
       return -EINVAL;
     }
@@ -141,12 +144,29 @@
 
   if (args.colorspace && connector->GetColorspaceProperty()) {
     if (!connector->GetColorspaceProperty()
-             .AtomicSet(*pset, connector->GetColorspacePropertyValue(*args.colorspace)))
+             .AtomicSet(*pset, connector->GetColorspacePropertyValue(
+                                   *args.colorspace)))
       return -EINVAL;
   }
 
   if (args.content_type && connector->GetContentTypeProperty()) {
-    if (!connector->GetContentTypeProperty().AtomicSet(*pset, *args.content_type))
+    if (!connector->GetContentTypeProperty().AtomicSet(*pset,
+                                                       *args.content_type))
+      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;
   }
 
@@ -166,8 +186,8 @@
       auto &v = unused_planes;
       v.erase(std::remove(v.begin(), v.end(), joining.plane), v.end());
 
-      if (plane->AtomicSetState(*pset, layer, joining.z_pos, crtc->GetId()) !=
-          0) {
+      if (plane->AtomicSetState(*pset, layer, joining.z_pos, crtc->GetId(),
+                                whole_display_rect_) != 0) {
         return -EINVAL;
       }
     }
diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h
index 8d22b99..f97a488 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{};
 
@@ -108,6 +110,8 @@
   int frames_staged_{};
   int frames_tracked_{};
 
+  DstRectInfo whole_display_rect_{};
+
   void ThreadFn(const std::shared_ptr<DrmAtomicStateManager> &dasm);
   std::condition_variable cv_;
   std::mutex mutex_;
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/DrmDevice.cpp b/drm/DrmDevice.cpp
index 4534104..6fd5c0c 100644
--- a/drm/DrmDevice.cpp
+++ b/drm/DrmDevice.cpp
@@ -18,6 +18,7 @@
 
 #include "DrmDevice.h"
 
+#include <sys/mman.h>
 #include <xf86drm.h>
 #include <xf86drmMode.h>
 
@@ -34,13 +35,13 @@
 namespace android {
 
 auto DrmDevice::CreateInstance(std::string const &path,
-                               ResourceManager *res_man)
+                               ResourceManager *res_man, uint32_t index)
     -> std::unique_ptr<DrmDevice> {
   if (!IsKMSDev(path.c_str())) {
     return {};
   }
 
-  auto device = std::unique_ptr<DrmDevice>(new DrmDevice(res_man));
+  auto device = std::unique_ptr<DrmDevice>(new DrmDevice(res_man, index));
 
   if (device->Init(path.c_str()) != 0) {
     return {};
@@ -49,7 +50,8 @@
   return device;
 }
 
-DrmDevice::DrmDevice(ResourceManager *res_man) : res_man_(res_man) {
+DrmDevice::DrmDevice(ResourceManager *res_man, uint32_t index)
+    : index_in_dev_array_(index), res_man_(res_man) {
   drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
 }
 
@@ -260,4 +262,93 @@
   return encoders_;
 }
 
+class DumbBufferFd : public PrimeFdsSharedBase {
+ public:
+  SharedFd fd;
+};
+
+// NOLINTBEGIN(cppcoreguidelines-avoid-goto)
+auto DrmDevice::CreateBufferForModeset(uint32_t width, uint32_t height)
+    -> std::optional<BufferInfo> {
+  constexpr uint32_t kDumbBufferFormat = DRM_FORMAT_XRGB8888;
+  constexpr uint32_t kDumbBufferBpp = 32;
+
+  std::optional<BufferInfo> result;
+  void *ptr = MAP_FAILED;
+  struct drm_mode_create_dumb create = {
+      .height = height,
+      .width = width,
+      .bpp = kDumbBufferBpp,
+      .flags = 0,
+  };
+
+  int ret = drmIoctl(*fd_, DRM_IOCTL_MODE_CREATE_DUMB, &create);
+  if (ret != 0) {
+    ALOGE("Failed to DRM_IOCTL_MODE_CREATE_DUMB %d", errno);
+    return {};
+  }
+
+  struct drm_mode_map_dumb map = {
+      .handle = create.handle,
+  };
+
+  auto dumb_buffer_fd = std::make_shared<DumbBufferFd>();
+
+  BufferInfo buffer_info = {
+      .width = width,
+      .height = height,
+
+      .format = kDumbBufferFormat,
+      .pitches = {create.pitch},
+      .prime_fds = {-1, -1, -1, -1},
+      .modifiers = {DRM_FORMAT_MOD_NONE},
+
+      .color_space = BufferColorSpace::kUndefined,
+      .sample_range = BufferSampleRange::kUndefined,
+      .blend_mode = BufferBlendMode::kNone,
+
+      .fds_shared = dumb_buffer_fd,
+  };
+
+  ret = drmIoctl(*fd_, DRM_IOCTL_MODE_MAP_DUMB, &map);
+  if (ret != 0) {
+    ALOGE("Failed to DRM_IOCTL_MODE_MAP_DUMB %d", errno);
+    goto done;
+  }
+
+  ptr = mmap(nullptr, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd_,
+             (off_t)map.offset);
+  if (ptr == MAP_FAILED) {
+    ALOGE("Failed to mmap dumb buffer %d", errno);
+    goto done;
+  }
+
+  memset(ptr, 0, create.size);
+
+  if (munmap(ptr, create.size) != 0) {
+    ALOGE("Failed to unmap dumb buffer: %d", errno);
+  }
+
+  ret = drmPrimeHandleToFD(*fd_, create.handle, 0, &buffer_info.prime_fds[0]);
+  if (ret != 0) {
+    ALOGE("Failed to export dumb buffer as FD: %d", errno);
+    goto done;
+  }
+
+  dumb_buffer_fd->fd = MakeSharedFd(buffer_info.prime_fds[0]);
+
+  result = buffer_info;
+
+done:
+  if (create.handle > 0) {
+    struct drm_mode_destroy_dumb destroy = {
+        .handle = create.handle,
+    };
+    drmIoctl(*fd_, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
+  }
+
+  return result;
+}
+// NOLINTEND(cppcoreguidelines-avoid-goto)
+
 }  // namespace android
diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h
index cbaa536..baa719d 100644
--- a/drm/DrmDevice.h
+++ b/drm/DrmDevice.h
@@ -18,11 +18,13 @@
 
 #include <cstdint>
 #include <map>
+#include <optional>
 #include <tuple>
 
 #include "DrmConnector.h"
 #include "DrmCrtc.h"
 #include "DrmEncoder.h"
+#include "bufferinfo/BufferInfo.h"
 #include "utils/fd.h"
 
 namespace android {
@@ -35,13 +37,17 @@
  public:
   ~DrmDevice() = default;
 
-  static auto CreateInstance(std::string const &path, ResourceManager *res_man)
-      -> std::unique_ptr<DrmDevice>;
+  static auto CreateInstance(std::string const &path, ResourceManager *res_man,
+                             uint32_t index) -> std::unique_ptr<DrmDevice>;
 
   auto &GetFd() const {
     return fd_;
   }
 
+  auto GetIndexInDevArray() const {
+    return index_in_dev_array_;
+  }
+
   auto &GetResMan() {
     return *res_man_;
   }
@@ -70,6 +76,9 @@
     return HasAddFb2ModifiersSupport_;
   }
 
+  auto CreateBufferForModeset(uint32_t width, uint32_t height)
+      -> std::optional<BufferInfo>;
+
   auto &GetDrmFbImporter() {
     return *drm_fb_importer_;
   }
@@ -98,12 +107,13 @@
                   DrmProperty *property) const;
 
  private:
-  explicit DrmDevice(ResourceManager *res_man);
+  explicit DrmDevice(ResourceManager *res_man, uint32_t index);
   auto Init(const char *path) -> int;
 
   static auto IsKMSDev(const char *path) -> bool;
 
   SharedFd fd_;
+  const uint32_t index_in_dev_array_;
 
   std::vector<std::unique_ptr<DrmConnector>> connectors_;
   std::vector<std::unique_ptr<DrmConnector>> writeback_connectors_;
diff --git a/drm/DrmDisplayPipeline.cpp b/drm/DrmDisplayPipeline.cpp
index 2d81578..7588ee2 100644
--- a/drm/DrmDisplayPipeline.cpp
+++ b/drm/DrmDisplayPipeline.cpp
@@ -158,22 +158,12 @@
   return {};
 }
 
-static bool ReadUseOverlayProperty() {
-  char use_overlay_planes_prop[PROPERTY_VALUE_MAX];
-  property_get("vendor.hwc.drm.use_overlay_planes", use_overlay_planes_prop,
-               "1");
-  constexpr int kStrtolBase = 10;
-  return strtol(use_overlay_planes_prop, nullptr, kStrtolBase) != 0;
-}
-
 auto DrmDisplayPipeline::GetUsablePlanes()
     -> std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> {
   std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> planes;
   planes.emplace_back(primary_plane);
 
-  const static bool kUseOverlayPlanes = ReadUseOverlayProperty();
-
-  if (kUseOverlayPlanes) {
+  if (Properties::UseOverlayPlanes()) {
     for (const auto &plane : device->GetPlanes()) {
       if (plane->IsCrtcSupported(*crtc->Get())) {
         if (plane->GetType() == DRM_PLANE_TYPE_OVERLAY) {
diff --git a/drm/DrmHwc.cpp b/drm/DrmHwc.cpp
index aaba506..3f30123 100644
--- a/drm/DrmHwc.cpp
+++ b/drm/DrmHwc.cpp
@@ -200,7 +200,7 @@
   /* Virtual display is an experimental feature.
    * Unless explicitly set to true, return 0 for no support.
    */
-  if (0 == property_get_bool("vendor.hwc.drm.enable_virtual_display", 0)) {
+  if (!Properties::EnableVirtualDisplay()) {
     return 0;
   }
 
diff --git a/drm/DrmMode.cpp b/drm/DrmMode.cpp
index 7cbea44..d9fdb37 100644
--- a/drm/DrmMode.cpp
+++ b/drm/DrmMode.cpp
@@ -28,6 +28,11 @@
   return memcmp(&m, &mode_, offsetof(drmModeModeInfo, name)) == 0;
 }
 
+bool DrmMode::SameSize(const DrmMode &mode) const {
+  return (mode_.vdisplay == mode.mode_.vdisplay) &&
+         (mode_.hdisplay == mode.mode_.hdisplay);
+}
+
 auto DrmMode::CreateModeBlob(const DrmDevice &drm)
     -> DrmModeUserPropertyBlobUnique {
   struct drm_mode_modeinfo drm_mode = {};
diff --git a/drm/DrmMode.h b/drm/DrmMode.h
index 5450daf..7520824 100644
--- a/drm/DrmMode.h
+++ b/drm/DrmMode.h
@@ -35,6 +35,8 @@
 
   bool operator==(const drmModeModeInfo &m) const;
 
+  bool SameSize(const DrmMode &mode) const;
+
   auto &GetRawMode() const {
     return mode_;
   }
diff --git a/drm/DrmPlane.cpp b/drm/DrmPlane.cpp
index dbb5ad6..68887dd 100644
--- a/drm/DrmPlane.cpp
+++ b/drm/DrmPlane.cpp
@@ -25,6 +25,7 @@
 
 #include "DrmDevice.h"
 #include "bufferinfo/BufferInfoGetter.h"
+#include "compositor/LayerData.h"
 #include "utils/log.h"
 
 namespace android {
@@ -88,22 +89,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);
@@ -121,17 +108,17 @@
   GetPlaneProperty("IN_FENCE_FD", in_fence_fd_property_, Presence::kOptional);
 
   if (HasNonRgbFormat()) {
-    if (GetPlaneProperty("COLOR_ENCODING", color_encoding_propery_,
+    if (GetPlaneProperty("COLOR_ENCODING", color_encoding_property_,
                          Presence::kOptional)) {
-      color_encoding_propery_.AddEnumToMap("ITU-R BT.709 YCbCr",
-                                           BufferColorSpace::kItuRec709,
-                                           color_encoding_enum_map_);
-      color_encoding_propery_.AddEnumToMap("ITU-R BT.601 YCbCr",
-                                           BufferColorSpace::kItuRec601,
-                                           color_encoding_enum_map_);
-      color_encoding_propery_.AddEnumToMap("ITU-R BT.2020 YCbCr",
-                                           BufferColorSpace::kItuRec2020,
-                                           color_encoding_enum_map_);
+      color_encoding_property_.AddEnumToMap("ITU-R BT.709 YCbCr",
+                                            BufferColorSpace::kItuRec709,
+                                            color_encoding_enum_map_);
+      color_encoding_property_.AddEnumToMap("ITU-R BT.601 YCbCr",
+                                            BufferColorSpace::kItuRec601,
+                                            color_encoding_enum_map_);
+      color_encoding_property_.AddEnumToMap("ITU-R BT.2020 YCbCr",
+                                            BufferColorSpace::kItuRec2020,
+                                            color_encoding_enum_map_);
     }
 
     if (GetPlaneProperty("COLOR_RANGE", color_range_property_,
@@ -159,32 +146,50 @@
     // any CRTC already, which is protected by the plane_switching_crtc function
     // in the kernel drivers/gpu/drm/drm_atomic.c file.
     // The current drm_hwc design is not ready to support such scenario yet,
-    // so adding the CRTC status check here to workaorund for now.
+    // so adding the CRTC status check here to workaround for now.
     return false;
   }
 
   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) {
+  if (!alpha_property_ && layer->pi.alpha != kAlphaOpaque) {
     ALOGV("Alpha is not supported on plane %d", GetId());
     return false;
   }
@@ -218,27 +223,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;
@@ -246,7 +230,8 @@
 }
 
 auto DrmPlane::AtomicSetState(drmModeAtomicReq &pset, LayerData &layer,
-                              uint32_t zpos, uint32_t crtc_id) -> int {
+                              uint32_t zpos, uint32_t crtc_id,
+                              DstRectInfo &whole_display_rect) -> int {
   if (!layer.fb || !layer.bi) {
     ALOGE("%s: Invalid arguments", __func__);
     return -EINVAL;
@@ -268,8 +253,24 @@
     return -EINVAL;
   }
 
-  auto &disp = layer.pi.display_frame;
-  auto &src = layer.pi.source_crop;
+  auto opt_disp = layer.pi.display_frame.i_rect;
+  if (!layer.pi.display_frame.i_rect) {
+    opt_disp = whole_display_rect.i_rect;
+  }
+
+  auto opt_src = layer.pi.source_crop.f_rect;
+  if (!layer.pi.source_crop.f_rect) {
+    opt_src = {0.0F, 0.0F, float(layer.bi->width), float(layer.bi->height)};
+  }
+
+  if (!opt_disp || !opt_src) {
+    ALOGE("%s: Invalid display frame or source crop", __func__);
+    return -EINVAL;
+  }
+
+  auto disp = opt_disp.value();
+  auto src = opt_src.value();
+
   if (!crtc_property_.AtomicSet(pset, crtc_id) ||
       !fb_property_.AtomicSet(pset, layer.fb->GetFbId()) ||
       !crtc_x_property_.AtomicSet(pset, disp.left) ||
@@ -288,7 +289,9 @@
     return -EINVAL;
   }
 
-  if (alpha_property_ && !alpha_property_.AtomicSet(pset, layer.pi.alpha)) {
+  if (alpha_property_ &&
+      !alpha_property_.AtomicSet(pset,
+                                 std::lround(layer.pi.alpha * UINT16_MAX))) {
     return -EINVAL;
   }
 
@@ -299,7 +302,7 @@
   }
 
   if (color_encoding_enum_map_.count(layer.bi->color_space) != 0 &&
-      !color_encoding_propery_
+      !color_encoding_property_
            .AtomicSet(pset, color_encoding_enum_map_[layer.bi->color_space])) {
     return -EINVAL;
   }
diff --git a/drm/DrmPlane.h b/drm/DrmPlane.h
index c26a3cc..81306d9 100644
--- a/drm/DrmPlane.h
+++ b/drm/DrmPlane.h
@@ -49,7 +49,7 @@
   bool HasNonRgbFormat() const;
 
   auto AtomicSetState(drmModeAtomicReq &pset, LayerData &layer, uint32_t zpos,
-                      uint32_t crtc_id) -> int;
+                      uint32_t crtc_id, DstRectInfo &whole_display_rect) -> int;
   auto AtomicDisablePlane(drmModeAtomicReq &pset) -> int;
   auto &GetZPosProperty() const {
     return zpos_property_;
@@ -90,12 +90,12 @@
   DrmProperty alpha_property_;
   DrmProperty blend_property_;
   DrmProperty in_fence_fd_property_;
-  DrmProperty color_encoding_propery_;
+  DrmProperty color_encoding_property_;
   DrmProperty color_range_property_;
 
   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 0c23734..fee251e 100644
--- a/drm/ResourceManager.cpp
+++ b/drm/ResourceManager.cpp
@@ -55,7 +55,7 @@
   auto path_len = property_get("vendor.hwc.drm.device", path_pattern,
                                "/dev/dri/card%");
   if (path_pattern[path_len - 1] != '%') {
-    auto dev = DrmDevice::CreateInstance(path_pattern, this);
+    auto dev = DrmDevice::CreateInstance(path_pattern, this, 0);
     if (dev) {
       drms_.emplace_back(std::move(dev));
     }
@@ -69,17 +69,16 @@
       if (stat(path.str().c_str(), &buf) != 0)
         break;
 
-      auto dev = DrmDevice::CreateInstance(path.str(), this);
+      auto dev = DrmDevice::CreateInstance(path.str(), this, idx);
       if (dev) {
         drms_.emplace_back(std::move(dev));
       }
     }
   }
 
-  char proptext[PROPERTY_VALUE_MAX];
-  property_get("vendor.hwc.drm.scale_with_gpu", proptext, "0");
-  scale_with_gpu_ = bool(strncmp(proptext, "0", 1));
+  scale_with_gpu_ = Properties::ScaleWithGpu();
 
+  char proptext[PROPERTY_VALUE_MAX];
   constexpr char kDrmOrGpu[] = "DRM_OR_GPU";
   constexpr char kDrmOrIgnore[] = "DRM_OR_IGNORE";
   property_get("vendor.hwc.drm.ctm", proptext, kDrmOrGpu);
@@ -125,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 963a37b..defbe42 100644
--- a/drm/VSyncWorker.cpp
+++ b/drm/VSyncWorker.cpp
@@ -30,12 +30,9 @@
 
 namespace android {
 
-auto VSyncWorker::CreateInstance(std::shared_ptr<DrmDisplayPipeline> &pipe,
-                                 VSyncWorkerCallbacks &callbacks)
-    -> std::shared_ptr<VSyncWorker> {
-  auto vsw = std::shared_ptr<VSyncWorker>(new VSyncWorker());
-
-  vsw->callbacks_ = callbacks;
+auto VSyncWorker::CreateInstance(std::shared_ptr<DrmDisplayPipeline> &pipe)
+    -> std::unique_ptr<VSyncWorker> {
+  auto vsw = std::unique_ptr<VSyncWorker>(new VSyncWorker());
 
   if (pipe) {
     vsw->high_crtc_ = pipe->crtc->Get()->GetIndexInResArray()
@@ -43,32 +40,73 @@
     vsw->drm_fd_ = pipe->device->GetFd();
   }
 
-  std::thread(&VSyncWorker::ThreadFn, vsw.get(), vsw).detach();
+  vsw->vswt_ = std::thread(&VSyncWorker::ThreadFn, vsw.get());
 
   return vsw;
 }
 
-void VSyncWorker::VSyncControl(bool enabled) {
+VSyncWorker::~VSyncWorker() {
+  StopThread();
+
+  vswt_.join();
+}
+
+void VSyncWorker::UpdateVSyncControl() {
   {
     const std::lock_guard<std::mutex> lock(mutex_);
-    enabled_ = enabled;
+    enabled_ = ShouldEnable();
     last_timestamp_ = -1;
   }
 
   cv_.notify_all();
 }
 
+void VSyncWorker::SetVsyncPeriodNs(uint32_t vsync_period_ns) {
+  const std::lock_guard<std::mutex> lock(mutex_);
+  vsync_period_ns_ = vsync_period_ns;
+}
+
+void VSyncWorker::SetVsyncTimestampTracking(bool enabled) {
+  {
+    const std::lock_guard<std::mutex> lock(mutex_);
+    enable_vsync_timestamps_ = enabled;
+    if (enabled) {
+      // Reset the last timestamp so the caller knows if a vsync timestamp is
+      // fresh or not.
+      last_vsync_timestamp_ = 0;
+    }
+  }
+  UpdateVSyncControl();
+}
+
+uint32_t VSyncWorker::GetLastVsyncTimestamp() {
+  const std::lock_guard<std::mutex> lock(mutex_);
+  return last_vsync_timestamp_;
+}
+
+void VSyncWorker::SetTimestampCallback(
+    std::optional<VsyncTimestampCallback> &&callback) {
+  {
+    const std::lock_guard<std::mutex> lock(mutex_);
+    callback_ = std::move(callback);
+  }
+  UpdateVSyncControl();
+}
+
 void VSyncWorker::StopThread() {
   {
     const std::lock_guard<std::mutex> lock(mutex_);
     thread_exit_ = true;
     enabled_ = false;
-    callbacks_ = {};
   }
 
   cv_.notify_all();
 }
 
+bool VSyncWorker::ShouldEnable() const {
+  return enable_vsync_timestamps_ || callback_.has_value();
+};
+
 /*
  * Returns the timestamp of the next vsync in phase with last_timestamp_.
  * For example:
@@ -86,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_;
 }
 
@@ -95,13 +133,7 @@
 int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) {
   auto time_now = ResourceManager::GetTimeMonotonicNs();
 
-  // Default to 60Hz refresh rate
-  constexpr uint32_t kDefaultVSPeriodNs = 16666666;
-  auto period_ns = kDefaultVSPeriodNs;
-  if (callbacks_.get_vperiod_ns && callbacks_.get_vperiod_ns() != 0)
-    period_ns = callbacks_.get_vperiod_ns();
-
-  auto phased_timestamp = GetPhasedVSync(period_ns, time_now);
+  auto phased_timestamp = GetPhasedVSync(vsync_period_ns_, time_now);
   struct timespec vsync {};
   vsync.tv_sec = int(phased_timestamp / kOneSecondNs);
   vsync.tv_nsec = int(phased_timestamp - (vsync.tv_sec * kOneSecondNs));
@@ -117,17 +149,17 @@
   return 0;
 }
 
-void VSyncWorker::ThreadFn(const std::shared_ptr<VSyncWorker> &vsw) {
+void VSyncWorker::ThreadFn() {
   int ret = 0;
 
   for (;;) {
     {
-      std::unique_lock<std::mutex> lock(vsw->mutex_);
+      std::unique_lock<std::mutex> lock(mutex_);
       if (thread_exit_)
         break;
 
       if (!enabled_)
-        vsw->cv_.wait(lock);
+        cv_.wait(lock);
 
       if (!enabled_)
         continue;
@@ -158,18 +190,21 @@
                   (int64_t)vblank.reply.tval_usec * kUsToNsMul;
     }
 
-    decltype(callbacks_.out_event) callback;
+    std::optional<VsyncTimestampCallback> vsync_callback;
 
     {
       const std::lock_guard<std::mutex> lock(mutex_);
       if (!enabled_)
         continue;
-      callback = callbacks_.out_event;
+      if (enable_vsync_timestamps_) {
+        last_vsync_timestamp_ = timestamp;
+      }
+      vsync_callback = callback_;
     }
 
-    if (callback)
-      callback(timestamp);
-
+    if (vsync_callback) {
+      vsync_callback.value()(timestamp, vsync_period_ns_);
+    }
     last_timestamp_ = timestamp;
   }
 
diff --git a/drm/VSyncWorker.h b/drm/VSyncWorker.h
index 2a4c7c8..c76dd14 100644
--- a/drm/VSyncWorker.h
+++ b/drm/VSyncWorker.h
@@ -26,31 +26,41 @@
 
 namespace android {
 
-struct VSyncWorkerCallbacks {
-  std::function<void(uint64_t /*timestamp*/)> out_event;
-  std::function<uint32_t()> get_vperiod_ns;
-};
-
 class VSyncWorker {
  public:
-  ~VSyncWorker() = default;
+  using VsyncTimestampCallback = std::function<void(int64_t /*timestamp*/,
+                                                    uint32_t /*period*/)>;
 
-  auto static CreateInstance(std::shared_ptr<DrmDisplayPipeline> &pipe,
-                             VSyncWorkerCallbacks &callbacks)
-      -> std::shared_ptr<VSyncWorker>;
+  ~VSyncWorker();
 
-  void VSyncControl(bool enabled);
+  auto static CreateInstance(std::shared_ptr<DrmDisplayPipeline> &pipe)
+      -> std::unique_ptr<VSyncWorker>;
+
+  // Set the expected vsync period.
+  void SetVsyncPeriodNs(uint32_t vsync_period_ns);
+
+  // Set or clear a callback to be fired on vsync.
+  void SetTimestampCallback(std::optional<VsyncTimestampCallback> &&callback);
+
+  // Enable vsync timestamp tracking. GetLastVsyncTimestamp will return 0 if
+  // vsync tracking is disabled, or if no vsync has happened since it was
+  // enabled.
+  void SetVsyncTimestampTracking(bool enabled);
+  uint32_t GetLastVsyncTimestamp();
+
   void StopThread();
 
  private:
   VSyncWorker() = default;
 
-  void ThreadFn(const std::shared_ptr<VSyncWorker> &vsw);
+  void ThreadFn();
 
   int64_t GetPhasedVSync(int64_t frame_ns, int64_t current) const;
   int SyntheticWaitVBlank(int64_t *timestamp);
 
-  VSyncWorkerCallbacks callbacks_;
+  // Must hold the lock before calling these.
+  void UpdateVSyncControl();
+  bool ShouldEnable() const;
 
   SharedFd drm_fd_;
   uint32_t high_crtc_ = 0;
@@ -59,6 +69,14 @@
   bool thread_exit_ = false;
   int64_t last_timestamp_ = -1;
 
+  // Default to 60Hz refresh rate
+  static constexpr uint32_t kDefaultVSPeriodNs = 16666666;
+  // Needs to be threadsafe.
+  uint32_t vsync_period_ns_ = kDefaultVSPeriodNs;
+  bool enable_vsync_timestamps_ = false;
+  uint32_t last_vsync_timestamp_ = 0;
+  std::optional<VsyncTimestampCallback> callback_;
+
   std::condition_variable cv_;
   std::thread vswt_;
   std::mutex mutex_;
diff --git a/hooks/check-non-public-commits b/hooks/check-non-public-commits
index fc20795..1e8e997 100755
--- a/hooks/check-non-public-commits
+++ b/hooks/check-non-public-commits
@@ -8,7 +8,8 @@
 
 # Don't bother checking if this is being pushed to gerrit.
 if [[ "$url" = "sso://googleplex-android/platform/external/drm_hwcomposer" ]] ||
-   [[ "$url" = "sso://android.googlesource.com/platform/external/drm_hwcomposer" ]]
+   [[ "$url" = "sso://android.googlesource.com/platform/external/drm_hwcomposer" ]] ||
+   [[ "$url" = "sso://android/platform/external/drm_hwcomposer" ]]
 then
   exit 0
 fi
diff --git a/hwc2_device/HwcDisplay.cpp b/hwc2_device/HwcDisplay.cpp
index 0503f39..42ebd63 100644
--- a/hwc2_device/HwcDisplay.cpp
+++ b/hwc2_device/HwcDisplay.cpp
@@ -21,10 +21,7 @@
 
 #include <cinttypes>
 
-#include <hardware/gralloc.h>
-#include <ui/GraphicBufferAllocator.h>
-#include <ui/GraphicBufferMapper.h>
-#include <ui/PixelFormat.h>
+#include <ui/ColorSpace.h>
 
 #include "backend/Backend.h"
 #include "backend/BackendManager.h"
@@ -37,78 +34,67 @@
 #include "utils/properties.h"
 
 using ::android::DrmDisplayPipeline;
+using ColorGamut = ::android::ColorSpace;
 
 namespace android {
 
 namespace {
-// 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.
-auto GetModesetBuffer(uint32_t width, uint32_t height) -> buffer_handle_t {
-  constexpr PixelFormat format = PIXEL_FORMAT_RGBA_8888;
-  constexpr uint64_t usage = GRALLOC_USAGE_SW_READ_OFTEN |
-                             GRALLOC_USAGE_SW_WRITE_OFTEN |
-                             GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
 
-  constexpr uint32_t layer_count = 1;
-  const std::string name = "drm-hwcomposer";
+constexpr int kCtmRows = 3;
+constexpr int kCtmCols = 3;
 
-  buffer_handle_t handle = nullptr;
-  uint32_t stride = 0;
-  status_t status = GraphicBufferAllocator::get().allocate(width, height,
-                                                           format, layer_count,
-                                                           usage, &handle,
-                                                           &stride, name);
-  if (status != OK) {
-    ALOGE("Failed to allocate modeset buffer.");
-    return nullptr;
-  }
+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,
+};
 
-  void *data = nullptr;
-  Rect bounds = {0, 0, static_cast<int32_t>(width),
-                 static_cast<int32_t>(height)};
-  status = GraphicBufferMapper::get().lock(handle, usage, bounds, &data);
-  if (status != OK) {
-    ALOGE("Failed to map modeset buffer.");
-    GraphicBufferAllocator::get().free(handle);
-    return nullptr;
-  }
-
-  // Cast one of the multiplicands to ensure that the multiplication happens
-  // in a wider type (size_t).
-  const size_t buffer_size = static_cast<size_t>(height) * stride *
-                             bytesPerPixel(format);
-  memset(data, 0, buffer_size);
-  status = GraphicBufferMapper::get().unlock(handle);
-  ALOGW_IF(status != OK, "Failed to unmap buffer.");
-  return handle;
+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 GetModesetLayerProperties(buffer_handle_t buffer, uint32_t width,
-                               uint32_t height) -> HwcLayer::LayerProperties {
-  HwcLayer::LayerProperties properties;
-  properties.buffer = {.buffer_handle = buffer, .acquire_fence = {}};
-  properties.display_frame = {
-      .left = 0,
-      .top = 0,
-      .right = int(width),
-      .bottom = int(height),
-  };
-  properties.source_crop = (hwc_frect_t){
-      .left = 0.0F,
-      .top = 0.0F,
-      .right = static_cast<float>(width),
-      .bottom = static_cast<float>(height),
-  };
-  properties.blend_mode = BufferBlendMode::kNone;
-  return properties;
+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;
 }
+
 }  // namespace
 
 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 +136,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;
     }
   }
 
@@ -205,14 +209,23 @@
     modeset_layer_data = client_layer_.GetLayerData();
   } else {
     ALOGV("Allocate modeset buffer.");
-    buffer_handle_t modeset_buffer = GetModesetBuffer(width, height);
-    if (modeset_buffer != nullptr) {
+    auto modeset_buffer =  //
+        GetPipe().device->CreateBufferForModeset(width, height);
+    if (modeset_buffer) {
       auto modeset_layer = std::make_unique<HwcLayer>(this);
-      modeset_layer->SetLayerProperties(
-          GetModesetLayerProperties(modeset_buffer, width, height));
+      HwcLayer::LayerProperties properties;
+      properties.slot_buffer = {
+          .slot_id = 0,
+          .bi = modeset_buffer,
+      };
+      properties.active_slot = {
+          .slot_id = 0,
+          .fence = {},
+      };
+      properties.blend_mode = BufferBlendMode::kNone;
+      modeset_layer->SetLayerProperties(properties);
       modeset_layer->PopulateLayerData();
       modeset_layer_data = modeset_layer->GetLayerData();
-      GraphicBufferAllocator::get().free(modeset_buffer);
     }
   }
 
@@ -232,6 +245,8 @@
   ALOGV("Blocking config succeeded.");
   configs_.active_config_id = config;
   staged_mode_config_id_.reset();
+  vsync_worker_->SetVsyncPeriodNs(new_config->mode.GetVSyncPeriodNs());
+  // set new vsync period
   return ConfigError::kNone;
 }
 
@@ -263,13 +278,93 @@
   staged_mode_config_id_ = config;
 
   // Enable vsync events until the mode has been applied.
-  last_vsync_ts_ = 0;
-  vsync_tracking_en_ = true;
-  vsync_worker_->VSyncControl(true);
+  vsync_worker_->SetVsyncTimestampTracking(true);
 
   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 {
+  if (IsInHeadlessMode()) {
+    return true;
+  }
+  HWC2::Error ret{};
+
+  ++total_stats_.total_frames_;
+
+  AtomicCommitArgs a_args{};
+  ret = CreateComposition(a_args);
+
+  if (ret != HWC2::Error::None)
+    ++total_stats_.failed_kms_present_;
+
+  if (ret == HWC2::Error::BadLayer) {
+    // Can we really have no client or device layers?
+    return true;
+  }
+  if (ret != HWC2::Error::None)
+    return false;
+
+  out_present_fence = a_args.out_fence;
+
+  // Reset the color matrix so we don't apply it over and over again.
+  color_matrix_ = {};
+
+  ++frame_no_;
+
+  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();
 
@@ -301,43 +396,18 @@
   }
 
   if (vsync_worker_) {
-    // TODO: There should be a mechanism to wait for this worker to complete,
-    // otherwise there is a race condition while destructing the HwcDisplay.
     vsync_worker_->StopThread();
     vsync_worker_ = {};
   }
 
-  SetClientTarget(nullptr, -1, 0, {});
+  client_layer_.ClearSlots();
 }
 
 HWC2::Error HwcDisplay::Init() {
   ChosePreferredConfig();
 
-  auto vsw_callbacks = (VSyncWorkerCallbacks){
-      .out_event =
-          [this](int64_t timestamp) {
-            const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
-            if (vsync_event_en_) {
-              uint32_t period_ns{};
-              GetDisplayVsyncPeriod(&period_ns);
-              hwc_->SendVsyncEventToClient(handle_, timestamp, period_ns);
-            }
-            if (vsync_tracking_en_) {
-              last_vsync_ts_ = timestamp;
-            }
-            if (!vsync_event_en_ && !vsync_tracking_en_) {
-              vsync_worker_->VSyncControl(false);
-            }
-          },
-      .get_vperiod_ns = [this]() -> uint32_t {
-        uint32_t outVsyncPeriod = 0;
-        GetDisplayVsyncPeriod(&outVsyncPeriod);
-        return outVsyncPeriod;
-      },
-  };
-
   if (type_ != HWC2::DisplayType::Virtual) {
-    vsync_worker_ = VSyncWorker::CreateInstance(pipeline_, vsw_callbacks);
+    vsync_worker_ = VSyncWorker::CreateInstance(pipeline_);
     if (!vsync_worker_) {
       ALOGE("Failed to create event worker for d=%d\n", int(handle_));
       return HWC2::Error::BadDisplay;
@@ -355,7 +425,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();
 
@@ -395,12 +467,6 @@
   return SetActiveConfig(configs_.preferred_config_id);
 }
 
-HWC2::Error HwcDisplay::AcceptDisplayChanges() {
-  for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_)
-    l.second.AcceptTypeChange();
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::CreateLayer(hwc2_layer_t *layer) {
   layers_.emplace(static_cast<hwc2_layer_t>(layer_idx_), HwcLayer(this));
   *layer = static_cast<hwc2_layer_t>(layer_idx_);
@@ -427,58 +493,31 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetChangedCompositionTypes(uint32_t *num_elements,
-                                                   hwc2_layer_t *layers,
-                                                   int32_t *types) {
-  if (IsInHeadlessMode()) {
-    *num_elements = 0;
-    return HWC2::Error::None;
-  }
-
-  uint32_t num_changes = 0;
-  for (auto &l : layers_) {
-    if (l.second.IsTypeChanged()) {
-      if (layers && num_changes < *num_elements)
-        layers[num_changes] = l.first;
-      if (types && num_changes < *num_elements)
-        types[num_changes] = static_cast<int32_t>(l.second.GetValidatedType());
-      ++num_changes;
-    }
-  }
-  if (!layers && !types)
-    *num_elements = num_changes;
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcDisplay::GetClientTargetSupport(uint32_t width, uint32_t height,
-                                               int32_t /*format*/,
-                                               int32_t dataspace) {
-  if (IsInHeadlessMode()) {
-    return HWC2::Error::None;
-  }
-
-  auto min = pipeline_->device->GetMinResolution();
-  auto max = pipeline_->device->GetMaxResolution();
-
-  if (width < min.first || height < min.second)
-    return HWC2::Error::Unsupported;
-
-  if (width > max.first || height > max.second)
-    return HWC2::Error::Unsupported;
-
-  if (dataspace != HAL_DATASPACE_UNKNOWN)
-    return HWC2::Error::Unsupported;
-
-  // TODO(nobody): Validate format can be handled by either GL or planes
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::GetColorModes(uint32_t *num_modes, int32_t *modes) {
-  if (!modes)
+  if (IsInHeadlessMode()) {
     *num_modes = 1;
+    if (modes)
+      modes[0] = HAL_COLOR_MODE_NATIVE;
+    return HWC2::Error::None;
+  }
 
-  if (modes)
-    *modes = HAL_COLOR_MODE_NATIVE;
+  if (!modes) {
+    std::vector<Colormode> temp_modes;
+    GetEdid()->GetColorModes(temp_modes);
+    *num_modes = temp_modes.size();
+    return HWC2::Error::None;
+  }
+
+  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;
 }
@@ -574,71 +613,45 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetDisplayRequests(int32_t * /*display_requests*/,
-                                           uint32_t *num_elements,
-                                           hwc2_layer_t * /*layers*/,
-                                           int32_t * /*layer_requests*/) {
-  // TODO(nobody): I think virtual display should request
-  //      HWC2_DISPLAY_REQUEST_WRITE_CLIENT_TARGET_TO_OUTPUT here
-  *num_elements = 0;
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::GetDisplayType(int32_t *type) {
   *type = static_cast<int32_t>(type_);
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetDozeSupport(int32_t *support) {
-  *support = 0;
-  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;
-  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=1767
- *
- * Called after PresentDisplay(), CLIENT is expecting release fence for the
- * prior buffer (not the one assigned to the layer at the moment).
- */
-HWC2::Error HwcDisplay::GetReleaseFences(uint32_t *num_elements,
-                                         hwc2_layer_t *layers,
-                                         int32_t *fences) {
+HWC2::Error HwcDisplay::GetHdrCapabilities(uint32_t *num_types, int32_t *types,
+                                           float *max_luminance,
+                                           float *max_average_luminance,
+                                           float *min_luminance) {
   if (IsInHeadlessMode()) {
-    *num_elements = 0;
+    *num_types = 0;
     return HWC2::Error::None;
   }
 
-  uint32_t num_layers = 0;
-
-  for (auto &l : layers_) {
-    if (!l.second.GetPriorBufferScanOutFlag() || !present_fence_) {
-      continue;
-    }
-
-    ++num_layers;
-
-    if (layers == nullptr || fences == nullptr)
-      continue;
-
-    if (num_layers > *num_elements) {
-      ALOGW("Overflow num_elements %d/%d", num_layers, *num_elements);
-      return HWC2::Error::None;
-    }
-
-    layers[num_layers - 1] = l.first;
-    fences[num_layers - 1] = DupFd(present_fence_);
+  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;
   }
-  *num_elements = num_layers;
 
+  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;
 }
 
@@ -650,6 +663,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) {
@@ -670,6 +684,7 @@
   return args;
 }
 
+// NOLINTNEXTLINE(readability-function-cognitive-complexity)
 HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
   if (IsInHeadlessMode()) {
     ALOGE("%s: Display is in headless mode, should never reach here", __func__);
@@ -679,11 +694,12 @@
   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);
 
-  auto mode_update_commited_ = false;
+  std::optional<uint32_t> new_vsync_period_ns;
   if (staged_mode_config_id_ &&
       staged_mode_change_time_ <= ResourceManager::GetTimeMonotonicNs()) {
     const HwcDisplayConfig *staged_config = GetConfig(
@@ -691,17 +707,11 @@
     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)});
 
     configs_.active_config_id = staged_mode_config_id_.value();
-
     a_args.display_mode = staged_config->mode;
     if (!a_args.test_only) {
-      mode_update_commited_ = true;
+      new_vsync_period_ns = staged_config->mode.GetVSyncPeriodNs();
     }
   }
 
@@ -723,9 +733,24 @@
         continue;
     }
   }
-  if (use_client_layer)
+  if (use_client_layer) {
     z_map.emplace(client_z_order, &client_layer_);
 
+    client_layer_.PopulateLayerData();
+    if (!client_layer_.IsLayerUsableAsDevice()) {
+      ALOGE_IF(!a_args.test_only,
+               "Client layer must be always usable by DRM/KMS");
+      /* This may be normally triggered on validation of the first frame
+       * containing CLIENT layer. At this moment client buffer is not yet
+       * provided by the CLIENT.
+       * This may be triggered once in HwcLayer lifecycle in case FB can't be
+       * imported. For example when non-contiguous buffer is imported into
+       * contiguous-only DRM/KMS driver.
+       */
+      return HWC2::Error::BadLayer;
+    }
+  }
+
   if (z_map.empty())
     return HWC2::Error::BadLayer;
 
@@ -739,13 +764,6 @@
   // now that they're ordered by z, add them to the composition
   for (std::pair<const uint32_t, HwcLayer *> &l : z_map) {
     if (!l.second->IsLayerUsableAsDevice()) {
-      /* This will be normally triggered on validation of the first frame
-       * containing CLIENT layer. At this moment client buffer is not yet
-       * provided by the CLIENT.
-       * This may be triggered once in HwcLayer lifecycle in case FB can't be
-       * imported. For example when non-contiguous buffer is imported into
-       * contiguous-only DRM/KMS driver.
-       */
       return HWC2::Error::BadLayer;
     }
     composition_layers.emplace_back(l.second->GetLayerData());
@@ -758,6 +776,11 @@
                                                std::move(composition_layers));
 
   if (type_ == HWC2::DisplayType::Virtual) {
+    writeback_layer_->PopulateLayerData();
+    if (!writeback_layer_->IsLayerUsableAsDevice()) {
+      ALOGE("Output layer must be always usable by DRM/KMS");
+      return HWC2::Error::BadLayer;
+    }
     a_args.writeback_fb = writeback_layer_->GetLayerData().fb;
     a_args.writeback_release_fence = writeback_layer_->GetLayerData()
                                          .acquire_fence;
@@ -777,12 +800,15 @@
     return HWC2::Error::BadParameter;
   }
 
-  if (mode_update_commited_) {
+  if (new_vsync_period_ns) {
+    vsync_worker_->SetVsyncPeriodNs(new_vsync_period_ns.value());
     staged_mode_config_id_.reset();
-    vsync_tracking_en_ = false;
-    if (last_vsync_ts_ != 0) {
+
+    vsync_worker_->SetVsyncTimestampTracking(false);
+    uint32_t last_vsync_ts = vsync_worker_->GetLastVsyncTimestamp();
+    if (last_vsync_ts != 0) {
       hwc_->SendVsyncPeriodTimingChangedEventToClient(handle_,
-                                                      last_vsync_ts_ +
+                                                      last_vsync_ts +
                                                           prev_vperiod_ns);
     }
   }
@@ -790,43 +816,6 @@
   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=1805
- */
-HWC2::Error HwcDisplay::PresentDisplay(int32_t *out_present_fence) {
-  if (IsInHeadlessMode()) {
-    *out_present_fence = -1;
-    return HWC2::Error::None;
-  }
-  HWC2::Error ret{};
-
-  ++total_stats_.total_frames_;
-
-  AtomicCommitArgs a_args{};
-  ret = CreateComposition(a_args);
-
-  if (ret != HWC2::Error::None)
-    ++total_stats_.failed_kms_present_;
-
-  if (ret == HWC2::Error::BadLayer) {
-    // Can we really have no client or device layers?
-    *out_present_fence = -1;
-    return HWC2::Error::None;
-  }
-  if (ret != HWC2::Error::None)
-    return ret;
-
-  this->present_fence_ = a_args.out_fence;
-  *out_present_fence = DupFd(a_args.out_fence);
-
-  // Reset the color matrix so we don't apply it over and over again.
-  color_matrix_ = {};
-
-  ++frame_no_;
-
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::SetActiveConfigInternal(uint32_t config,
                                                 int64_t change_time) {
   if (configs_.hwc_configs.count(config) == 0) {
@@ -844,78 +833,51 @@
   return SetActiveConfigInternal(config, ResourceManager::GetTimeMonotonicNs());
 }
 
-/* Find API details at:
- * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:hardware/libhardware/include/hardware/hwcomposer2.h;l=1861
- */
-HWC2::Error HwcDisplay::SetClientTarget(buffer_handle_t target,
-                                        int32_t acquire_fence,
-                                        int32_t dataspace,
-                                        hwc_region_t /*damage*/) {
-  client_layer_.SetLayerBuffer(target, acquire_fence);
-  client_layer_.SetLayerDataspace(dataspace);
-
-  /*
-   * target can be nullptr, this does mean the Composer Service is calling
-   * cleanDisplayResources() on after receiving HOTPLUG event. See more at:
-   * https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h;l=350;drc=944b68180b008456ed2eb4d4d329e33b19bd5166
-   */
-  if (target == nullptr) {
-    client_layer_.SwChainClearCache();
-    return HWC2::Error::None;
-  }
-
-  if (IsInHeadlessMode()) {
-    return HWC2::Error::None;
-  }
-
-  client_layer_.PopulateLayerData();
-  if (!client_layer_.IsLayerUsableAsDevice()) {
-    ALOGE("Client layer must be always usable by DRM/KMS");
-    return HWC2::Error::BadLayer;
-  }
-
-  auto &bi = client_layer_.GetLayerData().bi;
-  if (!bi) {
-    ALOGE("%s: Invalid state", __func__);
-    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);
-
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::SetColorMode(int32_t mode) {
   /* 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;
   }
@@ -924,17 +886,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)
@@ -957,37 +908,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:
@@ -1010,18 +938,6 @@
   return true;
 }
 
-HWC2::Error HwcDisplay::SetOutputBuffer(buffer_handle_t buffer,
-                                        int32_t release_fence) {
-  writeback_layer_->SetLayerBuffer(buffer, release_fence);
-  writeback_layer_->PopulateLayerData();
-  if (!writeback_layer_->IsLayerUsableAsDevice()) {
-    ALOGE("Output layer must be always usable by DRM/KMS");
-    return HWC2::Error::BadLayer;
-  }
-  /* TODO: Check if format is supported by writeback connector */
-  return HWC2::Error::None;
-}
-
 HWC2::Error HwcDisplay::SetPowerMode(int32_t mode_in) {
   auto mode = static_cast<HWC2::PowerMode>(mode_in);
 
@@ -1070,33 +986,24 @@
   if (type_ == HWC2::DisplayType::Virtual) {
     return HWC2::Error::None;
   }
+  if (!vsync_worker_) {
+    return HWC2::Error::NoResources;
+  }
 
   vsync_event_en_ = HWC2_VSYNC_ENABLE == enabled;
+  std::optional<VSyncWorker::VsyncTimestampCallback> callback = std::nullopt;
   if (vsync_event_en_) {
-    vsync_worker_->VSyncControl(true);
+    DrmHwc *hwc = hwc_;
+    hwc2_display_t id = handle_;
+    // Callback will be called from the vsync thread.
+    callback = [hwc, id](int64_t timestamp, uint32_t period_ns) {
+      hwc->SendVsyncEventToClient(id, timestamp, period_ns);
+    };
   }
+  vsync_worker_->SetTimestampCallback(std::move(callback));
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::ValidateDisplay(uint32_t *num_types,
-                                        uint32_t *num_requests) {
-  if (IsInHeadlessMode()) {
-    *num_types = *num_requests = 0;
-    return HWC2::Error::None;
-  }
-
-  /* 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);
-  }
-
-  return backend_->ValidateDisplay(this, num_types, num_requests);
-}
-
 std::vector<HwcLayer *> HwcDisplay::GetOrderLayersByZPos() {
   std::vector<HwcLayer *> ordered_layers;
   ordered_layers.reserve(layers_.size());
@@ -1120,6 +1027,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()) {
@@ -1170,22 +1132,7 @@
   outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeConstraints
                                               ->desiredTimeNanos;
 
-  last_vsync_ts_ = 0;
-  vsync_tracking_en_ = true;
-  vsync_worker_->VSyncControl(true);
-
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcDisplay::SetAutoLowLatencyMode(bool /*on*/) {
-  return HWC2::Error::Unsupported;
-}
-
-HWC2::Error HwcDisplay::GetSupportedContentTypes(
-    uint32_t *outNumSupportedContentTypes,
-    const uint32_t *outSupportedContentTypes) {
-  if (outSupportedContentTypes == nullptr)
-    *outNumSupportedContentTypes = 0;
+  vsync_worker_->SetVsyncTimestampTracking(true);
 
   return HWC2::Error::None;
 }
@@ -1211,12 +1158,21 @@
     return HWC2::Error::Unsupported;
   }
 
-  auto blob = GetPipe().connector->Get()->GetEdidBlob();
+  auto *connector = GetPipe().connector->Get();
+  auto blob = connector->GetEdidBlob();
   if (!blob) {
     return HWC2::Error::Unsupported;
   }
 
-  *outPort = handle_; /* TDOD(nobody): What should be here? */
+  constexpr uint8_t kDrmDeviceBitShift = 5U;
+  constexpr uint8_t kDrmDeviceBitMask = 0xE0;
+  constexpr uint8_t kConnectorBitMask = 0x1F;
+  const auto kDrmIdx = static_cast<uint8_t>(
+      connector->GetDev().GetIndexInDevArray());
+  const auto kConnectorIdx = static_cast<uint8_t>(
+      connector->GetIndexInResArray());
+  *outPort = (((kDrmIdx << kDrmDeviceBitShift) & kDrmDeviceBitMask) |
+              (kConnectorIdx & kConnectorBitMask));
 
   if (outData) {
     *outDataSize = std::min(*outDataSize, blob->length);
@@ -1258,15 +1214,6 @@
   return HWC2::Error::None;
 }
 
-HWC2::Error HwcDisplay::GetDisplayBrightnessSupport(bool *supported) {
-  *supported = false;
-  return HWC2::Error::None;
-}
-
-HWC2::Error HwcDisplay::SetDisplayBrightness(float /* brightness */) {
-  return HWC2::Error::Unsupported;
-}
-
 #endif /* __ANDROID_API__ > 28 */
 
 #if __ANDROID_API__ > 27
diff --git a/hwc2_device/HwcDisplay.h b/hwc2_device/HwcDisplay.h
index ecca514..7391785 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"
@@ -36,6 +38,11 @@
 class Backend;
 class DrmHwc;
 
+class FrontendDisplayBase {
+ public:
+  virtual ~FrontendDisplayBase() = default;
+};
+
 inline constexpr uint32_t kPrimaryDisplay = 0;
 
 // NOLINTNEXTLINE
@@ -52,6 +59,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,24 +96,41 @@
   // 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;
+
+  auto GetFrontendPrivateData() -> std::shared_ptr<FrontendDisplayBase> {
+    return frontend_private_data_;
+  }
+
+  auto SetFrontendPrivateData(std::shared_ptr<FrontendDisplayBase> data) {
+    frontend_private_data_ = std::move(data);
+  }
+
   // HWC2 Hooks - these should not be used outside of the hwc2 device.
-  HWC2::Error AcceptDisplayChanges();
   HWC2::Error CreateLayer(hwc2_layer_t *layer);
   HWC2::Error DestroyLayer(hwc2_layer_t layer);
   HWC2::Error GetActiveConfig(hwc2_config_t *config) const;
-  HWC2::Error GetChangedCompositionTypes(uint32_t *num_elements,
-                                         hwc2_layer_t *layers, int32_t *types);
-  HWC2::Error GetClientTargetSupport(uint32_t width, uint32_t height,
-                                     int32_t format, int32_t dataspace);
   HWC2::Error GetColorModes(uint32_t *num_modes, int32_t *modes);
   HWC2::Error GetDisplayAttribute(hwc2_config_t config, int32_t attribute,
                                   int32_t *value);
   HWC2::Error LegacyGetDisplayConfigs(uint32_t *num_configs,
                                       hwc2_config_t *configs);
   HWC2::Error GetDisplayName(uint32_t *size, char *name);
-  HWC2::Error GetDisplayRequests(int32_t *display_requests,
-                                 uint32_t *num_elements, hwc2_layer_t *layers,
-                                 int32_t *layer_requests);
   HWC2::Error GetDisplayType(int32_t *type);
 #if __ANDROID_API__ > 27
   HWC2::Error GetRenderIntents(int32_t mode, uint32_t *outNumIntents,
@@ -116,8 +143,6 @@
                                            uint8_t *outData);
   HWC2::Error GetDisplayCapabilities(uint32_t *outNumCapabilities,
                                      uint32_t *outCapabilities);
-  HWC2::Error GetDisplayBrightnessSupport(bool *supported);
-  HWC2::Error SetDisplayBrightness(float);
 #endif
 #if __ANDROID_API__ > 29
   HWC2::Error GetDisplayConnectionType(uint32_t *outType);
@@ -126,33 +151,21 @@
       hwc2_config_t config,
       hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints,
       hwc_vsync_period_change_timeline_t *outTimeline);
-  HWC2::Error SetAutoLowLatencyMode(bool on);
-  HWC2::Error GetSupportedContentTypes(
-      uint32_t *outNumSupportedContentTypes,
-      const uint32_t *outSupportedContentTypes);
 
   HWC2::Error SetContentType(int32_t contentType);
 #endif
   HWC2::Error GetDisplayVsyncPeriod(uint32_t *outVsyncPeriod);
 
-  HWC2::Error GetDozeSupport(int32_t *support);
   HWC2::Error GetHdrCapabilities(uint32_t *num_types, int32_t *types,
                                  float *max_luminance,
                                  float *max_average_luminance,
                                  float *min_luminance);
-  HWC2::Error GetReleaseFences(uint32_t *num_elements, hwc2_layer_t *layers,
-                               int32_t *fences);
-  HWC2::Error PresentDisplay(int32_t *out_present_fence);
   HWC2::Error SetActiveConfig(hwc2_config_t config);
   HWC2::Error ChosePreferredConfig();
-  HWC2::Error SetClientTarget(buffer_handle_t target, int32_t acquire_fence,
-                              int32_t dataspace, hwc_region_t damage);
   HWC2::Error SetColorMode(int32_t mode);
   HWC2::Error SetColorTransform(const float *matrix, int32_t hint);
-  HWC2::Error SetOutputBuffer(buffer_handle_t buffer, int32_t release_fence);
   HWC2::Error SetPowerMode(int32_t mode);
   HWC2::Error SetVsyncEnabled(int32_t enabled);
-  HWC2::Error ValidateDisplay(uint32_t *num_types, uint32_t *num_requests);
   HwcLayer *get_layer(hwc2_layer_t layer) {
     auto it = layers_.find(layer);
     if (it == layers_.end())
@@ -216,6 +229,10 @@
     return flatcon_;
   }
 
+  auto GetClientLayer() -> HwcLayer & {
+    return client_layer_;
+  }
+
   auto &GetWritebackLayer() {
     return writeback_layer_;
   }
@@ -236,8 +253,6 @@
 
   DrmHwc *const hwc_;
 
-  SharedFd present_fence_;
-
   int64_t staged_mode_change_time_{};
   std::optional<uint32_t> staged_mode_config_id_{};
 
@@ -246,10 +261,8 @@
   std::unique_ptr<Backend> backend_;
   std::shared_ptr<FlatteningController> flatcon_;
 
-  std::shared_ptr<VSyncWorker> vsync_worker_;
+  std::unique_ptr<VSyncWorker> vsync_worker_;
   bool vsync_event_en_{};
-  bool vsync_tracking_en_{};
-  int64_t last_vsync_ts_{};
 
   const hwc2_display_t handle_;
   HWC2::DisplayType type_;
@@ -262,12 +275,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_;
 
@@ -281,6 +293,12 @@
   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();
+  }
+
+  std::shared_ptr<FrontendDisplayBase> frontend_private_data_;
 };
 
 }  // namespace android
diff --git a/hwc2_device/HwcLayer.cpp b/hwc2_device/HwcLayer.cpp
index cb18fdd..d0ff216 100644
--- a/hwc2_device/HwcLayer.cpp
+++ b/hwc2_device/HwcLayer.cpp
@@ -25,10 +25,21 @@
 namespace android {
 
 void HwcLayer::SetLayerProperties(const LayerProperties& layer_properties) {
-  if (layer_properties.buffer) {
-    layer_data_.acquire_fence = layer_properties.buffer->acquire_fence;
-    buffer_handle_ = layer_properties.buffer->buffer_handle;
-    buffer_handle_updated_ = true;
+  if (layer_properties.slot_buffer) {
+    auto slot_id = layer_properties.slot_buffer->slot_id;
+    if (!layer_properties.slot_buffer->bi) {
+      slots_.erase(slot_id);
+    } else {
+      slots_[slot_id] = {
+          .bi = layer_properties.slot_buffer->bi.value(),
+          .fb = {},
+      };
+    }
+  }
+  if (layer_properties.active_slot) {
+    active_slot_id_ = layer_properties.active_slot->slot_id;
+    layer_data_.acquire_fence = layer_properties.active_slot->fence;
+    buffer_updated_ = true;
   }
   if (layer_properties.blend_mode) {
     blend_mode_ = layer_properties.blend_mode.value();
@@ -46,8 +57,7 @@
     layer_data_.pi.display_frame = layer_properties.display_frame.value();
   }
   if (layer_properties.alpha) {
-    layer_data_.pi.alpha = std::lround(layer_properties.alpha.value() *
-                                       UINT16_MAX);
+    layer_data_.pi.alpha = layer_properties.alpha.value();
   }
   if (layer_properties.source_crop) {
     layer_data_.pi.source_crop = layer_properties.source_crop.value();
@@ -60,192 +70,40 @@
   }
 }
 
-// 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_) {
+  if (!IsLayerUsableAsDevice() || !buffer_updated_ ||
+      !active_slot_id_.has_value()) {
     return;
   }
-  buffer_handle_updated_ = false;
+  buffer_updated_ = false;
 
-  layer_data_.fb = {};
-
-  auto unique_id = BufferInfoGetter::GetInstance()->GetUniqueId(buffer_handle_);
-  if (unique_id && SwChainGetBufferFromCache(*unique_id)) {
+  if (slots_[*active_slot_id_].fb) {
     return;
   }
 
-  layer_data_.bi = BufferInfoGetter::GetInstance()->GetBoInfo(buffer_handle_);
-  if (!layer_data_.bi) {
-    ALOGW("Unable to get buffer information (0x%p)", buffer_handle_);
-    bi_get_failed_ = true;
-    return;
-  }
+  auto& fb_importer = parent_->GetPipe().device->GetDrmFbImporter();
+  auto fb = fb_importer.GetOrCreateFbId(&slots_[*active_slot_id_].bi);
 
-  layer_data_
-      .fb = parent_->GetPipe().device->GetDrmFbImporter().GetOrCreateFbId(
-      &layer_data_.bi.value());
-
-  if (!layer_data_.fb) {
-    ALOGV("Unable to create framebuffer object for buffer 0x%p",
-          buffer_handle_);
+  if (!fb) {
+    ALOGE("Unable to create framebuffer object for layer %p", this);
     fb_import_failed_ = true;
     return;
   }
 
-  if (unique_id) {
-    SwChainAddCurrentBuffer(*unique_id);
-  }
+  slots_[*active_slot_id_].fb = fb;
 }
 
 void HwcLayer::PopulateLayerData() {
   ImportFb();
 
-  if (!layer_data_.bi) {
-    ALOGE("%s: Invalid state", __func__);
+  if (!active_slot_id_.has_value()) {
+    ALOGE("Internal error: populate layer data called without active slot");
     return;
   }
 
+  layer_data_.bi = slots_[*active_slot_id_].bi;
+  layer_data_.fb = slots_[*active_slot_id_].fb;
+
   if (blend_mode_ != BufferBlendMode::kUndefined) {
     layer_data_.bi->blend_mode = blend_mode_;
   }
@@ -257,75 +115,9 @@
   }
 }
 
-/* SwapChain Cache */
-
-bool HwcLayer::SwChainGetBufferFromCache(BufferUniqueId unique_id) {
-  if (swchain_lookup_table_.count(unique_id) == 0) {
-    return false;
-  }
-
-  auto seq = swchain_lookup_table_[unique_id];
-
-  if (swchain_cache_.count(seq) == 0) {
-    return false;
-  }
-
-  auto& el = swchain_cache_[seq];
-  if (!el.bi) {
-    return false;
-  }
-
-  layer_data_.bi = el.bi;
-  layer_data_.fb = el.fb;
-
-  return true;
-}
-
-void HwcLayer::SwChainReassemble(BufferUniqueId unique_id) {
-  if (swchain_lookup_table_.count(unique_id) != 0) {
-    if (swchain_lookup_table_[unique_id] ==
-        int(swchain_lookup_table_.size()) - 1) {
-      /* Skip same buffer */
-      return;
-    }
-    if (swchain_lookup_table_[unique_id] == 0) {
-      swchain_reassembled_ = true;
-      return;
-    }
-    /* Tracking error */
-    SwChainClearCache();
-    return;
-  }
-
-  swchain_lookup_table_[unique_id] = int(swchain_lookup_table_.size());
-}
-
-void HwcLayer::SwChainAddCurrentBuffer(BufferUniqueId unique_id) {
-  if (!swchain_reassembled_) {
-    SwChainReassemble(unique_id);
-  }
-
-  if (swchain_reassembled_) {
-    if (swchain_lookup_table_.count(unique_id) == 0) {
-      SwChainClearCache();
-      return;
-    }
-
-    auto seq = swchain_lookup_table_[unique_id];
-
-    if (swchain_cache_.count(seq) == 0) {
-      swchain_cache_[seq] = {};
-    }
-
-    swchain_cache_[seq].bi = layer_data_.bi;
-    swchain_cache_[seq].fb = layer_data_.fb;
-  }
-}
-
-void HwcLayer::SwChainClearCache() {
-  swchain_cache_.clear();
-  swchain_lookup_table_.clear();
-  swchain_reassembled_ = false;
+void HwcLayer::ClearSlots() {
+  slots_.clear();
+  active_slot_id_.reset();
 }
 
 }  // namespace android
\ No newline at end of file
diff --git a/hwc2_device/HwcLayer.h b/hwc2_device/HwcLayer.h
index e1d62b7..c335d88 100644
--- a/hwc2_device/HwcLayer.h
+++ b/hwc2_device/HwcLayer.h
@@ -18,30 +18,43 @@
 
 #include <aidl/android/hardware/graphics/common/Transform.h>
 #include <hardware/hwcomposer2.h>
+#include <memory>
 
+#include "bufferinfo/BufferInfo.h"
 #include "bufferinfo/BufferInfoGetter.h"
 #include "compositor/LayerData.h"
+#include "utils/fd.h"
 
 namespace android {
 
 class HwcDisplay;
 
+class FrontendLayerBase {
+ public:
+  virtual ~FrontendLayerBase() = default;
+};
+
 class HwcLayer {
  public:
   struct Buffer {
-    buffer_handle_t buffer_handle;
-    SharedFd acquire_fence;
+    int32_t slot_id;
+    std::optional<BufferInfo> bi;
+  };
+  struct Slot {
+    int32_t slot_id;
+    SharedFd fence;
   };
   // A set of properties to be validated.
   struct LayerProperties {
-    std::optional<Buffer> buffer;
+    std::optional<Buffer> slot_buffer;
+    std::optional<Slot> active_slot;
     std::optional<BufferBlendMode> blend_mode;
     std::optional<BufferColorSpace> color_space;
     std::optional<BufferSampleRange> sample_range;
     std::optional<HWC2::Composition> composition_type;
-    std::optional<hwc_rect_t> display_frame;
+    std::optional<DstRectInfo> display_frame;
     std::optional<float> alpha;
-    std::optional<hwc_frect_t> source_crop;
+    std::optional<SrcRectInfo> source_crop;
     std::optional<LayerTransform> transform;
     std::optional<uint32_t> z_order;
   };
@@ -82,21 +95,13 @@
 
   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);
+  auto GetFrontendPrivateData() -> std::shared_ptr<FrontendLayerBase> {
+    return frontend_private_data_;
+  }
+
+  auto SetFrontendPrivateData(std::shared_ptr<FrontendLayerBase> data) {
+    frontend_private_data_ = std::move(data);
+  }
 
  private:
   // sf_type_ stores the initial type given to us by surfaceflinger,
@@ -117,43 +122,32 @@
   BufferColorSpace color_space_{};
   BufferSampleRange sample_range_{};
   BufferBlendMode blend_mode_{};
-  buffer_handle_t buffer_handle_{};
-  bool buffer_handle_updated_{};
+  bool buffer_updated_{};
 
   bool prior_buffer_scanout_flag_{};
 
   HwcDisplay *const parent_;
 
-  /* Layer state */
- public:
-  void PopulateLayerData();
+  std::shared_ptr<FrontendLayerBase> frontend_private_data_;
 
-  bool IsLayerUsableAsDevice() const {
-    return !bi_get_failed_ && !fb_import_failed_ && buffer_handle_ != nullptr;
-  }
-
- private:
-  void ImportFb();
-  bool bi_get_failed_{};
-  bool fb_import_failed_{};
-
-  /* SwapChain Cache */
- public:
-  void SwChainClearCache();
-
- private:
-  struct SwapChainElement {
-    std::optional<BufferInfo> bi;
+  std::optional<int32_t> active_slot_id_;
+  struct BufferSlot {
+    BufferInfo bi;
     std::shared_ptr<DrmFbIdHandle> fb;
   };
+  std::map<int32_t /*slot*/, BufferSlot> slots_;
 
-  bool SwChainGetBufferFromCache(BufferUniqueId unique_id);
-  void SwChainReassemble(BufferUniqueId unique_id);
-  void SwChainAddCurrentBuffer(BufferUniqueId unique_id);
+  void ImportFb();
+  bool fb_import_failed_{};
 
-  std::map<int /*seq_no*/, SwapChainElement> swchain_cache_;
-  std::map<BufferUniqueId, int /*seq_no*/> swchain_lookup_table_;
-  bool swchain_reassembled_{};
+ public:
+  void PopulateLayerData();
+  void ClearSlots();
+
+  bool IsLayerUsableAsDevice() const {
+    return !fb_import_failed_ && active_slot_id_.has_value() &&
+           slots_.count(*active_slot_id_) > 0;
+  }
 };
 
 }  // namespace android
diff --git a/hwc2_device/hwc2_device.cpp b/hwc2_device/hwc2_device.cpp
index 28b6963..72d0aa9 100644
--- a/hwc2_device/hwc2_device.cpp
+++ b/hwc2_device/hwc2_device.cpp
@@ -19,10 +19,16 @@
 
 #define LOG_TAG "drmhwc"
 
+#include <cassert>
 #include <cinttypes>
+#include <memory>
+#include <optional>
+
+#include <cutils/native_handle.h>
 
 #include "DrmHwcTwo.h"
 #include "backend/Backend.h"
+#include "hwc2_device/HwcLayer.h"
 #include "utils/log.h"
 
 namespace android {
@@ -43,6 +49,126 @@
   return str.substr(p1, p2 - p1);
 }
 
+class Hwc2DeviceDisplay : public FrontendDisplayBase {
+ public:
+  std::vector<HwcDisplay::ReleaseFence> release_fences;
+  std::vector<HwcDisplay::ChangedLayer> changed_layers;
+};
+
+static auto GetHwc2DeviceDisplay(HwcDisplay &display)
+    -> std::shared_ptr<Hwc2DeviceDisplay> {
+  auto frontend_private_data = display.GetFrontendPrivateData();
+  if (!frontend_private_data) {
+    frontend_private_data = std::make_shared<Hwc2DeviceDisplay>();
+    display.SetFrontendPrivateData(frontend_private_data);
+  }
+  return std::static_pointer_cast<Hwc2DeviceDisplay>(frontend_private_data);
+}
+
+class Hwc2DeviceLayer : public FrontendLayerBase {
+ public:
+  auto HandleNextBuffer(buffer_handle_t buffer_handle, int32_t fence_fd)
+      -> std::pair<std::optional<HwcLayer::LayerProperties>,
+                   bool /* not a swapchain */> {
+    auto slot = GetSlotNumber(buffer_handle);
+
+    if (invalid_) {
+      return std::make_pair(std::nullopt, true);
+    }
+
+    bool buffer_provided = false;
+    bool not_a_swapchain = true;
+    int32_t slot_id = 0;
+
+    if (slot.has_value()) {
+      buffer_provided = swchain_slots_[slot.value()];
+      slot_id = slot.value();
+      not_a_swapchain = true;
+    }
+
+    HwcLayer::LayerProperties lp;
+    if (!buffer_provided) {
+      auto bo_info = BufferInfoGetter::GetInstance()->GetBoInfo(buffer_handle);
+      if (!bo_info) {
+        invalid_ = true;
+        return std::make_pair(std::nullopt, true);
+      }
+
+      lp.slot_buffer = {
+          .slot_id = slot_id,
+          .bi = bo_info,
+      };
+    }
+    lp.active_slot = {
+        .slot_id = slot_id,
+        .fence = MakeSharedFd(fence_fd),
+    };
+
+    return std::make_pair(lp, not_a_swapchain);
+  }
+
+  void SwChainClearCache() {
+    swchain_lookup_table_.clear();
+    swchain_slots_.clear();
+    swchain_reassembled_ = false;
+  }
+
+ private:
+  auto GetSlotNumber(buffer_handle_t buffer_handle) -> std::optional<int32_t> {
+    auto unique_id = BufferInfoGetter::GetInstance()->GetUniqueId(
+        buffer_handle);
+    if (!unique_id) {
+      ALOGE("Failed to get unique id for buffer handle %p", buffer_handle);
+      return std::nullopt;
+    }
+
+    if (swchain_lookup_table_.count(*unique_id) == 0) {
+      SwChainReassemble(*unique_id);
+      return std::nullopt;
+    }
+
+    if (!swchain_reassembled_) {
+      return std::nullopt;
+    }
+
+    return swchain_lookup_table_[*unique_id];
+  }
+
+  void SwChainReassemble(BufferUniqueId unique_id) {
+    if (swchain_lookup_table_.count(unique_id) != 0) {
+      if (swchain_lookup_table_[unique_id] ==
+          int(swchain_lookup_table_.size()) - 1) {
+        /* Skip same buffer */
+        return;
+      }
+      if (swchain_lookup_table_[unique_id] == 0) {
+        swchain_reassembled_ = true;
+        return;
+      }
+      /* Tracking error */
+      SwChainClearCache();
+      return;
+    }
+
+    swchain_lookup_table_[unique_id] = int(swchain_lookup_table_.size());
+  }
+
+  bool invalid_{}; /* Layer is invalid and should be skipped */
+  std::map<BufferUniqueId, int /*slot*/> swchain_lookup_table_;
+  std::map<int /*slot*/, bool /*buffer_provided*/> swchain_slots_;
+  bool swchain_reassembled_{};
+};
+
+static auto GetHwc2DeviceLayer(HwcLayer &layer)
+    -> std::shared_ptr<Hwc2DeviceLayer> {
+  auto frontend_private_data = layer.GetFrontendPrivateData();
+  if (!frontend_private_data) {
+    frontend_private_data = std::make_shared<Hwc2DeviceLayer>();
+    layer.SetFrontendPrivateData(frontend_private_data);
+  }
+  return std::static_pointer_cast<Hwc2DeviceLayer>(frontend_private_data);
+}
+
 struct Drmhwc2Device : hwc2_device {
   DrmHwcTwo drmhwctwo;
 };
@@ -54,6 +180,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 +208,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 +220,517 @@
   *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;
+  }
+}
+
+/* Display functions */
+static int32_t GetDisplayRequests(hwc2_device_t * /*device*/,
+                                  hwc2_display_t /*display*/,
+                                  int32_t * /* out_display_requests */,
+                                  uint32_t *out_num_elements,
+                                  hwc2_layer_t * /*out_layers*/,
+                                  int32_t * /*out_layer_requests*/) {
+  ALOGV("GetDisplayRequests");
+
+  *out_num_elements = 0;
+  return 0;
+}
+
+static int32_t GetDozeSupport(hwc2_device_t * /*device*/,
+                              hwc2_display_t /*display*/,
+                              int32_t *out_support) {
+  ALOGV("GetDozeSupport");
+  *out_support = 0;  // Doze support is not available
+  return 0;
+}
+
+static int32_t GetClientTargetSupport(hwc2_device_t * /*device*/,
+                                      hwc2_display_t /*display*/,
+                                      uint32_t /*width*/, uint32_t /*height*/,
+                                      int32_t /*format*/, int32_t dataspace) {
+  ALOGV("GetClientTargetSupport");
+
+  if (dataspace != HAL_DATASPACE_UNKNOWN)
+    return static_cast<int32_t>(HWC2::Error::Unsupported);
+
+  return 0;
+}
+
+static int32_t SetClientTarget(hwc2_device_t *device, hwc2_display_t display,
+                               buffer_handle_t target, int32_t acquire_fence,
+                               int32_t dataspace, hwc_region_t /*damage*/) {
+  ALOGV("SetClientTarget");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto &client_layer = idisplay->GetClientLayer();
+  auto h2l = GetHwc2DeviceLayer(client_layer);
+  if (!h2l) {
+    client_layer.SetFrontendPrivateData(std::make_shared<Hwc2DeviceLayer>());
+  }
+
+  if (target == nullptr) {
+    client_layer.ClearSlots();
+    h2l->SwChainClearCache();
+
+    return 0;
+  }
+
+  auto [lp, not_a_swapchain] = h2l->HandleNextBuffer(target, acquire_fence);
+  if (!lp) {
+    ALOGE("Failed to process client target");
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+  }
+
+  if (not_a_swapchain) {
+    client_layer.ClearSlots();
+  }
+
+  lp->color_space = Hwc2ToColorSpace(dataspace);
+  lp->sample_range = Hwc2ToSampleRange(dataspace);
+
+  idisplay->GetClientLayer().SetLayerProperties(lp.value());
+
+  return 0;
+}
+
+static int32_t SetOutputBuffer(hwc2_device_t *device, hwc2_display_t display,
+                               buffer_handle_t buffer, int32_t release_fence) {
+  ALOGV("SetOutputBuffer");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto &writeback_layer = idisplay->GetWritebackLayer();
+  if (!writeback_layer) {
+    ALOGE("Writeback layer is not available");
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+  }
+
+  auto h2l = GetHwc2DeviceLayer(*writeback_layer);
+  if (!h2l) {
+    writeback_layer->SetFrontendPrivateData(
+        std::make_shared<Hwc2DeviceLayer>());
+  }
+
+  auto [lp, not_a_swapchain] = h2l->HandleNextBuffer(buffer, release_fence);
+  if (!lp) {
+    ALOGE("Failed to process output buffer");
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+  }
+
+  if (not_a_swapchain) {
+    writeback_layer->ClearSlots();
+  }
+
+  writeback_layer->SetLayerProperties(lp.value());
+
+  return 0;
+}
+
+static int32_t AcceptDisplayChanges(hwc2_device_t *device,
+                                    hwc2_display_t display) {
+  ALOGV("AcceptDisplayChanges");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  idisplay->AcceptValidatedComposition();
+
+  return 0;
+}
+
+static int32_t GetReleaseFences(hwc2_device_t *device, hwc2_display_t display,
+                                uint32_t *out_num_elements,
+                                hwc2_layer_t *out_layers, int32_t *out_fences) {
+  ALOGV("GetReleaseFences");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+  if (*out_num_elements < hwc2display->release_fences.size()) {
+    ALOGW("Overflow num_elements %d/%zu", *out_num_elements,
+          hwc2display->release_fences.size());
+    return static_cast<int32_t>(HWC2::Error::NoResources);
+  }
+
+  for (size_t i = 0; i < hwc2display->release_fences.size(); ++i) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic):
+    out_layers[i] = hwc2display->release_fences[i].first;
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic):
+    out_fences[i] = DupFd(hwc2display->release_fences[i].second);
+  }
+
+  *out_num_elements = hwc2display->release_fences.size();
+  hwc2display->release_fences.clear();
+
+  return static_cast<int32_t>(HWC2::Error::None);
+}
+
+static int32_t ValidateDisplay(hwc2_device_t *device, hwc2_display_t display,
+                               uint32_t *out_num_types,
+                               uint32_t *out_num_requests) {
+  ALOGV("ValidateDisplay");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+  hwc2display->changed_layers = idisplay->ValidateStagedComposition();
+
+  *out_num_types = hwc2display->changed_layers.size();
+  *out_num_requests = 0;
+
+  return 0;
+}
+
+static int32_t GetChangedCompositionTypes(hwc2_device_t *device,
+                                          hwc2_display_t display,
+                                          uint32_t *out_num_elements,
+                                          hwc2_layer_t *out_layers,
+                                          int32_t *out_types) {
+  ALOGV("GetChangedCompositionTypes");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+  if (*out_num_elements < hwc2display->changed_layers.size()) {
+    ALOGW("Overflow num_elements %d/%zu", *out_num_elements,
+          hwc2display->changed_layers.size());
+    return static_cast<int32_t>(HWC2::Error::NoResources);
+  }
+
+  for (size_t i = 0; i < hwc2display->changed_layers.size(); ++i) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic):
+    out_layers[i] = hwc2display->changed_layers[i].first;
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic):
+    out_types[i] = static_cast<int32_t>(hwc2display->changed_layers[i].second);
+  }
+
+  *out_num_elements = hwc2display->changed_layers.size();
+  hwc2display->changed_layers.clear();
+
+  return static_cast<int32_t>(HWC2::Error::None);
+}
+
+static int32_t PresentDisplay(hwc2_device_t *device, hwc2_display_t display,
+                              int32_t *out_release_fence) {
+  ALOGV("PresentDisplay");
+  LOCK_COMPOSER(device);
+  GET_DISPLAY(display);
+
+  auto hwc2display = GetHwc2DeviceDisplay(*idisplay);
+
+  SharedFd out_fence;
+
+  hwc2display->release_fences.clear();
+
+  if (!idisplay->PresentStagedComposition(out_fence,
+                                          hwc2display->release_fences)) {
+    ALOGE("Failed to present display");
+    return static_cast<int32_t>(HWC2::Error::BadDisplay);
+  }
+
+  *out_release_fence = DupFd(out_fence);
+
+  return 0;
+}
+
+#if __ANDROID_API__ >= 28
+
+static int32_t GetDisplayBrightnessSupport(hwc2_device_t * /*device*/,
+                                           hwc2_display_t /*display*/,
+                                           bool *out_support) {
+  ALOGV("GetDisplayBrightnessSupport");
+  *out_support = false;  // Brightness support is not available
+  return static_cast<int32_t>(HWC2::Error::None);
+}
+
+static int32_t SetDisplayBrightness(hwc2_device_t * /*device*/,
+                                    hwc2_display_t /*display*/,
+                                    float /*brightness*/) {
+  ALOGV("SetDisplayBrightness");
+  return static_cast<int32_t>(HWC2::Error::Unsupported);
+}
+
+#endif
+
+#if __ANDROID_API__ >= 29
+static int32_t SetAutoLowLatencyMode(hwc2_device_t * /*device*/,
+                                     hwc2_display_t /*display*/, bool /*on*/) {
+  ALOGV("SetAutoLowLatencyMode");
+  return static_cast<int32_t>(HWC2::Error::Unsupported);
+}
+
+static int32_t GetSupportedContentTypes(
+    hwc2_device_t * /*device*/, hwc2_display_t /*display*/,
+    uint32_t *out_num_supported_content_types,
+    uint32_t * /*out_supported_content_types*/) {
+  ALOGV("GetSupportedContentTypes");
+  *out_num_supported_content_types = 0;
+  return static_cast<int32_t>(HWC2::Error::None);
+}
+#endif
+
+/* Layer functions */
+
+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);
+
+  auto h2l = GetHwc2DeviceLayer(*ilayer);
+
+  auto [lp, not_a_swapchain] = h2l->HandleNextBuffer(buffer, acquire_fence);
+  if (!lp) {
+    ALOGV("Failed to process layer buffer");
+    return static_cast<int32_t>(HWC2::Error::BadLayer);
+  }
+
+  if (not_a_swapchain) {
+    ilayer->ClearSlots();
+  }
+
+  ilayer->SetLayerProperties(lp.value());
+
+  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 = {
+      .i_rect = DstRectInfo::IRect{.left = frame.left,
+                                   .top = frame.top,
+                                   .right = frame.right,
+                                   .bottom = frame.bottom}};
+  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 = {
+      .f_rect = SrcRectInfo::FRect{.left = crop.left,
+                                   .top = crop.top,
+                                   .right = crop.right,
+                                   .bottom = crop.bottom}};
+  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);
@@ -141,9 +761,7 @@
 
     // Display functions
     case HWC2::FunctionDescriptor::AcceptDisplayChanges:
-      return ToHook<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
-          DisplayHook<decltype(&HwcDisplay::AcceptDisplayChanges),
-                      &HwcDisplay::AcceptDisplayChanges>);
+      return (hwc2_function_pointer_t)AcceptDisplayChanges;
     case HWC2::FunctionDescriptor::CreateLayer:
       return ToHook<HWC2_PFN_CREATE_LAYER>(
           DisplayHook<decltype(&HwcDisplay::CreateLayer),
@@ -157,15 +775,9 @@
           DisplayHook<decltype(&HwcDisplay::GetActiveConfig),
                       &HwcDisplay::GetActiveConfig, hwc2_config_t *>);
     case HWC2::FunctionDescriptor::GetChangedCompositionTypes:
-      return ToHook<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
-          DisplayHook<decltype(&HwcDisplay::GetChangedCompositionTypes),
-                      &HwcDisplay::GetChangedCompositionTypes, uint32_t *,
-                      hwc2_layer_t *, int32_t *>);
+      return (hwc2_function_pointer_t)GetChangedCompositionTypes;
     case HWC2::FunctionDescriptor::GetClientTargetSupport:
-      return ToHook<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(
-          DisplayHook<decltype(&HwcDisplay::GetClientTargetSupport),
-                      &HwcDisplay::GetClientTargetSupport, uint32_t, uint32_t,
-                      int32_t, int32_t>);
+      return (hwc2_function_pointer_t)GetClientTargetSupport;
     case HWC2::FunctionDescriptor::GetColorModes:
       return ToHook<HWC2_PFN_GET_COLOR_MODES>(
           DisplayHook<decltype(&HwcDisplay::GetColorModes),
@@ -185,41 +797,28 @@
           DisplayHook<decltype(&HwcDisplay::GetDisplayName),
                       &HwcDisplay::GetDisplayName, uint32_t *, char *>);
     case HWC2::FunctionDescriptor::GetDisplayRequests:
-      return ToHook<HWC2_PFN_GET_DISPLAY_REQUESTS>(
-          DisplayHook<decltype(&HwcDisplay::GetDisplayRequests),
-                      &HwcDisplay::GetDisplayRequests, int32_t *, uint32_t *,
-                      hwc2_layer_t *, int32_t *>);
+      return (hwc2_function_pointer_t)GetDisplayRequests;
     case HWC2::FunctionDescriptor::GetDisplayType:
       return ToHook<HWC2_PFN_GET_DISPLAY_TYPE>(
           DisplayHook<decltype(&HwcDisplay::GetDisplayType),
                       &HwcDisplay::GetDisplayType, int32_t *>);
     case HWC2::FunctionDescriptor::GetDozeSupport:
-      return ToHook<HWC2_PFN_GET_DOZE_SUPPORT>(
-          DisplayHook<decltype(&HwcDisplay::GetDozeSupport),
-                      &HwcDisplay::GetDozeSupport, int32_t *>);
+      return (hwc2_function_pointer_t)GetDozeSupport;
     case HWC2::FunctionDescriptor::GetHdrCapabilities:
       return ToHook<HWC2_PFN_GET_HDR_CAPABILITIES>(
           DisplayHook<decltype(&HwcDisplay::GetHdrCapabilities),
                       &HwcDisplay::GetHdrCapabilities, uint32_t *, int32_t *,
                       float *, float *, float *>);
     case HWC2::FunctionDescriptor::GetReleaseFences:
-      return ToHook<HWC2_PFN_GET_RELEASE_FENCES>(
-          DisplayHook<decltype(&HwcDisplay::GetReleaseFences),
-                      &HwcDisplay::GetReleaseFences, uint32_t *, hwc2_layer_t *,
-                      int32_t *>);
+      return (hwc2_function_pointer_t)GetReleaseFences;
     case HWC2::FunctionDescriptor::PresentDisplay:
-      return ToHook<HWC2_PFN_PRESENT_DISPLAY>(
-          DisplayHook<decltype(&HwcDisplay::PresentDisplay),
-                      &HwcDisplay::PresentDisplay, int32_t *>);
+      return (hwc2_function_pointer_t)PresentDisplay;
     case HWC2::FunctionDescriptor::SetActiveConfig:
       return ToHook<HWC2_PFN_SET_ACTIVE_CONFIG>(
           DisplayHook<decltype(&HwcDisplay::SetActiveConfig),
                       &HwcDisplay::SetActiveConfig, hwc2_config_t>);
     case HWC2::FunctionDescriptor::SetClientTarget:
-      return ToHook<HWC2_PFN_SET_CLIENT_TARGET>(
-          DisplayHook<decltype(&HwcDisplay::SetClientTarget),
-                      &HwcDisplay::SetClientTarget, buffer_handle_t, int32_t,
-                      int32_t, hwc_region_t>);
+      return (hwc2_function_pointer_t)SetClientTarget;
     case HWC2::FunctionDescriptor::SetColorMode:
       return ToHook<HWC2_PFN_SET_COLOR_MODE>(
           DisplayHook<decltype(&HwcDisplay::SetColorMode),
@@ -229,9 +828,7 @@
           DisplayHook<decltype(&HwcDisplay::SetColorTransform),
                       &HwcDisplay::SetColorTransform, const float *, int32_t>);
     case HWC2::FunctionDescriptor::SetOutputBuffer:
-      return ToHook<HWC2_PFN_SET_OUTPUT_BUFFER>(
-          DisplayHook<decltype(&HwcDisplay::SetOutputBuffer),
-                      &HwcDisplay::SetOutputBuffer, buffer_handle_t, int32_t>);
+      return (hwc2_function_pointer_t)SetOutputBuffer;
     case HWC2::FunctionDescriptor::SetPowerMode:
       return ToHook<HWC2_PFN_SET_POWER_MODE>(
           DisplayHook<decltype(&HwcDisplay::SetPowerMode),
@@ -241,9 +838,7 @@
           DisplayHook<decltype(&HwcDisplay::SetVsyncEnabled),
                       &HwcDisplay::SetVsyncEnabled, int32_t>);
     case HWC2::FunctionDescriptor::ValidateDisplay:
-      return ToHook<HWC2_PFN_VALIDATE_DISPLAY>(
-          DisplayHook<decltype(&HwcDisplay::ValidateDisplay),
-                      &HwcDisplay::ValidateDisplay, uint32_t *, uint32_t *>);
+      return (hwc2_function_pointer_t)ValidateDisplay;
 #if __ANDROID_API__ > 27
     case HWC2::FunctionDescriptor::GetRenderIntents:
       return ToHook<HWC2_PFN_GET_RENDER_INTENTS>(
@@ -267,13 +862,9 @@
                       &HwcDisplay::GetDisplayCapabilities, uint32_t *,
                       uint32_t *>);
     case HWC2::FunctionDescriptor::GetDisplayBrightnessSupport:
-      return ToHook<HWC2_PFN_GET_DISPLAY_BRIGHTNESS_SUPPORT>(
-          DisplayHook<decltype(&HwcDisplay::GetDisplayBrightnessSupport),
-                      &HwcDisplay::GetDisplayBrightnessSupport, bool *>);
+      return (hwc2_function_pointer_t)GetDisplayBrightnessSupport;
     case HWC2::FunctionDescriptor::SetDisplayBrightness:
-      return ToHook<HWC2_PFN_SET_DISPLAY_BRIGHTNESS>(
-          DisplayHook<decltype(&HwcDisplay::SetDisplayBrightness),
-                      &HwcDisplay::SetDisplayBrightness, float>);
+      return (hwc2_function_pointer_t)SetDisplayBrightness;
 #endif /* __ANDROID_API__ > 28 */
 #if __ANDROID_API__ > 29
     case HWC2::FunctionDescriptor::GetDisplayConnectionType:
@@ -292,14 +883,9 @@
                       hwc2_config_t, hwc_vsync_period_change_constraints_t *,
                       hwc_vsync_period_change_timeline_t *>);
     case HWC2::FunctionDescriptor::SetAutoLowLatencyMode:
-      return ToHook<HWC2_PFN_SET_AUTO_LOW_LATENCY_MODE>(
-          DisplayHook<decltype(&HwcDisplay::SetAutoLowLatencyMode),
-                      &HwcDisplay::SetAutoLowLatencyMode, bool>);
+      return (hwc2_function_pointer_t)SetAutoLowLatencyMode;
     case HWC2::FunctionDescriptor::GetSupportedContentTypes:
-      return ToHook<HWC2_PFN_GET_SUPPORTED_CONTENT_TYPES>(
-          DisplayHook<decltype(&HwcDisplay::GetSupportedContentTypes),
-                      &HwcDisplay::GetSupportedContentTypes, uint32_t *,
-                      uint32_t *>);
+      return (hwc2_function_pointer_t)GetSupportedContentTypes;
     case HWC2::FunctionDescriptor::SetContentType:
       return ToHook<HWC2_PFN_SET_CONTENT_TYPE>(
           DisplayHook<decltype(&HwcDisplay::SetContentType),
@@ -307,68 +893,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/Composer.cpp b/hwc3/Composer.cpp
index 124380d..28d096c 100644
--- a/hwc3/Composer.cpp
+++ b/hwc3/Composer.cpp
@@ -38,11 +38,13 @@
   }
 
   auto client = ndk::SharedRefBase::make<ComposerClient>();
-  if (!client || !client->Init()) {
+  if (!client) {
     *out_client = nullptr;
     return ToBinderStatus(hwc3::Error::kNoResources);
   }
 
+  client->Init();
+
   *out_client = client;
   client_ = client;
 
diff --git a/hwc3/ComposerClient.cpp b/hwc3/ComposerClient.cpp
index b8128f0..c378397 100644
--- a/hwc3/ComposerClient.cpp
+++ b/hwc3/ComposerClient.cpp
@@ -41,6 +41,7 @@
 #include <cutils/native_handle.h>
 #include <hardware/hwcomposer2.h>
 #include <hardware/hwcomposer_defs.h>
+#include <ui/GraphicBufferMapper.h>
 
 #include "bufferinfo/BufferInfo.h"
 #include "compositor/DisplayInfo.h"
@@ -50,19 +51,25 @@
 #include "hwc3/DrmHwcThree.h"
 #include "hwc3/Utils.h"
 
+using ::android::DstRectInfo;
 using ::android::HwcDisplay;
 using ::android::HwcDisplayConfig;
 using ::android::HwcDisplayConfigs;
 using ::android::HwcLayer;
 using ::android::LayerTransform;
+using ::android::SrcRectInfo;
 
 #include "utils/log.h"
 
 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,
@@ -90,12 +97,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):
@@ -117,13 +120,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):
@@ -138,6 +145,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) {
@@ -162,6 +177,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;
@@ -170,6 +206,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) {
@@ -245,18 +294,29 @@
   return aidl_configuration;
 }
 
-std::optional<hwc_rect> AidlToRect(const std::optional<common::Rect>& rect) {
+std::optional<DstRectInfo> AidlToRect(const std::optional<common::Rect>& rect) {
   if (!rect) {
     return std::nullopt;
   }
-  return hwc_rect{rect->left, rect->top, rect->right, rect->bottom};
+  DstRectInfo dst_rec;
+  dst_rec.i_rect = {.left = rect->left,
+                    .top = rect->top,
+                    .right = rect->right,
+                    .bottom = rect->bottom};
+  return dst_rec;
 }
 
-std::optional<hwc_frect> AidlToFRect(const std::optional<common::FRect>& rect) {
+std::optional<SrcRectInfo> AidlToFRect(
+    const std::optional<common::FRect>& rect) {
   if (!rect) {
     return std::nullopt;
   }
-  return hwc_frect{rect->left, rect->top, rect->right, rect->bottom};
+  SrcRectInfo src_rect;
+  src_rect.f_rect = {.left = rect->left,
+                     .top = rect->top,
+                     .right = rect->right,
+                     .bottom = rect->bottom};
+  return src_rect;
 }
 
 std::optional<float> AidlToAlpha(const std::optional<PlaneAlpha>& alpha) {
@@ -279,66 +339,149 @@
     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
 
+class Hwc3BufferHandle : public PrimeFdsSharedBase {
+ public:
+  static auto Create(buffer_handle_t handle)
+      -> std::shared_ptr<Hwc3BufferHandle> {
+    auto hwc3 = std::shared_ptr<Hwc3BufferHandle>(new Hwc3BufferHandle());
+
+    ::android::GraphicBufferMapper::get()
+        .importBufferNoValidate(handle, &hwc3->imported_handle_);
+
+    return hwc3;
+  }
+
+  auto GetHandle() const -> buffer_handle_t {
+    return imported_handle_;
+  }
+
+  ~Hwc3BufferHandle() override {
+    ::android::GraphicBufferMapper::get().freeBuffer(imported_handle_);
+  }
+
+ private:
+  Hwc3BufferHandle() = default;
+  buffer_handle_t imported_handle_{};
+};
+
+class Hwc3Layer : public ::android::FrontendLayerBase {
+ public:
+  auto HandleNextBuffer(std::optional<buffer_handle_t> raw_handle,
+                        ::android::SharedFd fence_fd, int32_t slot_id)
+      -> std::optional<HwcLayer::LayerProperties> {
+    HwcLayer::LayerProperties lp;
+    if (!raw_handle && slots_.count(slot_id) != 0) {
+      lp.active_slot = {
+          .slot_id = slot_id,
+          .fence = std::move(fence_fd),
+      };
+
+      return lp;
+    }
+
+    if (!raw_handle) {
+      ALOGE("Buffer handle is nullopt but slot was not cached.");
+      return std::nullopt;
+    }
+
+    auto hwc3 = Hwc3BufferHandle::Create(*raw_handle);
+    if (!hwc3) {
+      return std::nullopt;
+    }
+
+    auto bi = ::android::BufferInfoGetter::GetInstance()->GetBoInfo(
+        hwc3->GetHandle());
+    if (!bi) {
+      return std::nullopt;
+    }
+
+    bi->fds_shared = hwc3;
+
+    lp.slot_buffer = {
+        .slot_id = slot_id,
+        .bi = bi,
+    };
+
+    lp.active_slot = {
+        .slot_id = slot_id,
+        .fence = std::move(fence_fd),
+    };
+
+    slots_[slot_id] = hwc3;
+
+    return lp;
+  }
+
+  [[maybe_unused]]
+  auto HandleClearSlot(int32_t slot_id)
+      -> std::optional<HwcLayer::LayerProperties> {
+    if (slots_.count(slot_id) == 0) {
+      return std::nullopt;
+    }
+
+    slots_.erase(slot_id);
+
+    auto lp = HwcLayer::LayerProperties{};
+    lp.slot_buffer = {
+        .slot_id = slot_id,
+        .bi = std::nullopt,
+    };
+
+    return lp;
+  }
+
+  void ClearSlots() {
+    slots_.clear();
+  }
+
+ private:
+  std::map<int32_t /*slot*/, std::shared_ptr<Hwc3BufferHandle>> slots_;
+};
+
+static auto GetHwc3Layer(HwcLayer& layer) -> std::shared_ptr<Hwc3Layer> {
+  auto frontend_private_data = layer.GetFrontendPrivateData();
+  if (!frontend_private_data) {
+    frontend_private_data = std::make_shared<Hwc3Layer>();
+    layer.SetFrontendPrivateData(frontend_private_data);
+  }
+  return std::static_pointer_cast<Hwc3Layer>(frontend_private_data);
+}
+
 ComposerClient::ComposerClient() {
   DEBUG_FUNC();
 }
 
-bool ComposerClient::Init() {
+void ComposerClient::Init() {
   DEBUG_FUNC();
-  composer_resources_ = ComposerResources::Create();
-  if (composer_resources_) {
-    hwc_ = std::make_unique<DrmHwcThree>(composer_resources_.get());
-  }
-  return composer_resources_ != nullptr;
+  hwc_ = std::make_unique<DrmHwcThree>();
 }
 
 ComposerClient::~ComposerClient() {
   DEBUG_FUNC();
-  {
-    // First Deinit the displays to start shutting down the Display's dependent
-    // threads such as VSyncWorker.
+  if (hwc_) {
     const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
     hwc_->DeinitDisplays();
-  }
-  // Sleep to wait for threads to complete and exit.
-  const int time_for_threads_to_exit_us = 200000;
-  usleep(time_for_threads_to_exit_us);
-  {
-    // Hold the lock while destructing the hwc_ and the objects that it owns.
-    const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
     hwc_.reset();
   }
   LOG(DEBUG) << "removed composer client";
 }
 
 ndk::ScopedAStatus ComposerClient::createLayer(int64_t display_id,
-                                               int32_t buffer_slot_count,
+                                               int32_t /*buffer_slot_count*/,
                                                int64_t* layer_id) {
   DEBUG_FUNC();
   const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
@@ -354,21 +497,13 @@
     return ToBinderStatus(err);
   }
 
-  const int64_t created_layer_id = Hwc2LayerToHwc3(hwc2_layer_id);
-  err = composer_resources_->AddLayer(display_id, created_layer_id,
-                                      buffer_slot_count);
-  if (err != hwc3::Error::kNone) {
-    destroyLayer(display_id, created_layer_id);
-    return ToBinderStatus(err);
-  }
-
-  *layer_id = created_layer_id;
+  *layer_id = Hwc2LayerToHwc3(hwc2_layer_id);
   return ndk::ScopedAStatus::ok();
 }
 
 ndk::ScopedAStatus ComposerClient::createVirtualDisplay(
     int32_t width, int32_t height, AidlPixelFormat format_hint,
-    int32_t output_buffer_slot_count, VirtualDisplay* out_display) {
+    int32_t /*output_buffer_slot_count*/, VirtualDisplay* out_display) {
   DEBUG_FUNC();
   const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
 
@@ -382,15 +517,7 @@
     return ToBinderStatus(err);
   }
 
-  const int64_t created_display_id = Hwc2DisplayToHwc3(hwc2_display_id);
-  err = composer_resources_->AddVirtualDisplay(hwc2_display_id,
-                                               output_buffer_slot_count);
-  if (err != hwc3::Error::kNone) {
-    hwc_->DestroyVirtualDisplay(hwc2_display_id);
-    return ToBinderStatus(err);
-  }
-
-  out_display->display = created_display_id;
+  out_display->display = Hwc2DisplayToHwc3(hwc2_display_id);
   out_display->format = format_hint;
   return ndk::ScopedAStatus::ok();
 }
@@ -409,7 +536,6 @@
     return ToBinderStatus(err);
   }
 
-  err = composer_resources_->RemoveLayer(display_id, layer_id);
   return ToBinderStatus(err);
 }
 
@@ -420,113 +546,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);
 }
@@ -559,18 +578,44 @@
     return;
   }
 
+#if __ANDROID_API__ >= 34
+  /* https://source.android.com/docs/core/graphics/reduce-consumption */
+  if (command.bufferSlotsToClear) {
+    auto hwc3_layer = GetHwc3Layer(*layer);
+    for (const auto& slot : *command.bufferSlotsToClear) {
+      auto lp = hwc3_layer->HandleClearSlot(slot);
+      if (!lp) {
+        cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
+        return;
+      }
+
+      layer->SetLayerProperties(lp.value());
+    }
+  }
+#endif
+
   HwcLayer::LayerProperties properties;
   if (command.buffer) {
-    HwcLayer::Buffer buffer;
-    auto err = ImportLayerBuffer(display_id, command.layer, *command.buffer,
-                                 &buffer.buffer_handle);
-    if (err != hwc3::Error::kNone) {
-      cmd_result_writer_->AddError(err);
+    auto hwc3_layer = GetHwc3Layer(*layer);
+    std::optional<buffer_handle_t> buffer_handle = std::nullopt;
+    if (command.buffer->handle) {
+      buffer_handle = ::android::makeFromAidl(*command.buffer->handle);
+    }
+
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+    auto fence = const_cast<::ndk::ScopedFileDescriptor&>(command.buffer->fence)
+                     .release();
+
+    auto lp = hwc3_layer->HandleNextBuffer(buffer_handle,
+                                           ::android::MakeSharedFd(fence),
+                                           command.buffer->slot);
+
+    if (!lp) {
+      cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
       return;
     }
-    buffer.acquire_fence = ::android::MakeSharedFd(
-        command.buffer->fence.dup().release());
-    properties.buffer.emplace(buffer);
+
+    properties = lp.value();
   }
 
   properties.blend_mode = AidlToBlendMode(command.blendMode);
@@ -606,7 +651,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;
   }
@@ -617,14 +663,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);
   }
@@ -632,18 +686,67 @@
     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);
+    auto hwc3_display = DrmHwcThree::GetHwc3Display(*display);
+    hwc3_display->must_validate = 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->AcceptValidatedComposition();
   }
+
   if (command.presentDisplay) {
-    ExecutePresentDisplay(command.display);
-  }
-  if (command.presentOrValidateDisplay) {
-    ExecutePresentOrValidateDisplay(command.display,
-                                    command.expectedPresentTime);
+    auto hwc3_display = DrmHwcThree::GetHwc3Display(*display);
+    if (hwc3_display->must_validate) {
+      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);
   }
 }
 
@@ -973,8 +1076,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();
 }
 
@@ -1101,6 +1224,20 @@
                                  next_config != nullptr &&
                                  current_config->group_id ==
                                      next_config->group_id;
+  const bool same_resolution = current_config != nullptr &&
+                               next_config != nullptr &&
+                               current_config->mode.SameSize(next_config->mode);
+
+  /* Client framebuffer management:
+   * https://source.android.com/docs/core/graphics/framebuffer-mgmt
+   */
+  if (!same_resolution && !future_config) {
+    auto& client_layer = display->GetClientLayer();
+    auto hwc3_layer = GetHwc3Layer(client_layer);
+    hwc3_layer->ClearSlots();
+    client_layer.ClearSlots();
+  }
+
   // If the contraints dictate that this is to be applied in the future, it
   // must be queued. If the new config is in the same config group as the
   // current one, then queue it to reduce jank.
@@ -1151,7 +1288,7 @@
 }
 
 ndk::ScopedAStatus ComposerClient::setAutoLowLatencyMode(int64_t display_id,
-                                                         bool on) {
+                                                         bool /*on*/) {
   DEBUG_FUNC();
   const std::unique_lock lock(hwc_->GetResMan().GetMainLock());
   HwcDisplay* display = GetDisplay(display_id);
@@ -1159,15 +1296,13 @@
     return ToBinderStatus(hwc3::Error::kBadDisplay);
   }
 
-  auto error = Hwc2toHwc3Error(display->SetAutoLowLatencyMode(on));
-  return ToBinderStatus(error);
+  return ToBinderStatus(hwc3::Error::kUnsupported);
 }
 
-ndk::ScopedAStatus ComposerClient::setClientTargetSlotCount(int64_t display_id,
-                                                            int32_t count) {
+ndk::ScopedAStatus ComposerClient::setClientTargetSlotCount(
+    int64_t /*display_id*/, int32_t /*count*/) {
   DEBUG_FUNC();
-  return ToBinderStatus(
-      composer_resources_->SetDisplayClientTargetCacheSize(display_id, count));
+  return ToBinderStatus(hwc3::Error::kNone);
 }
 
 ndk::ScopedAStatus ComposerClient::setColorMode(int64_t display_id,
@@ -1296,7 +1431,8 @@
 }
 
 ndk::ScopedAStatus ComposerClient::notifyExpectedPresent(
-    int64_t /*display*/, const ClockMonotonicTimestamp& /*expected_present_time*/,
+    int64_t /*display*/,
+    const ClockMonotonicTimestamp& /*expected_present_time*/,
     int32_t /*frame_interval_ns*/) {
   return ToBinderStatus(hwc3::Error::kUnsupported);
 }
@@ -1332,41 +1468,6 @@
   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);
-  auto err = composer_resources_->GetLayerBuffer(display_id, layer_id, buffer,
-                                                 out_imported_buffer,
-                                                 releaser.get());
-  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);
@@ -1375,37 +1476,35 @@
     return;
   }
 
-  hwc_region_t damage_regions;
-  damage_regions.numRects = command.damage.size();
+  auto& client_layer = display->GetClientLayer();
+  auto hwc3layer = GetHwc3Layer(client_layer);
 
-  std::vector<hwc_rect_t> regions(command.damage.size());
-  for (const auto& region : command.damage) {
-    regions.push_back({region.left, region.top, region.right, region.bottom});
-  }
-  damage_regions.rects = regions.data();
-
-  buffer_handle_t imported_buffer = nullptr;
-  auto buf_releaser = composer_resources_->CreateResourceReleaser(true);
-
-  auto error = composer_resources_->GetDisplayClientTarget(display_id,
-                                                           command.buffer,
-                                                           &imported_buffer,
-                                                           buf_releaser.get());
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
-    return;
+  std::optional<buffer_handle_t> raw_buffer = std::nullopt;
+  if (command.buffer.handle) {
+    raw_buffer = ::android::makeFromAidl(*command.buffer.handle);
   }
 
   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
   auto fence = const_cast<::ndk::ScopedFileDescriptor&>(command.buffer.fence)
                    .release();
-  error = Hwc2toHwc3Error(
-      display->SetClientTarget(imported_buffer, fence,
-                               Hwc3DataspaceToHwc2(command.dataspace),
-                               damage_regions));
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
+
+  auto properties = hwc3layer->HandleNextBuffer(raw_buffer,
+                                                ::android::MakeSharedFd(fence),
+                                                command.buffer.slot);
+
+  if (!properties) {
+    ALOGE("Failed to import client target buffer.");
+    /* Here, sending an error would be the natural way to do the thing.
+     * But VTS checks for no error. Is it the VTS issue?
+     * https://cs.android.com/android/platform/superproject/main/+/main:hardware/interfaces/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp;l=1892;drc=2647200f4c535ca6567b452695b7d13f2aaf3f2a
+     */
+    return;
   }
+
+  properties->color_space = AidlToColorSpace(command.dataspace);
+  properties->sample_range = AidlToSampleRange(command.dataspace);
+
+  client_layer.SetLayerProperties(properties.value());
 }
 
 void ComposerClient::ExecuteSetDisplayOutputBuffer(uint64_t display_id,
@@ -1416,149 +1515,32 @@
     return;
   }
 
-  buffer_handle_t imported_buffer = nullptr;
-  auto buf_releaser = composer_resources_->CreateResourceReleaser(true);
-
-  auto error = composer_resources_->GetDisplayOutputBuffer(display_id, buffer,
-                                                           &imported_buffer,
-                                                           buf_releaser.get());
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
+  auto& writeback_layer = display->GetWritebackLayer();
+  if (!writeback_layer) {
+    cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
     return;
   }
 
+  auto hwc3layer = GetHwc3Layer(*writeback_layer);
+
+  std::optional<buffer_handle_t> raw_buffer = std::nullopt;
+  if (buffer.handle) {
+    raw_buffer = ::android::makeFromAidl(*buffer.handle);
+  }
+
   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
   auto fence = const_cast<::ndk::ScopedFileDescriptor&>(buffer.fence).release();
-  error = Hwc2toHwc3Error(display->SetOutputBuffer(imported_buffer, fence));
-  if (error != hwc3::Error::kNone) {
-    cmd_result_writer_->AddError(error);
-    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);
+
+  auto properties = hwc3layer->HandleNextBuffer(raw_buffer,
+                                                ::android::MakeSharedFd(fence),
+                                                buffer.slot);
+
+  if (!properties) {
+    cmd_result_writer_->AddError(hwc3::Error::kBadLayer);
     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);
+  writeback_layer->SetLayerProperties(properties.value());
 }
 
 }  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/hwc3/ComposerClient.h b/hwc3/ComposerClient.h
index 2595203..ed47cd6 100644
--- a/hwc3/ComposerClient.h
+++ b/hwc3/ComposerClient.h
@@ -20,8 +20,8 @@
 
 #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"
 #include "utils/Mutex.h"
 
@@ -30,10 +30,7 @@
 using AidlNativeHandle = aidl::android::hardware::common::NativeHandle;
 
 namespace android {
-
 class HwcDisplay;
-class HwcLayer;
-
 }  // namespace android
 
 namespace aidl::android::hardware::graphics::composer3::impl {
@@ -45,7 +42,7 @@
   ComposerClient();
   ~ComposerClient() override;
 
-  bool Init();
+  void Init();
   std::string Dump();
 
   // composer3 interface
@@ -171,48 +168,21 @@
  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);
 
   std::unique_ptr<CommandResultWriter> cmd_result_writer_;
 
-  // Manages importing and caching gralloc buffers for displays and layers.
-  std::unique_ptr<ComposerResources> composer_resources_;
-
   std::unique_ptr<DrmHwcThree> hwc_;
 };
 
diff --git a/hwc3/ComposerResources.cpp b/hwc3/ComposerResources.cpp
deleted file mode 100644
index ae0edf4..0000000
--- a/hwc3/ComposerResources.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-
-/*
- * 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"
-#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
-
-#include "ComposerResources.h"
-
-#include <aidlcommonsupport/NativeHandle.h>
-
-#include "hardware/hwcomposer2.h"
-#include "hwc3/Utils.h"
-
-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);
-}
-
-std::unique_ptr<ComposerResources> ComposerResources::Create() {
-  auto instance = std::unique_ptr<ComposerResources>(new ComposerResources);
-  if (instance->resources_ == nullptr) {
-    ALOGE("%s: Failed to initialise ComposerResources", __func__);
-    return nullptr;
-  }
-
-  return instance;
-}
-
-hwc3::Error ComposerResources::GetLayerBuffer(
-    uint64_t display_id, int64_t layer_id, const Buffer& buffer,
-    buffer_handle_t* out_buffer_handle,
-    ComposerResourceReleaser* buf_releaser) {
-  auto display = ToHwc2Display(display_id);
-  auto layer = ToHwc2Layer(layer_id);
-
-  const bool use_cache = !buffer.handle.has_value();
-  buffer_handle_t buffer_handle = nullptr;
-  if (buffer.handle.has_value()) {
-    buffer_handle = ::android::makeFromAidl(*buffer.handle);
-  }
-
-  auto err = resources_->getLayerBuffer(display, layer, buffer.slot, use_cache,
-                                        buffer_handle, out_buffer_handle,
-                                        buf_releaser->GetReplacedHandle());
-
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::GetLayerSidebandStream(
-    uint64_t display_id, int64_t layer_id,
-    const aidl::android::hardware::common::NativeHandle& handle,
-    buffer_handle_t* out_handle, ComposerResourceReleaser* releaser) {
-  auto display = ToHwc2Display(display_id);
-  auto layer = ToHwc2Layer(layer_id);
-
-  auto err = resources_->getLayerSidebandStream(display, layer,
-                                                ::android::makeFromAidl(handle),
-                                                out_handle,
-                                                releaser->GetReplacedHandle());
-
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::AddLayer(uint64_t display_id, int64_t layer_id,
-                                        uint32_t buffer_cache_size) {
-  auto display = ToHwc2Display(display_id);
-  auto layer = ToHwc2Layer(layer_id);
-
-  auto err = resources_->addLayer(display, layer, buffer_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::RemoveLayer(uint64_t display_id,
-                                           int64_t layer_id) {
-  auto display = ToHwc2Display(display_id);
-  auto layer = ToHwc2Layer(layer_id);
-
-  auto err = resources_->removeLayer(display, layer);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-bool ComposerResources::HasDisplay(uint64_t display_id) {
-  auto display = ToHwc2Display(display_id);
-  return resources_->hasDisplay(display);
-}
-
-hwc3::Error ComposerResources::AddPhysicalDisplay(uint64_t display_id) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_->addPhysicalDisplay(display);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::AddVirtualDisplay(
-    uint64_t display, uint32_t output_buffer_cache_size) {
-  auto err = resources_->addVirtualDisplay(display, output_buffer_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::RemoveDisplay(uint64_t display_id) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_->removeDisplay(display);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-void ComposerResources::SetDisplayMustValidateState(uint64_t display_id,
-                                                    bool must_validate) {
-  auto display = ToHwc2Display(display_id);
-  resources_->setDisplayMustValidateState(display, must_validate);
-}
-
-bool ComposerResources::MustValidateDisplay(uint64_t display_id) {
-  auto display = ToHwc2Display(display_id);
-  return resources_->mustValidateDisplay(display);
-}
-
-hwc3::Error ComposerResources::GetDisplayClientTarget(
-    uint64_t display_id, const Buffer& buffer, buffer_handle_t* out_handle,
-    ComposerResourceReleaser* releaser) {
-  auto display = ToHwc2Display(display_id);
-
-  const bool use_cache = !buffer.handle.has_value();
-  buffer_handle_t buffer_handle = nullptr;
-  if (buffer.handle.has_value()) {
-    buffer_handle = ::android::makeFromAidl(*buffer.handle);
-  }
-
-  auto err = resources_->getDisplayClientTarget(display, buffer.slot, use_cache,
-                                                buffer_handle, out_handle,
-                                                releaser->GetReplacedHandle());
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::SetDisplayClientTargetCacheSize(
-    uint64_t display_id, uint32_t client_target_cache_size) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_
-                 ->setDisplayClientTargetCacheSize(display,
-                                                   client_target_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::GetDisplayClientTargetCacheSize(
-    uint64_t display_id, size_t* out_cache_size) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_->getDisplayClientTargetCacheSize(display,
-                                                         out_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::GetDisplayOutputBufferCacheSize(
-    uint64_t display_id, size_t* out_cache_size) {
-  auto display = ToHwc2Display(display_id);
-  auto err = resources_->getDisplayOutputBufferCacheSize(display,
-                                                         out_cache_size);
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-
-hwc3::Error ComposerResources::GetDisplayOutputBuffer(
-    uint64_t display_id, const Buffer& buffer, buffer_handle_t* out_handle,
-    ComposerResourceReleaser* releaser) {
-  auto display = ToHwc2Display(display_id);
-  const bool use_cache = !buffer.handle.has_value();
-
-  buffer_handle_t buffer_handle = nullptr;
-  if (buffer.handle.has_value()) {
-    buffer_handle = ::android::makeFromAidl(*buffer.handle);
-  }
-
-  auto err = resources_->getDisplayOutputBuffer(display, buffer.slot, use_cache,
-                                                buffer_handle, out_handle,
-                                                releaser->GetReplacedHandle());
-  return Hwc2toHwc3Error(static_cast<HWC2::Error>(err));
-}
-}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/hwc3/ComposerResources.h b/hwc3/ComposerResources.h
deleted file mode 100644
index 6f4eee7..0000000
--- a/hwc3/ComposerResources.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <memory>
-
-#include "aidl/android/hardware/graphics/composer3/IComposerClient.h"
-#include "composer-resources/2.2/ComposerResources.h"
-#include "cutils/native_handle.h"
-#include "hwc3/Utils.h"
-
-namespace aidl::android::hardware::graphics::composer3::impl {
-
-class ComposerResourceReleaser {
- public:
-  explicit ComposerResourceReleaser(bool is_buffer)
-      : replaced_handle_(is_buffer) {
-  }
-  virtual ~ComposerResourceReleaser() = default;
-
-  ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
-      ReplacedHandle*
-      GetReplacedHandle() {
-    return &replaced_handle_;
-  }
-
- private:
-  ::android::hardware::graphics::composer::V2_2::hal::ComposerResources::
-      ReplacedHandle replaced_handle_;
-};
-
-class ComposerResources {
- public:
-  static std::unique_ptr<ComposerResources> Create();
-  ~ComposerResources() = default;
-
-  hwc3::Error GetLayerBuffer(uint64_t display_id, int64_t layer_id,
-                             const Buffer& buffer,
-                             buffer_handle_t* out_buffer_handle,
-                             ComposerResourceReleaser* releaser);
-  hwc3::Error GetLayerSidebandStream(
-      uint64_t display_id, int64_t layer_id,
-      const aidl::android::hardware::common::NativeHandle& handle,
-      buffer_handle_t* out_handle, ComposerResourceReleaser* releaser);
-
-  hwc3::Error AddLayer(uint64_t display, int64_t layer,
-                       uint32_t buffer_cache_size);
-  hwc3::Error RemoveLayer(uint64_t display, int64_t layer);
-
-  bool HasDisplay(uint64_t display);
-  hwc3::Error AddPhysicalDisplay(uint64_t display);
-  hwc3::Error AddVirtualDisplay(uint64_t display,
-                                uint32_t output_buffer_cache_size);
-  hwc3::Error RemoveDisplay(uint64_t display);
-
-  void SetDisplayMustValidateState(uint64_t display_id, bool must_validate);
-  bool MustValidateDisplay(uint64_t display_id);
-
-  hwc3::Error GetDisplayClientTarget(uint64_t display_id, const Buffer& buffer,
-                                     buffer_handle_t* out_handle,
-                                     ComposerResourceReleaser* releaser);
-
-  hwc3::Error SetDisplayClientTargetCacheSize(
-      uint64_t display_id, uint32_t client_target_cache_size);
-  hwc3::Error GetDisplayClientTargetCacheSize(uint64_t display_id,
-                                              size_t* out_cache_size);
-  hwc3::Error GetDisplayOutputBufferCacheSize(uint64_t display,
-                                              size_t* out_cache_size);
-  hwc3::Error GetDisplayOutputBuffer(uint64_t display_id, const Buffer& buffer,
-                                     buffer_handle_t* out_handle,
-                                     ComposerResourceReleaser* releaser);
-
-  static std::unique_ptr<ComposerResourceReleaser> CreateResourceReleaser(
-      bool is_buffer);
-
- private:
-  ComposerResources() = default;
-
-  std::unique_ptr<
-      ::android::hardware::graphics::composer::V2_2::hal::ComposerResources>
-      resources_ = ::android::hardware::graphics::composer::V2_2::hal::
-          ComposerResources::create();
-};
-
-}  // namespace aidl::android::hardware::graphics::composer3::impl
\ No newline at end of file
diff --git a/hwc3/DrmHwcThree.cpp b/hwc3/DrmHwcThree.cpp
index fb14bc9..6df3022 100644
--- a/hwc3/DrmHwcThree.cpp
+++ b/hwc3/DrmHwcThree.cpp
@@ -28,7 +28,15 @@
 
 namespace aidl::android::hardware::graphics::composer3::impl {
 
-using ::android::HwcDisplay;
+auto DrmHwcThree::GetHwc3Display(::android::HwcDisplay& display)
+    -> std::shared_ptr<Hwc3Display> {
+  auto frontend_private_data = display.GetFrontendPrivateData();
+  if (!frontend_private_data) {
+    frontend_private_data = std::make_shared<Hwc3Display>();
+    display.SetFrontendPrivateData(frontend_private_data);
+  }
+  return std::static_pointer_cast<Hwc3Display>(frontend_private_data);
+}
 
 DrmHwcThree::~DrmHwcThree() {
   /* Display deinit routine is handled by resource manager */
@@ -53,7 +61,16 @@
 }
 
 void DrmHwcThree::SendRefreshEventToClient(uint64_t display_id) {
-  composer_resources_->SetDisplayMustValidateState(display_id, true);
+  {
+    const std::unique_lock lock(GetResMan().GetMainLock());
+    auto* idisplay = GetDisplay(display_id);
+    if (idisplay == nullptr) {
+      ALOGE("Failed to get display %" PRIu64, display_id);
+      return;
+    }
+    auto hwc3_display = GetHwc3Display(*idisplay);
+    hwc3_display->must_validate = true;
+  }
   composer_callback_->onRefresh(static_cast<int64_t>(display_id));
 }
 
@@ -71,11 +88,9 @@
   switch (display_status) {
     case DrmHwc::kDisconnected:
       event = common::DisplayHotplugEvent::DISCONNECTED;
-      HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), false);
       break;
     case DrmHwc::kConnected:
       event = common::DisplayHotplugEvent::CONNECTED;
-      HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), true);
       break;
     case DrmHwc::kLinkTrainingFailed:
       event = common::DisplayHotplugEvent::ERROR_INCOMPATIBLE_CABLE;
@@ -89,73 +104,9 @@
 void DrmHwcThree::SendHotplugEventToClient(
     hwc2_display_t display_id, DrmHwc::DisplayStatus display_status) {
   bool connected = display_status != DrmHwc::kDisconnected;
-  HandleDisplayHotplugEvent(static_cast<uint64_t>(display_id), connected);
   composer_callback_->onHotplug(static_cast<int64_t>(display_id), connected);
 }
 
 #endif
 
-void DrmHwcThree::CleanDisplayResources(uint64_t display_id) {
-  DEBUG_FUNC();
-  HwcDisplay* display = GetDisplay(display_id);
-  if (display == nullptr) {
-    return;
-  }
-
-  display->SetPowerMode(static_cast<int32_t>(PowerMode::OFF));
-
-  size_t cache_size = 0;
-  auto err = composer_resources_->GetDisplayClientTargetCacheSize(display_id,
-                                                                  &cache_size);
-  if (err != hwc3::Error::kNone) {
-    ALOGE("%s: Could not clear target buffer cache for display: %" PRIu64,
-          __func__, display_id);
-    return;
-  }
-
-  for (size_t slot = 0; slot < cache_size; slot++) {
-    buffer_handle_t buffer_handle = nullptr;
-    auto buf_releaser = ComposerResources::CreateResourceReleaser(true);
-
-    Buffer buf{};
-    buf.slot = static_cast<int32_t>(slot);
-    err = composer_resources_->GetDisplayClientTarget(display_id, buf,
-                                                      &buffer_handle,
-                                                      buf_releaser.get());
-    if (err != hwc3::Error::kNone) {
-      continue;
-    }
-
-    err = Hwc2toHwc3Error(
-        display->SetClientTarget(buffer_handle, -1,
-                                 static_cast<int32_t>(
-                                     common::Dataspace::UNKNOWN),
-                                 {}));
-    if (err != hwc3::Error::kNone) {
-      ALOGE(
-          "%s: Could not clear slot %zu of the target buffer cache for "
-          "display %" PRIu64,
-          __func__, slot, display_id);
-    }
-  }
-}
-
-void DrmHwcThree::HandleDisplayHotplugEvent(uint64_t display_id,
-                                            bool connected) {
-  DEBUG_FUNC();
-  if (!connected) {
-    composer_resources_->RemoveDisplay(display_id);
-    Displays().erase(display_id);
-    return;
-  }
-
-  if (composer_resources_->HasDisplay(display_id)) {
-    /* Cleanup existing display resources */
-    CleanDisplayResources(display_id);
-    composer_resources_->RemoveDisplay(display_id);
-    Displays().erase(display_id);
-  }
-  composer_resources_->AddPhysicalDisplay(display_id);
-}
-
 }  // namespace aidl::android::hardware::graphics::composer3::impl
diff --git a/hwc3/DrmHwcThree.h b/hwc3/DrmHwcThree.h
index f020634..44168aa 100644
--- a/hwc3/DrmHwcThree.h
+++ b/hwc3/DrmHwcThree.h
@@ -19,15 +19,18 @@
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
 
 #include "drm/DrmHwc.h"
-#include "hwc3/ComposerResources.h"
+#include "hwc2_device/HwcDisplay.h"
 
 namespace aidl::android::hardware::graphics::composer3::impl {
 
+class Hwc3Display : public ::android::FrontendDisplayBase {
+ public:
+  bool must_validate = false;
+};
+
 class DrmHwcThree : public ::android::DrmHwc {
  public:
-  explicit DrmHwcThree(ComposerResources* composer_resources)
-      : composer_resources_(composer_resources) {
-  }
+  explicit DrmHwcThree() = default;
   ~DrmHwcThree() override;
 
   void Init(std::shared_ptr<IComposerCallback> callback);
@@ -41,11 +44,10 @@
   void SendHotplugEventToClient(hwc2_display_t display_id,
                                 DrmHwc::DisplayStatus display_status) override;
 
- private:
-  void CleanDisplayResources(uint64_t display_id);
-  void HandleDisplayHotplugEvent(uint64_t display_id, bool connected);
+  static auto GetHwc3Display(::android::HwcDisplay& display)
+      -> std::shared_ptr<Hwc3Display>;
 
+ private:
   std::shared_ptr<IComposerCallback> composer_callback_;
-  ComposerResources* composer_resources_;
 };
 }  // namespace aidl::android::hardware::graphics::composer3::impl
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/hwc3/meson.build b/hwc3/meson.build
index 291c71a..c525308 100644
--- a/hwc3/meson.build
+++ b/hwc3/meson.build
@@ -4,7 +4,6 @@
     'Composer.cpp',
     'DrmHwcThree.cpp',
     'service.cpp',
-    'ComposerResources.cpp',
     'Utils.cpp',
 )
 
diff --git a/meson.build b/meson.build
index 8cfbbc8..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',
 )
 
@@ -45,6 +46,12 @@
     '-DUSE_IMAPPER4_METADATA_API',
 ]
 
+dep_libdisplay_info = dependency('display_info', required : false)
+if dep_libdisplay_info.found()
+  common_cpp_flags += '-DHAS_LIBDISPLAY_INFO'
+  deps += dep_libdisplay_info
+endif
+
 hwc2_cpp_flags = [
     '-DHWC2_INCLUDE_STRINGIFICATION',
     '-DHWC2_USE_CPP11',
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
diff --git a/utils/properties.cpp b/utils/properties.cpp
index a855c94..a975822 100644
--- a/utils/properties.cpp
+++ b/utils/properties.cpp
@@ -27,5 +27,17 @@
 }
 
 auto Properties::UseConfigGroups() -> bool {
-  return (property_get_bool("ro.vendor.hwc.drm.use_config_groups", 1) != 0);
+  return (property_get_bool("ro.vendor.hwc.drm.use_config_groups", 0) != 0);
+}
+
+auto Properties::UseOverlayPlanes() -> bool {
+  return (property_get_bool("ro.vendor.hwc.use_overlay_planes", 1) != 0);
+}
+
+auto Properties::ScaleWithGpu() -> bool {
+  return (property_get_bool("vendor.hwc.drm.scale_with_gpu", 0) != 0);
+}
+
+auto Properties::EnableVirtualDisplay() -> bool {
+  return (property_get_bool("vendor.hwc.drm.enable_virtual_display", 0) != 0);
 }
diff --git a/utils/properties.h b/utils/properties.h
index 15c2fb2..4df79eb 100644
--- a/utils/properties.h
+++ b/utils/properties.h
@@ -78,4 +78,7 @@
  public:
   static auto IsPresentFenceNotReliable() -> bool;
   static auto UseConfigGroups() -> bool;
+  static auto UseOverlayPlanes() -> bool;
+  static auto ScaleWithGpu() -> bool;
+  static auto EnableVirtualDisplay() -> bool;
 };